aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authororwell <orwell@bleipb.de>2024-11-25 22:31:26 +0100
committerorwell <orwell@bleipb.de>2024-11-25 22:31:26 +0100
commit922e654b7bef51c7ddaf510ec70880d48181dd35 (patch)
tree3d12820d7a67180df3d2bd36d3fa7b4d61c1166c
parent73c393e223b1d2d81b767d8036c7fdec7ca20100 (diff)
downloadadvtrains-922e654b7bef51c7ddaf510ec70880d48181dd35.tar.gz
advtrains-922e654b7bef51c7ddaf510ec70880d48181dd35.tar.bz2
advtrains-922e654b7bef51c7ddaf510ec70880d48181dd35.zip
Make Buffers become implicitly their own TCBs and signals when interlocking is enabled
-rw-r--r--advtrains/signals.lua8
-rw-r--r--advtrains/trackplacer.lua16
-rw-r--r--advtrains/tracks.lua1
-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
-rw-r--r--advtrains_train_track/init.lua29
-rw-r--r--advtrains_train_track/mod.conf2
10 files changed, 242 insertions, 33 deletions
diff --git a/advtrains/signals.lua b/advtrains/signals.lua
index 4dec7f5..8bdd877 100644
--- a/advtrains/signals.lua
+++ b/advtrains/signals.lua
@@ -3,15 +3,15 @@
local mrules_wallsignal = advtrains.meseconrules
-local function can_dig_func(pos)
+local function can_dig_func(pos, player)
if advtrains.interlocking then
- return advtrains.interlocking.signal.can_dig(pos)
+ return advtrains.interlocking.signal.can_dig(pos, player)
end
return true
end
-local function after_dig_func(pos)
+local function after_dig_func(pos, oldnode, oldmetadata, digger)
if advtrains.interlocking then
- return advtrains.interlocking.signal.after_dig(pos)
+ return advtrains.interlocking.signal.after_dig(pos, oldnode, oldmetadata, digger)
end
return true
end
diff --git a/advtrains/trackplacer.lua b/advtrains/trackplacer.lua
index 6a2c7a8..1543209 100644
--- a/advtrains/trackplacer.lua
+++ b/advtrains/trackplacer.lua
@@ -152,12 +152,14 @@ local function check_or_bend_rail(origin, dir, pname, commit)
end
end
-local function track_place_node(pos, node, ndef)
+local function track_place_node(pos, node, ndef, pname)
--atdebug("track_place_node: ",pos, node)
advtrains.ndb.swap_node(pos, node)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.after_place_node then
- ndef.after_place_node(pos)
+ -- resolve player again
+ local player = pname and core.get_player_by_name(pname) or nil
+ ndef.after_place_node(pos, player) -- note: itemstack and pointed_thing are NOT available here anymore (crap!)
end
end
@@ -191,16 +193,16 @@ function tp.place_track(pos, tpg, pname, yaw)
for k1, conn1 in ipairs(cand) do
for k2, conn2 in ipairs(cand) do
if k1~=k2 then
- -- order of conn1/conn2: prefer conn2 being in the direction of the player facing.
+ -- order of conn1/conn2: prefer conn1 being in the direction of the player facing.
-- the combination the other way round will be run through in a later loop iteration
- if advtrains.yawToDirection(yaw, conn1, conn2) == conn2 then
+ if advtrains.yawToDirection(yaw, conn1, conn2) == conn1 then
-- does there exist a suitable double-connection rail?
--atdebug("Try double conn: ",conn1, conn2)
local node = g.double[conn1.."_"..conn2]
if node then
check_or_bend_rail(pos, conn1, pname, true)
check_or_bend_rail(pos, conn2, pname, true)
- track_place_node(pos, node) -- calls after_place_node implicitly
+ track_place_node(pos, node, pname) -- calls after_place_node implicitly
return true
end
end
@@ -220,13 +222,13 @@ function tp.place_track(pos, tpg, pname, yaw)
local node = single[conn1]
if node then
check_or_bend_rail(pos, conn1, pname, true)
- track_place_node(pos, node) -- calls after_place_node implicitly
+ track_place_node(pos, node, nil, pname) -- calls after_place_node implicitly
return true
end
end
-- 4. if nothing worked, set the default
local node = g.default
- track_place_node(pos, node) -- calls after_place_node implicitly
+ track_place_node(pos, node, nil, pname) -- calls after_place_node implicitly
return true
end
diff --git a/advtrains/tracks.lua b/advtrains/tracks.lua
index 661da8a..fa7b702 100644
--- a/advtrains/tracks.lua
+++ b/advtrains/tracks.lua
@@ -253,6 +253,7 @@ end
-- Function called when a track is about to be dug or modified by the trackworker
-- Returns either true (ok) or false,"translated string describing reason why it isn't allowed"
+-- Impl Note: possibly duplicate code in "self contained TCB" - see interlocking/tcb_ts_ui.lua!
function advtrains.can_dig_or_modify_track(pos)
if advtrains.get_train_at_pos(pos) then
return false, attrans("Position is occupied by a train.")
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"
diff --git a/advtrains_train_track/init.lua b/advtrains_train_track/init.lua
index 32e1235..f551ec5 100644
--- a/advtrains_train_track/init.lua
+++ b/advtrains_train_track/init.lua
@@ -600,6 +600,35 @@ advtrains.register_tracks("default", {
--bumpers still use the old texture until the models are redone.
description=attrans("Bumper"),
formats={},
+ get_additional_definiton = function(def, preset, suffix, rotation)
+ -- 2024-11-25: Bumpers get the additional feature of being both a signal and a self-contained TCB, when interlocking is used.
+ if advtrains.interlocking then
+ return {
+ -- use the special callbacks for self_tcb (see tcb_ts_ui.lua)
+ can_dig = advtrains.interlocking.self_tcb_make_can_dig_callback(true),
+ after_dig_node = advtrains.interlocking.self_tcb_make_after_dig_callback(true),
+ after_place_node = advtrains.interlocking.self_tcb_make_after_place_callback(true, true),
+ on_rightclick = advtrains.interlocking.self_tcb_make_on_rightclick_callback(false, true),
+ advtrains = {
+ main_aspects = {
+ -- No main aspects, it always shows Stop.
+ -- But we need to define the table so that signal caplevel is 3
+ },
+ apply_aspect = function(pos, node, main_aspect, rem_aspect, rem_aspinfo)
+ -- is a no-op for bumpers, it always shows Stop
+ end,
+ get_aspect_info = function(pos, main_aspect)
+ -- it always shows Stop
+ return advtrains.interlocking.signal.ASPI_HALT
+ end,
+ distant_support = false, -- not a distant
+ route_role = "end", -- the end is nigh!
+ }
+ }
+ else
+ return {} -- no additional defs when interlocking is not used
+ end
+ end,
}, advtrains.ap.t_30deg_straightonly)
minetest.register_craft({
output = 'advtrains:dtrack_bumper_placer 2',
diff --git a/advtrains_train_track/mod.conf b/advtrains_train_track/mod.conf
index 2aece3e..a7fef4d 100644
--- a/advtrains_train_track/mod.conf
+++ b/advtrains_train_track/mod.conf
@@ -4,4 +4,4 @@ description=Default track set for Advanced Trains
author=orwell96
depends=advtrains
-optional_depends=mesecons,digtron
+optional_depends=mesecons,digtron,advtrains_interlocking