aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking/distant.lua
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_interlocking/distant.lua')
-rw-r--r--advtrains_interlocking/distant.lua200
1 files changed, 200 insertions, 0 deletions
diff --git a/advtrains_interlocking/distant.lua b/advtrains_interlocking/distant.lua
new file mode 100644
index 0000000..32ada82
--- /dev/null
+++ b/advtrains_interlocking/distant.lua
@@ -0,0 +1,200 @@
+--- Distant signaling.
+-- This module implements a database backend for distant signal assignments.
+-- The actual modifications to signal aspects are still done by signal aspect accessors.
+-- @module advtrains.interlocking.distant
+
+local db_distant = {}
+local db_distant_of = {}
+
+local pts = advtrains.encode_pos
+local stp = advtrains.decode_pos
+
+--- Replace the distant signal assignment database.
+-- @function load
+-- @param db The new database to load.
+local function db_load(x)
+ if type(x) ~= "table" then
+ return
+ end
+ db_distant = x.distant
+ db_distant_of = x.distant_of
+end
+
+--- Retrieve the current distant signal assignment database.
+-- @function save
+-- @return The current database.
+local function db_save()
+ return {
+ distant = db_distant,
+ distant_of = db_distant_of,
+ }
+end
+
+local update_signal, update_main, update_dst
+
+--- Unassign a distant signal.
+-- @function unassign_dst
+-- @param dst The position of the distant signal.
+-- @param[opt=false] force Whether to skip callbacks.
+local function unassign_dst(dst, force)
+ local pts_dst = pts(dst)
+ local main = db_distant_of[pts_dst]
+ db_distant_of[pts_dst] = nil
+ if main then
+ local pts_main = main[1]
+ local t = db_distant[pts_main]
+ if t then
+ t[pts_dst] = nil
+ end
+ end
+ if not force then
+ update_dst(dst)
+ end
+end
+
+--- Unassign a main signal.
+-- @function unassign_main
+-- @param main The position of the main signal.
+-- @param[opt=false] force Whether to skip callbacks.
+local function unassign_main(main, force)
+ local pts_main = pts(main)
+ local t = db_distant[pts_main]
+ if not t then
+ return
+ end
+ for pts_dst in pairs(t) do
+ local realmain = db_distant_of[pts_dst]
+ if realmain and realmain[1] == pts_main then
+ db_distant_of[pts_dst] = nil
+ if not force then
+ local dst = stp(pts_dst)
+ update_dst(dst)
+ end
+ end
+ end
+ db_distant[pts_main] = nil
+end
+
+--- Remove all (main and distant) signal assignments from a signal.
+-- @function unassign_all
+-- @param pos The position of the signal.
+-- @param[opt=false] force Whether to skip callbacks.
+local function unassign_all(pos, force)
+ unassign_main(pos)
+ unassign_dst(pos, force)
+end
+
+--- Check whether a signal is "appropriate" for the distant signal system.
+-- Currently, a signal is considered appropriate if its signal aspect can be set.
+-- @function appropriate_signal
+-- @param pos The position of the signal
+local function appropriate_signal(pos)
+ local node = advtrains.ndb.get_node(pos)
+ local ndef = minetest.registered_nodes[node.name] or {}
+ if not ndef then
+ return false
+ end
+ local atdef = ndef.advtrains
+ if not atdef then
+ return false
+ end
+ return atdef.supported_aspects and atdef.set_aspect and true
+end
+
+--- Assign a distant signal to a main signal.
+-- @function assign
+-- @param main The position of the main signal.
+-- @param dst The position of the distant signal.
+-- @param[opt="manual"] by The method of assignment.
+-- @param[opt=false] skip_update Whether to skip callbacks.
+local function assign(main, dst, by, skip_update)
+ if not (appropriate_signal(main) and appropriate_signal(dst)) then
+ return
+ end
+ local pts_main = pts(main)
+ local pts_dst = pts(dst)
+ local t = db_distant[pts_main]
+ if not t then
+ t = {}
+ db_distant[pts_main] = t
+ end
+ if not by then
+ by = "manual"
+ end
+ unassign_dst(dst, true)
+ t[pts_dst] = by
+ db_distant_of[pts_dst] = {pts_main, by}
+ if not skip_update then
+ update_dst(dst)
+ end
+end
+
+--- Get the distant signals assigned to a main signal.
+-- @function get_distant
+-- @param main The position of the main signal.
+-- @treturn {[pos]=by,...} A table of distant signals, with the positions encoded using `advtrains.encode_pos`.
+local function get_distant(main)
+ local pts_main = pts(main)
+ return db_distant[pts_main] or {}
+end
+
+--- Get the main signal assigned the a distant signal.
+-- @function get_main
+-- @param dst The position of the distant signal.
+-- @return The position of the main signal.
+-- @return The method of assignment.
+local function get_main(dst)
+ local pts_dst = pts(dst)
+ local main = db_distant_of[pts_dst]
+ if not main then
+ return
+ end
+ if main[1] then
+ return stp(main[1]), unpack(main, 2)
+ else
+ return unpack(main)
+ end
+end
+
+--- Update all distant signals assigned to a main signal.
+-- @function update_main
+-- @param main The position of the main signal.
+update_main = function(main)
+ local pts_main = pts(main)
+ local t = get_distant(main)
+ for pts_dst in pairs(t) do
+ local dst = stp(pts_dst)
+ advtrains.interlocking.signal_readjust_aspect(dst)
+ end
+end
+
+--- Update the aspect of a distant signal.
+-- @function update_dst
+-- @param dst The position of the distant signal.
+update_dst = function(dst)
+ advtrains.interlocking.signal_readjust_aspect(dst)
+end
+
+--- Update the aspect of a combined (main and distant) signal and all distant signals assigned to it.
+-- @function update_signal
+-- @param pos The position of the signal.
+update_signal = function(pos)
+ update_main(pos)
+ update_dst(pos)
+end
+
+advtrains.distant = {
+ load = db_load,
+ save = db_save,
+ assign = assign,
+ unassign_dst = unassign_dst,
+ unassign_main = unassign_main,
+ unassign_all = unassign_all,
+ get_distant = get_distant,
+ get_dst = get_distant,
+ get_main = get_main,
+ update_main = update_main,
+ update_dst = update_dst,
+ update_signal = update_signal,
+ appropriate_signal = appropriate_signal,
+}