aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_interlocking')
-rw-r--r--advtrains_interlocking/database.lua41
-rw-r--r--advtrains_interlocking/mod.conf2
-rw-r--r--advtrains_interlocking/route_prog.lua7
-rw-r--r--advtrains_interlocking/signal_api.lua23
-rwxr-xr-xadvtrains_interlocking/tcb_ts_ui.lua146
5 files changed, 198 insertions, 21 deletions
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index e77d073..75247de 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -802,10 +802,18 @@ function ildb.create_tcb_at(pos)
end
-- Remove TCB at the position and update/repair the now joined section
-function ildb.remove_tcb_at(pos)
+-- skip_tsrepair: should be set to true when the rail node at the TCB position is already gone.
+-- Assumes the track sections are now separated and does not attempt the repair process.
+function ildb.remove_tcb_at(pos, pname, skip_tsrepair)
--atdebug("remove_tcb_at",pos)
local pts = advtrains.encode_pos(pos)
local old_tcb = track_circuit_breaks[pts]
+ -- unassign signals if defined
+ for connid=1,2 do
+ if old_tcb[connid].signal then
+ ildb.set_sigd_for_signal(old_tcb[connid].signal, nil)
+ end
+ end
track_circuit_breaks[pts] = nil
-- purge the track sections adjacent
if old_tcb[1].ts_id then
@@ -823,7 +831,9 @@ function ildb.remove_tcb_at(pos)
end
advtrains.interlocking.remove_tcb_marker(pos)
-- If needed, merge the track sections here
- ildb.check_and_repair_ts_at_pos(pos, nil)
+ if not skip_tsrepair then
+ ildb.check_and_repair_ts_at_pos(pos, pname)
+ end
return true
end
@@ -973,11 +983,34 @@ function ildb.get_sigd_for_signal(pos)
end
return nil
end
-function ildb.set_sigd_for_signal(pos, sigd)
+function ildb.set_sigd_for_signal(pos, sigd) -- do not use!
local pts = advtrains.roundfloorpts(pos)
signal_assignments[pts] = sigd
end
+-- Assign the signal at pos to the given TCB side.
+function ildb.assign_signal_to_tcbs(pos, sigd)
+ local tcbs = ildb.get_tcbs(sigd)
+ assert(tcbs, "assign_signal_to_tcbs invalid sigd!")
+ tcbs.signal = pos
+ if not tcbs.routes then
+ tcbs.routes = {}
+ end
+ ildb.set_sigd_for_signal(pos, sigd)
+end
+
+-- unassign the signal from the given sigd (looks in tcbs.signal for the signalpos)
+function ildb.unassign_signal_for_tcbs(sigd)
+ local tcbs = advtrains.interlocking.db.get_tcbs(sigd)
+ if not tcbs then return end
+ local pos = tcbs.signal
+ if not pos then return end
+ ildb.set_sigd_for_signal(pos, nil)
+ tcbs.signal = nil
+ tcbs.route_aspect = nil
+ tcbs.route_remote = nil
+end
+
-- checks if there's any influence point set to this position
-- if purge is true, checks whether the associated signal still exists
-- and deletes the ip if not.
@@ -987,7 +1020,7 @@ function ildb.is_ip_at(pos, purge)
if purge then
-- is there still a signal assigned to it?
for connid, sigpos in pairs(influence_points[pts]) do
- local asp = advtrains.interlocking.signal_get_aspect(sigpos)
+ local asp = advtrains.interlocking.signal.get_aspect(sigpos)
if not asp then
atlog("Clearing orphaned signal influence point", pts, "/", connid)
ildb.clear_ip_signal(pts, connid)
diff --git a/advtrains_interlocking/mod.conf b/advtrains_interlocking/mod.conf
index 3b2d029..9191dd9 100644
--- a/advtrains_interlocking/mod.conf
+++ b/advtrains_interlocking/mod.conf
@@ -4,4 +4,4 @@ description=Interlocking system for Advanced Trains
author=orwell96
depends=advtrains
-optional_depends=advtrains_train_track
+#optional_depends=advtrains_train_track
diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua
index 2f0f8ee..3bdf6d6 100644
--- a/advtrains_interlocking/route_prog.lua
+++ b/advtrains_interlocking/route_prog.lua
@@ -535,9 +535,12 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
-- show formspec
show_routing_form(pname, tcbpos)
-
advtrains.interlocking.visualize_route(rp.origin, rp.route, "prog_"..pname, rp.tmp_lcks, pname)
-
+ return
+ elseif advtrains.interlocking.database.get_tcb(pos) then
+ -- the punched node itself is a TCB
+ show_routing_form(pname, pos)
+ advtrains.interlocking.visualize_route(rp.origin, rp.route, "prog_"..pname, rp.tmp_lcks, pname)
return
end
if advtrains.is_passive(pos) then
diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua
index 9b0479f..8347b1c 100644
--- a/advtrains_interlocking/signal_api.lua
+++ b/advtrains_interlocking/signal_api.lua
@@ -400,7 +400,8 @@ end
-- 0: not a signal at all
-- 1: signal has get_aspect_info() but the aspect is not variable (e.g. a sign)
-- 2: signal has apply_aspect() but does not have main aspects (e.g. a pure distant signal)
--- 3: Full capabilities, signal has main aspects and can be used as main/shunt signal (can be start/endpoint of a route)
+-- 3: signal has main signal role but can only ever display a halt aspect, such as a bumper (can be endpoint, but not startpoint, of a route)
+-- 4: Full capabilities, signal has main aspects and can be used as main/shunt signal (can be start/endpoint of a route)
function signal.get_signal_cap_level(pos)
local node = advtrains.ndb.get_node_or_nil(pos)
local ndef = node and minetest.registered_nodes[node.name]
@@ -408,6 +409,10 @@ function signal.get_signal_cap_level(pos)
if ndefat and ndefat.get_aspect_info then
if ndefat.apply_aspect then
if ndefat.main_aspects then
+ -- if the table contains anything, 4, otherwise 3
+ for _,_ in pairs(ndefat.main_aspects) do
+ return 4
+ end
return 3
end
return 2
@@ -419,9 +424,17 @@ end
----------------
-function signal.can_dig(pos)
+function signal.can_dig(pos, player)
local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
if sigd then
+ -- check privileges
+ if not player or not minetest.check_player_privs(player:get_player_name(), "interlocking") then
+ if not player then -- intermediate debug to uncover hard-to-find bugz
+ atdebug("advtrains.interlocking.signal.can_dig(",pos,") called with player==nil!")
+ end
+ return false
+ end
+ -- check if route is set
local tcbs = advtrains.interlocking.db.get_tcbs(sigd)
if tcbs.routeset then
return false
@@ -434,11 +447,7 @@ function signal.after_dig(pos, oldnode, oldmetadata, player)
-- unassign signal if necessary
local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
if sigd then
- local tcbs = advtrains.interlocking.db.get_tcbs(sigd)
- advtrains.interlocking.db.set_sigd_for_signal(pos, nil)
- tcbs.signal = nil
- tcbs.route_aspect = nil
- tcbs.route_remote = nil
+ advtrains.interlocking.db.unassign_signal_for_tcbs(sigd)
minetest.chat_send_player(player:get_player_name(), "Signal has been unassigned. Name and routes are kept for reuse.")
end
-- TODO clear influence point
diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua
index 1cdbb29..2b93234 100755
--- a/advtrains_interlocking/tcb_ts_ui.lua
+++ b/advtrains_interlocking/tcb_ts_ui.lua
@@ -156,7 +156,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
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)
+ local node_ok, conns, rhe = advtrains.get_rail_info_at(pos)
if node_ok and #conns == 2 then
-- if there is already a tcb here, reassign it
if ildb.get_tcb(pos) then
@@ -189,11 +189,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
if ndef and ndef.advtrains and ndef.advtrains.apply_aspect then
local tcbs = ildb.get_tcbs(sigd)
if tcbs then
- tcbs.signal = pos
- if not tcbs.routes then
- tcbs.routes = {}
- end
- ildb.set_sigd_for_signal(pos, sigd)
+ ildb.assign_signal_to_tcbs(pos, sigd)
minetest.chat_send_player(pname, "Configuring TCB: Successfully assigned signal.")
advtrains.interlocking.show_ip_form(pos, pname, true)
else
@@ -212,6 +208,138 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
end
end)
+-- "Self-contained TCB"
+-- 2024-11-25: Buffers should become their own TCB (and signal) automatically to permit setting routes to them
+-- These are support functions for this kind of node.
+
+-- Create an after_place_node callback for a self-contained TCB node. The parameters control additional behavior:
+-- fail_silently_on_noprivs: (boolean) Does not give an error in case the placer does not have the interlocking privilege
+-- auto_create_self_signal: (boolean) Automatically assign this same node as signal to the A side of the newly-created TCB
+-- (this is useful for buffers as they serve both as TCB and as an always-halt signal)
+function advtrains.interlocking.self_tcb_make_after_place_callback(fail_silently_on_noprivs, auto_create_self_signal)
+ return function(pos, player, itemstack, pointed_thing)
+ atdebug("selftcb apn ",pos, player, itemstack, pointed_thing)
+ local pname = player:get_player_name()
+ if not minetest.check_player_privs(pname, "interlocking") then
+ if not fail_silently_on_noprivs then
+ minetest.chat_send_player(pname, "Insufficient privileges to use this!")
+ end
+ return
+ end
+ if ildb.get_tcb(pos) then
+ minetest.chat_send_player(pname, "TCB already existed at this position, now linked to this node")
+ else
+ ildb.create_tcb_at(pos, pname)
+ end
+ if auto_create_self_signal then
+ local sigd = { p = pos, s = 1 }
+ local tcbs = ildb.get_tcbs(sigd)
+ -- make sure signal doesn't already exist
+ if tcbs.signal then
+ minetest.chat_send_player(pname, "Signal on B side already assigned!")
+ return
+ end
+ ildb.assign_signal_to_tcbs(pos, sigd)
+ -- assign influence point to itself
+ ildb.set_ip_signal(advtrains.roundfloorpts(pos), 1, pos)
+ end
+ end
+end
+
+-- Create an can_dig callback for a self-contained TCB node. The parameters control additional behavior:
+-- is_signal: (boolean) Whether this node is also a signal (in addition to being a TCB), e.g. when auto_create_self_signal was set.
+-- Causes also the signal API's can_dig to be called
+function advtrains.interlocking.self_tcb_make_can_dig_callback(is_signal)
+ return function(pos, player)
+ local pname = player and player:get_player_name() or ""
+ -- need to duplicate logic of the regular "can_dig_or_modify_track()" function in core/tracks.lua
+ if advtrains.get_train_at_pos(pos) then
+ minetest.chat_send_player(pname, "Can't remove track, a train is here!")
+ return false
+ end
+ -- end of standard checks
+ local tcb = ildb.get_tcb(pos)
+ if not tcb then
+ -- digging always allowed because the TCB hasn't been created (unless signal callback interjects)
+ if is_signal then
+ return advtrains.interlocking.signal.can_dig(pos, player)
+ else
+ return true
+ end
+ end
+ -- TCB exists
+ if not minetest.check_player_privs(pname, "interlocking") then
+ return false
+ end
+ -- fine to remove (unless signal callback interjects)
+ if is_signal then
+ return advtrains.interlocking.signal.can_dig(pos, player)
+ else
+ return true
+ end
+ end
+end
+
+-- Create an after_dig_node callback for a self-contained TCB node. The parameters control additional behavior:
+-- is_signal: (boolean) Whether this node is also a signal (in addition to being a TCB), e.g. when auto_create_self_signal was set.
+-- Causes also the signal API's after_dig_node to be called
+function advtrains.interlocking.self_tcb_make_after_dig_callback(is_signal)
+ return function(pos, oldnode, oldmetadata, player)
+ local pname = player:get_player_name()
+ if is_signal then
+ -- "dig" the signal first
+ advtrains.interlocking.signal.after_dig(pos, oldnode, oldmetadata, player)
+ end
+ if ildb.get_tcb(pos) then
+ -- remove the TCB
+ ildb.remove_tcb_at(pos, pname, true)
+ end
+ end
+end
+
+-- Create an on_rightclick callback for a self-contained TCB node. The rightclick callback tries to repeat the TCB assignment
+-- if necessary and otherwise shows the TCB formspec. The parameters control additional behavior:
+-- fail_silently_on_noprivs: (boolean) Does not give an error in case the placer does not have the interlocking privilege
+-- auto_create_self_signal: (boolean) Automatically assign this same node as signal to the B side of the
+-- newly-created TCB if that has not already happened during place.
+-- Otherwise, opens the signal dialog instead of the TCB dialog on rightclick
+function advtrains.interlocking.self_tcb_make_on_rightclick_callback(fail_silently_on_noprivs, auto_create_self_signal)
+ return function(pos, node, player, itemstack, pointed_thing)
+ local pname = player:get_player_name()
+ if not minetest.check_player_privs(pname, "interlocking") then
+ if not fail_silently_on_noprivs then
+ minetest.chat_send_player(pname, "Insufficient privileges to use this!")
+ end
+ return
+ end
+ if ildb.get_tcb(pos) then
+ -- TCB already here. go on
+ else
+ -- otherwise create tcb
+ ildb.create_tcb_at(pos, pname)
+ end
+ if auto_create_self_signal then
+ local sigd = { p = pos, s = 1 }
+ local tcbs = ildb.get_tcbs(sigd)
+ -- make sure signal doesn't already exist
+ if not tcbs.signal then
+ -- go ahead and assign signal
+ ildb.assign_signal_to_tcbs(pos, sigd)
+ -- assign influence point to itself
+ ildb.set_ip_signal(advtrains.roundfloorpts(pos), 1, pos)
+ end
+ -- in any case open the signalling form nouw
+ local control = player:get_player_control()
+ advtrains.interlocking.show_signal_form(pos, node, pname, control.aux1)
+ return
+ else
+ -- not an autosignal. Then show the TCB form
+ advtrains.interlocking.show_tcb_form(pos, pname)
+ return
+ end
+ end
+end
+
-- TCB Form
local function mktcbformspec(pos, side, tcbs, offset, pname)
@@ -666,7 +794,7 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle
-- no route is active, and no route is so far defined
if not tcbs.signal then atwarn("signalling form missing signal?!", pos) return end -- safeguard, nothing else in this function checks tcbs.signal
local caps = advtrains.interlocking.signal.get_signal_cap_level(tcbs.signal)
- if caps >= 3 then
+ if caps >= 4 then
-- offer user the "block signal mode"
form = form.."label[0.5,2.5;No routes are yet defined.]"
if hasprivs then
@@ -683,6 +811,10 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle
.."Sets a route into the section ahead with auto-working set on\n"
.."Short block: This signal becomes distant signal for next signal.]"
end
+ elseif caps >= 3 then
+ -- it's a buffer!
+ form = form.."label[0.5,2.5;This is an always-halt signal (e.g. a buffer)\n"
+ .."No routes can be set from here.]"
else
-- signal caps say it cannot be route start/end
form = form.."label[0.5,2.5;This is a Non-Halt signal (e.g. pure distant signal)\n"