aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_interlocking')
-rw-r--r--advtrains_interlocking/database.lua2
-rw-r--r--advtrains_interlocking/distant.lua200
-rw-r--r--advtrains_interlocking/init.lua3
-rw-r--r--advtrains_interlocking/route_ui.lua18
-rw-r--r--advtrains_interlocking/routesetting.lua15
-rw-r--r--advtrains_interlocking/signal_api.lua220
-rw-r--r--advtrains_interlocking/signal_aspect_accessors.lua163
-rw-r--r--advtrains_interlocking/signal_aspect_ui.lua366
-rwxr-xr-xadvtrains_interlocking/tcb_ts_ui.lua11
9 files changed, 207 insertions, 791 deletions
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index 4213c3d..e2df547 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -1006,7 +1006,7 @@ end
function ildb.get_ip_signal_asp(pts, connid)
local p = ildb.get_ip_signal(pts, connid)
if p then
- local asp = advtrains.interlocking.signal_get_aspect(p)
+ local asp = advtrains.interlocking.signal.get_aspect(p)
if not asp then
atlog("Clearing orphaned signal influence point", pts, "/", connid)
ildb.clear_ip_signal(pts, connid)
diff --git a/advtrains_interlocking/distant.lua b/advtrains_interlocking/distant.lua
deleted file mode 100644
index 32ada82..0000000
--- a/advtrains_interlocking/distant.lua
+++ /dev/null
@@ -1,200 +0,0 @@
---- 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,
-}
diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua
index dd08b4a..c397aa6 100644
--- a/advtrains_interlocking/init.lua
+++ b/advtrains_interlocking/init.lua
@@ -15,9 +15,6 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELI
--advtrains.interlocking.aspect = dofile(modpath.."aspect.lua")
dofile(modpath.."database.lua")
-dofile(modpath.."distant.lua")
-dofile(modpath.."distant_ui.lua")
-dofile(modpath.."signal_aspect_accessors.lua")
dofile(modpath.."signal_api.lua")
dofile(modpath.."signal_aspect_ui.lua")
dofile(modpath.."demosignals.lua")
diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua
index a1a331d..982c579 100644
--- a/advtrains_interlocking/route_ui.lua
+++ b/advtrains_interlocking/route_ui.lua
@@ -33,7 +33,7 @@ function atil.show_route_edit_form(pname, sigd, routeid)
local function itab(t)
tab[#tab+1] = minetest.formspec_escape(string.gsub(t, ",", " "))
end
- itab("TCB "..sigd_to_string(sigd).." ("..(tcbs.signal_name or "")..") Route #"..routeid)
+ itab("("..(tcbs.signal_name or "+")..") Route #"..routeid)
-- this code is partially copy-pasted from routesetting.lua
-- we start at the tc designated by signal
@@ -56,13 +56,14 @@ function atil.show_route_edit_form(pname, sigd, routeid)
c_rseg = route[i]
c_lckp = {}
- itab(""..i.." Entry "..sigd_to_string(c_sigd).." -> Sec. "..(c_ts and c_ts.name or "-").." -> Exit "..(c_rseg.next and sigd_to_string(c_rseg.next) or "END"))
+ itab(""..i.." "..sigd_to_string(c_sigd))
+ itab("= "..(c_ts and c_ts.name or "-").." =")
if c_rseg.locks then
for pts, state in pairs(c_rseg.locks) do
local pos = minetest.string_to_pos(pts)
- itab(" Lock: "..pts.." -> "..state)
+ itab("L "..pts.." -> "..state)
if not advtrains.is_passive(pos) then
itab("-!- No passive component at "..pts..". Please reconfigure route!")
break
@@ -75,16 +76,17 @@ function atil.show_route_edit_form(pname, sigd, routeid)
end
if c_sigd then
local e_tcbs = ildb.get_tcbs(c_sigd)
- itab("Route end: "..sigd_to_string(c_sigd).." ("..(e_tcbs and e_tcbs.signal_name or "-")..")")
+ local signame = "-"
+ if e_tcbs and e_tcbs.signal then signame = e_tcbs.signal_name or "+" end
+ itab("E "..sigd_to_string(c_sigd).." ("..signame..")")
else
- itab("Route ends on dead-end")
+ itab("E (none)")
end
- form = form.."textlist[0.5,2;7.75,3.9;rtelog;"..table.concat(tab, ",").."]"
+ form = form.."textlist[0.5,2;3,3.9;rtelog;"..table.concat(tab, ",").."]"
form = form.."button[0.5,6;3,1;back;<<< Back to signal]"
- form = form.."button[4.5,6;2,1;aspect;Signal Aspect]"
- form = form.."button[6.5,6;2,1;delete;Delete Route]"
+ form = form.."button[5.5,6;3,1;delete;Delete Route]"
--atdebug(route.ars)
form = form.."style[ars;font=mono]"
diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua
index 24b3199..a576139 100644
--- a/advtrains_interlocking/routesetting.lua
+++ b/advtrains_interlocking/routesetting.lua
@@ -138,7 +138,7 @@ function ilrs.set_route(signal, route, try)
}
if c_tcbs.signal then
c_tcbs.route_committed = true
- c_tcbs.aspect = route.aspect or advtrains.interlocking.FULL_FREE
+ c_tcbs.aspect = advtrains.interlocking.signal.MASP_FREE
c_tcbs.route_origin = signal
signals[#signals+1] = c_tcbs
end
@@ -166,7 +166,7 @@ function ilrs.set_route(signal, route, try)
if (not nodst) and (not assigned_by or assigned_by == "routesetting") then
advtrains.distant.assign(lastsig, pos, "routesetting", true)
end
- advtrains.interlocking.update_signal_aspect(tcbs, i ~= 1)
+ advtrains.interlocking.signal.update_route_aspect(tcbs, i ~= 1)
end
end
@@ -278,14 +278,7 @@ function ilrs.cancel_route_from(sigd)
c_tcbs.route_auto = nil
c_tcbs.route_origin = nil
- if c_tcbs.signal then
- local pos = c_tcbs.signal
- local _, assigned_by = advtrains.distant.get_main(pos)
- if assigned_by == "routesetting" then
- advtrains.distant.unassign_dst(pos, true)
- end
- end
- advtrains.interlocking.update_signal_aspect(c_tcbs)
+ advtrains.interlocking.signal.update_route_aspect(c_tcbs)
c_ts_id = c_tcbs.ts_id
if not c_tcbs then
@@ -370,7 +363,7 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel)
end
if has_changed_aspect then
-- FIX: prevent an minetest.after() loop caused by update_signal_aspect dispatching path invalidation, which in turn calls ARS again
- advtrains.interlocking.update_signal_aspect(tcbs)
+ advtrains.interlocking.signal.update_route_aspect(tcbs)
end
advtrains.interlocking.update_player_forms(sigd)
end
diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua
index d27a045..5216594 100644
--- a/advtrains_interlocking/signal_api.lua
+++ b/advtrains_interlocking/signal_api.lua
@@ -5,9 +5,15 @@ local F = advtrains.formspec
local signal = {}
signal.MASP_HALT = {
- name = "halt",
- description = "HALT",
- halt = true,
+ name = nil,
+ speed = nil,
+ remote = nil,
+}
+
+signal.MASP_FREE = {
+ name = "_free",
+ speed = -1,
+ remote = nil,
}
signal.ASPI_HALT = {
@@ -50,11 +56,12 @@ Note that once apply_aspect returns, there is no need for advtrains anymore to q
When the signal, for any reason, wants to change its aspect by itself *without* going through the signal API then
it should update the aspect info cache by calling advtrains.interlocking.signal.update_aspect_info(pos)
-Note that the apply_aspect function MUST accept the following main aspect, even if it is not defined in the main_aspects table:
-{ name = "halt", halt = true }
-It should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only
+Apply_aspect may also receive nil as the main aspect. It usually means that the signal is not assigned to anything particular,
+and it should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only
signals this would be "expect stop".
+Main aspect names starting with underscore (e.g. "_default") are reserved and must not be used!
+
== Aspect Info ==
The actual signal aspect in the already-known format. This is what the trains use to determine halt/proceed and speed.
asp = {
@@ -152,7 +159,7 @@ end
-- - Storing the main aspect and dst pos for this signal permanently (until next change)
-- - Assigning the distant signal for this signal
-- - Calling apply_aspect() in the signal's node definition to make the signal show the aspect
--- - Calling apply_aspect() again whenever the distant signal changes its aspect
+-- - Calling apply_aspect() again whenever the remote signal changes its aspect
-- - Notifying this signal's distant signals about changes to this signal (unless skip_dst_notify is specified)
function signal.set_aspect(pos, main_asp_name, main_asp_speed, rem_pos, skip_dst_notify)
local main_pts = advtrains.encode_pos(pos)
@@ -252,10 +259,7 @@ function signal.get_aspect(pos)
end
local function cache_mainaspects(ndefat)
- ndefat.main_aspects_lookup = {
- -- always define halt aspect
- halt = signal.MASP_HALT
- }
+ ndefat.main_aspects_lookup = {}
for _,ma in ipairs(ndefat.main_aspects) do
ndefat.main_aspects_lookup[ma.name] = ma
end
@@ -264,7 +268,7 @@ end
function signal.get_aspect_internal(pos, aspt)
if not aspt then
-- oh, no main aspect, nevermind
- return nil, aspt.remote, nil
+ return nil, nil, nil
end
atdebug("get_aspect_internal",pos,aspt)
-- look aspect in nodedef
@@ -277,6 +281,10 @@ function signal.get_aspect_internal(pos, aspt)
cache_mainaspects(ndefat)
end
local masp = ndefat.main_aspects_lookup[aspt.name]
+ -- special handling for the default free aspect ("_free")
+ if aspt.name == "_free" then
+ masp = ndefat.main_aspects[1]
+ end
if not masp then
atwarn(pos,"invalid main aspect",aspt.name,"valid are",ndefat.main_aspects_lookup)
return nil, aspt.remote, node, ndef
@@ -355,10 +363,30 @@ end
function signal.update_route_aspect(tcbs, skip_dst_notify)
if tcbs.signal then
local asp = tcbs.aspect or signal.MASP_HALT
- signal.set_aspect(tcbs.signal, asp, skip_dst_notify)
+ signal.set_aspect(tcbs.signal, asp.name, asp.speed, asp.remote, skip_dst_notify)
end
end
+-- Returns how capable the signal is with regards to aspect setting
+-- 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)
+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]
+ local ndefat = ndef and ndef.advtrains
+ if ndefat and ndefat.get_aspect_info then
+ if ndefat.apply_aspect then
+ if ndefat.main_aspects then
+ return 3
+ end
+ return 2
+ end
+ return 1
+ end
+ return 0
+end
----------------
@@ -366,7 +394,7 @@ function signal.can_dig(pos)
return not advtrains.interlocking.db.get_sigd_for_signal(pos)
end
-function advtrains.interlocking.signal_after_dig(pos)
+function signal.after_dig(pos)
-- TODO clear influence point
advtrains.interlocking.signal.clear_aspect(pos)
end
@@ -374,169 +402,7 @@ end
function signal.on_rightclick(pos, node, player, itemstack, pointed_thing)
local pname = player:get_player_name()
local control = player:get_player_control()
- if control.aux1 then
- advtrains.interlocking.show_ip_form(pos, pname)
- return
- end
- advtrains.interlocking.show_signal_form(pos, node, pname)
-end
-
-function advtrains.interlocking.show_signal_form(pos, node, pname)
- local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
- if sigd then
- advtrains.interlocking.show_signalling_form(sigd, pname)
- else
- local ndef = minetest.registered_nodes[node.name]
- if ndef.advtrains and ndef.advtrains.set_aspect then
- -- permit to set aspect manually
- local function callback(pname, aspect)
- signal.set_aspect(pos, aspect)
- end
- local isasp = advtrains.interlocking.signal_get_aspect(pos, node)
-
- advtrains.interlocking.show_signal_aspect_selector(
- pname,
- ndef.advtrains.supported_aspects,
- pos, callback,
- isasp)
- else
- --static signal - only IP
- advtrains.interlocking.show_ip_form(pos, pname)
- end
- end
-end
-
-local players_assign_ip = {}
-
-local function ipmarker(ipos, connid)
- local node_ok, conns, rhe = advtrains.get_rail_info_at(ipos, advtrains.all_tracktypes)
- if not node_ok then return end
- local yaw = advtrains.dir_to_angle(conns[connid].c)
-
- -- using tcbmarker here
- local obj = minetest.add_entity(vector.add(ipos, {x=0, y=0.2, z=0}), "advtrains_interlocking:tcbmarker")
- if not obj then return end
- obj:set_yaw(yaw)
- obj:set_properties({
- textures = { "at_il_signal_ip.png" },
- })
-end
-
-function advtrains.interlocking.make_ip_formspec_component(pos, x, y, w)
- advtrains.interlocking.db.check_for_duplicate_ip(pos)
- local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos)
- if pts then
- return table.concat {
- F.S_label(x, y, "Influence point is set at @1.", string.format("%s/%s", pts, connid)),
- F.S_button_exit(x, y+0.5, w/2-0.125, "ip_set", "Modify"),
- F.S_button_exit(x+w/2+0.125, y+0.5, w/2-0.125, "ip_clear", "Clear"),
- }, pts, connid
- else
- return table.concat {
- F.S_label(x, y, "Influence point is not set."),
- F.S_button_exit(x, y+0.5, w, "ip_set", "Set influence point"),
- }
- end
-end
-
--- shows small info form for signal properties
--- This function is named show_ip_form because it was originally only intended
--- for assigning/changing the influence point.
--- only_notset: show only if it is not set yet (used by signal tcb assignment)
-function advtrains.interlocking.show_ip_form(pos, pname, only_notset)
- if not minetest.check_player_privs(pname, "interlocking") then
- return
- end
- local ipform, pts, connid = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7)
- local form = {
- "formspec_version[4]",
- "size[8,2.25]",
- ipform,
- }
- if pts then
- local ipos = minetest.string_to_pos(pts)
- ipmarker(ipos, connid)
- end
- if advtrains.distant.appropriate_signal(pos) then
- form[#form+1] = advtrains.interlocking.make_dst_formspec_component(pos, 0.5, 2, 7, 4.25)
- form[2] = "size[8,6.75]"
- end
- form = table.concat(form)
- if not only_notset or not pts then
- minetest.show_formspec(pname, "at_il_propassign_"..minetest.pos_to_string(pos), form)
- end
+ advtrains.interlocking.show_signal_form(pos, node, pname, control.aux1)
end
-function advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields)
- if not (pos and minetest.check_player_privs(pname, {train_operator=true, interlocking=true})) then
- return
- end
- if fields.ip_set then
- advtrains.interlocking.signal_init_ip_assign(pos, pname)
- elseif fields.ip_clear then
- advtrains.interlocking.db.clear_ip_by_signalpos(pos)
- end
-end
-
-minetest.register_on_player_receive_fields(function(player, formname, fields)
- local pname = player:get_player_name()
- local pts = string.match(formname, "^at_il_propassign_([^_]+)$")
- local pos
- if pts then
- pos = minetest.string_to_pos(pts)
- end
- if pos then
- advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields)
- advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields)
- end
-end)
-
--- inits the signal IP assignment process
-function signal.init_ip_assign(pos, pname)
- if not minetest.check_player_privs(pname, "interlocking") then
- minetest.chat_send_player(pname, "Insufficient privileges to use this!")
- return
- end
- --remove old IP
- --advtrains.interlocking.db.clear_ip_by_signalpos(pos)
- minetest.chat_send_player(pname, "Configuring Signal: Please look in train's driving direction and punch rail to set influence point.")
-
- players_assign_ip[pname] = pos
-end
-
-minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
- local pname = player:get_player_name()
- if not minetest.check_player_privs(pname, "interlocking") then
- return
- end
- -- IP assignment
- local signalpos = players_assign_ip[pname]
- if signalpos then
- if vector.distance(pos, signalpos)<=50 then
- local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
- if node_ok and #conns == 2 then
-
- local yaw = player:get_look_horizontal()
- local plconnid = advtrains.yawToClosestConn(yaw, conns)
-
- -- add assignment if not already present.
- local pts = advtrains.roundfloorpts(pos)
- if not advtrains.interlocking.db.get_ip_signal_asp(pts, plconnid) then
- advtrains.interlocking.db.set_ip_signal(pts, plconnid, signalpos)
- ipmarker(pos, plconnid)
- minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point")
- else
- minetest.chat_send_player(pname, "Configuring Signal: Influence point of another signal is already present!")
- end
- else
- minetest.chat_send_player(pname, "Configuring Signal: This is not a normal two-connection rail! Aborted.")
- end
- else
- minetest.chat_send_player(pname, "Configuring Signal: Node is too far away. Aborted.")
- end
- players_assign_ip[pname] = nil
- end
-end)
-
-
advtrains.interlocking.signal = signal
diff --git a/advtrains_interlocking/signal_aspect_accessors.lua b/advtrains_interlocking/signal_aspect_accessors.lua
deleted file mode 100644
index d91df31..0000000
--- a/advtrains_interlocking/signal_aspect_accessors.lua
+++ /dev/null
@@ -1,163 +0,0 @@
---- Signal aspect accessors
--- @module advtrains.interlocking
-
-local A = advtrains.interlocking.aspect
-local D = advtrains.distant
-local I = advtrains.interlocking
-local N = advtrains.ndb
-local pts = advtrains.roundfloorpts
-
-local get_aspect
-
-local supposed_aspects = {}
-
---- Replace the signal aspect cache.
--- @function load_supposed_aspects
--- @param db The new database.
-function I.load_supposed_aspects(tbl)
- if tbl then
- supposed_aspects = {}
- for k, v in pairs(tbl) do
- supposed_aspects[k] = A(v)
- end
- end
-end
-
---- Retrieve the signal aspect cache.
--- @function save_supposed_aspects
--- @return The current database in use.
-function I.save_supposed_aspects()
- local t = {}
- for k, v in pairs(supposed_aspects) do
- t[k] = v:plain(true)
- end
- return t
-end
-
---- Read the aspect of a signal strictly from cache.
--- @param pos The position of the signal.
--- @return[1] The aspect of the signal (if present in cache).
--- @return[2] The nil constant (otherwise).
-local function get_supposed_aspect(pos)
- return supposed_aspects[pts(pos)]
-end
-
---- Update the signal aspect information in cache.
--- @param pos The position of the signal.
--- @param asp The new signal aspect
-local function set_supposed_aspect(pos, asp)
- supposed_aspects[pts(pos)] = asp
-end
-
---- Get the definition of a node.
--- @param pos The position of the node.
--- @return[1] The definition of the node (if present).
--- @return[2] An empty table (otherwise).
-local function get_ndef(pos)
- local node = N.get_node(pos)
- return (minetest.registered_nodes[node.name] or {}), node
-end
-
---- Get the aspects supported by a signal.
--- @function signal_get_supported_aspects
--- @param pos The position of the signal.
--- @return[1] The table of supported aspects (if present).
--- @return[2] The nil constant (otherwise).
-local function get_supported_aspects(pos)
- local ndef = get_ndef(pos)
- if ndef.advtrains and ndef.advtrains.supported_aspects then
- return ndef.advtrains.supported_aspects
- end
- return nil
-end
-
---- Adjust a new signal aspect to fit a signal.
--- @param pos The position of the signal.
--- @param asp The new signal aspect.
--- @return The adjusted signal aspect.
--- @return The information to pass to the `advtrains.set_aspect` field in the node definitions.
-local function adjust_aspect(pos, asp)
- local asp = A(asp)
-
- local mainpos = D.get_main(pos)
- local nxtasp
- if mainpos then
- nxtasp = get_aspect(mainpos)
- end
- local suppasp = get_supported_aspects(pos)
- if not suppasp then
- return asp
- end
- return asp:adjust_distant(nxtasp, suppasp.dst_shift):to_group(suppasp.group)
-end
-
---- Get the aspect of a signal without accessing the cache.
--- For most cases, `get_aspect` should be used instead.
--- @function signal_get_real_aspect
--- @param pos The position of the signal.
--- @return[1] The signal aspect adjusted using `adjust_aspect` (if present).
--- @return[2] The nil constant (otherwise).
-local function get_real_aspect(pos)
- local ndef, node = get_ndef(pos)
- if ndef.advtrains and ndef.advtrains.get_aspect then
- local asp = ndef.advtrains.get_aspect(pos, node) or I.DANGER
- return adjust_aspect(pos, asp)
- end
- return nil
-end
-
---- Get the aspect of a signal.
--- @function signal_get_aspect
--- @param pos The position of the signal.
--- @return[1] The aspect of the signal (if present).
--- @return[2] The nil constant (otherwise).
-get_aspect = function(pos)
- local asp = get_supposed_aspect(pos)
- if not asp then
- asp = get_real_aspect(pos)
- set_supposed_aspect(pos, asp)
- end
- return asp
-end
-
---- Set the aspect of a signal.
--- @function signal_set_aspect
--- @param pos The position of the signal.
--- @param asp The new signal aspect.
--- @param[opt=false] skipdst Whether to skip updating distant signals.
-local function set_aspect(pos, asp, skipdst)
- local node = N.get_node(pos)
- local ndef = minetest.registered_nodes[node.name]
- if ndef and ndef.advtrains and ndef.advtrains.set_aspect then
- local oldasp = I.signal_get_aspect(pos) or DANGER
- local newasp = adjust_aspect(pos, asp)
- set_supposed_aspect(pos, newasp)
- ndef.advtrains.set_aspect(pos, node, newasp)
- I.signal_on_aspect_changed(pos)
- local aspect_changed = oldasp ~= newasp
- if (not skipdst) and aspect_changed then
- D.update_main(pos)
- end
- end
-end
-
---- Remove a signal from cache.
--- @function signal_clear_aspect
--- @param pos The position of the signal.
-local function clear_aspect(pos)
- set_supposed_aspect(pos, nil)
-end
-
---- Readjust the aspect of a signal.
--- @function signal_readjust_aspect
--- @param pos The position of the signal.
-local function readjust_aspect(pos)
- set_aspect(pos, get_aspect(pos))
-end
-
-I.signal_get_supported_aspects = get_supported_aspects
-I.signal_get_real_aspect = get_real_aspect
-I.signal_get_aspect = get_aspect
-I.signal_set_aspect = set_aspect
-I.signal_clear_aspect = clear_aspect
-I.signal_readjust_aspect = readjust_aspect
diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua
index a81b7fe..e5d2003 100644
--- a/advtrains_interlocking/signal_aspect_ui.lua
+++ b/advtrains_interlocking/signal_aspect_ui.lua
@@ -1,262 +1,188 @@
local F = advtrains.formspec
-local players_aspsel = {}
-local function describe_main_aspect(spv)
- if spv == 0 then
- return attrans("Danger (halt)")
- elseif spv == -1 then
- return attrans("Continue at maximum speed")
- elseif not spv then
- return attrans("Continue with current speed limit")
+function advtrains.interlocking.show_signal_form(pos, node, pname, aux_key)
+ local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
+ if sigd and not aux_key then
+ advtrains.interlocking.show_signalling_form(sigd, pname)
else
- return attrans("Continue with the speed limit of @1", tostring(spv))
- end
-end
-
-local function describe_shunt_aspect(shunt)
- if shunt then
- return attrans("Shunting allowed")
- else
- return attrans("No shunting")
+ if advtrains.interlocking.signal.get_signal_cap_level(pos) >= 2 then
+ advtrains.interlocking.show_ip_sa_form(pos, pname)
+ else
+ advtrains.interlocking.show_ip_form(pos, pname)
+ end
end
end
-local function describe_distant_aspect(spv)
- if spv == 0 then
- return attrans("Expect to stop at the next signal")
- elseif spv == -1 then
- return attrans("Expect to continue at maximum speed")
- elseif not spv then
- return attrans("No distant signal information")
- else
- return attrans("Expect to continue with a speed limit of @1", tostring(spv))
- end
+local players_assign_ip = {}
+local players_assign_distant = {}
+
+local function ipmarker(ipos, connid)
+ local node_ok, conns, rhe = advtrains.get_rail_info_at(ipos, advtrains.all_tracktypes)
+ if not node_ok then return end
+ local yaw = advtrains.dir_to_angle(conns[connid].c)
+
+ -- using tcbmarker here
+ local obj = minetest.add_entity(vector.add(ipos, {x=0, y=0.2, z=0}), "advtrains_interlocking:tcbmarker")
+ if not obj then return end
+ obj:set_yaw(yaw)
+ obj:set_properties({
+ textures = { "at_il_signal_ip.png" },
+ })
end
-advtrains.interlocking.describe_main_aspect = describe_main_aspect
-advtrains.interlocking.describe_shunt_aspect = describe_shunt_aspect
-advtrains.interlocking.describe_distant_aspect = describe_distant_aspect
-
-local function dsel(p, q, x, y)
- if p == nil then
- if q then
- return x
- else
- return y
- end
- elseif p then
- return x
+function advtrains.interlocking.make_ip_formspec_component(pos, x, y, w)
+ advtrains.interlocking.db.check_for_duplicate_ip(pos)
+ local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos)
+ if pts then
+ -- display marker
+ local ipos = minetest.string_to_pos(pts)
+ ipmarker(ipos, connid)
+ return table.concat {
+ F.S_label(x, y, "Influence point is set at @1.", string.format("%s/%s", pts, connid)),
+ F.S_button_exit(x, y+0.5, w/2-0.125, "ip_set", "Modify"),
+ F.S_button_exit(x+w/2+0.125, y+0.5, w/2-0.125, "ip_clear", "Clear"),
+ }
else
- return y
+ return table.concat {
+ F.S_label(x, y, "Influence point is not set."),
+ F.S_button_exit(x, y+0.5, w, "ip_set", "Set influence point"),
+ }
end
end
-local function describe_supported_aspects(suppasp, isasp)
- local t = {}
-
- local entries = {attrans("Use default value")}
- local selid = 0
- local mainasps = suppasp.main
- if type(mainasps) ~= "table" then
- mainasps = {mainasps}
- end
- for idx, spv in ipairs(mainasps) do
- if isasp and spv == rawget(isasp, "main") then
- selid = idx
- end
- entries[idx+1] = describe_main_aspect(spv)
- end
- t.main = entries
- t.main_current = selid+1
- t.main_string = tostring(isasp.main)
- if t.main == nil then
- t.main_string = ""
+-- shows small formspec to set the signal influence point
+-- only_notset: show only if it is not set yet (used by signal tcb assignment)
+function advtrains.interlocking.show_ip_form(pos, pname, only_notset)
+ if not minetest.check_player_privs(pname, "interlocking") then
+ return
end
-
- t.shunt = {
- attrans("No shunting"),
- attrans("Shunting allowed"),
- attrans("Proceed as main"),
+ local ipform = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7)
+ local form = {
+ "formspec_version[4]",
+ "size[8,2.25]",
+ ipform,
}
-
- t.shunt_current = dsel(suppasp.shunt, isasp.shunt, 2, 1)
- if dsel(suppasp.proceed_as_main, isasp.proceed_as_main, t.shunt_current == 1) then
- t.shunt_current = 3
- end
- t.shunt_const = suppasp.shunt ~= nil
-
- if suppasp.group then
- local gdef = advtrains.interlocking.aspect.get_group_definition(suppasp.group)
- if gdef then
- t.group = suppasp.group
- t.groupdef = gdef
- local entries = {}
- local selid = 1
- for idx, name in ipairs(suppasp.name or {}) do
- entries[idx] = gdef.aspects[name].label
- if suppasp.group == isasp.group and name == isasp.name then
- selid = idx
- end
- end
- t.name = entries
- t.name_current = selid
- end
+ if not only_notset or not pts then
+ minetest.show_formspec(pname, "at_il_ipsaform_"..minetest.pos_to_string(pos), table.concat(form))
end
-
- return t
end
-advtrains.interlocking.describe_supported_aspects = describe_supported_aspects
-
-local function make_signal_aspect_selector(suppasp, purpose, isasp)
- local t = describe_supported_aspects(suppasp, isasp)
- local formmode = 1
-
- local pos
- if type(purpose) == "table" then
- formmode = 2
- pos = purpose.pos
+-- shows larger formspec to set the signal influence point, main aspect and distant signal pos
+-- only_notset: show only if it is not set yet (used by signal tcb assignment)
+function advtrains.interlocking.show_ip_sa_form(pos, pname)
+ if not minetest.check_player_privs(pname, "interlocking") then
+ return
end
-
+ local ipform = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7)
+ local ma, rpos = advtrains.interlocking.signal.get_aspect(pos)
+ local saform = F.S_button_exit(0, 2, 4, "sa_dst_assign", rpos and minetest.pos_to_string(rpos) or "<distant signal>")
+ .. F.S_button_exit(0, 3, 2, "sa_tmp_mainfree", "Main to free") .. F.S_button_exit(2, 3, 2, "sa_tmp_mainhalt", "Main to halt")
local form = {
"formspec_version[4]",
- string.format("size[8,%f]", ({5.75, 10.75})[formmode]),
- F.S_label(0.5, 0.5, "Select signal aspect"),
+ "size[8,4]",
+ ipform,
+ saform,
}
- local h0 = ({0, 1.5})[formmode]
- form[#form+1] = F.S_label(0.5, 1.5+h0, "Main aspect")
- form[#form+1] = F.S_label(0.5, 3+h0, "Shunt aspect")
- form[#form+1] = F.S_button_exit(0.5, 4.5+h0, 7, "asp_save", "Save signal aspect")
- if formmode == 1 then
- form[#form+1] = F.label(0.5, 1, purpose)
- form[#form+1] = F.field(0.5, 2, 7, "asp_mainval", "", t.main_string)
- elseif formmode == 2 then
- if t.group then
- form[#form+1] = F.S_label(0.5, 1.5, "Signal aspect group: @1", t.groupdef.label)
- form[#form+1] = F.dropdown(0.5, 2, 7, "asp_namesel", t.name, t.name_current, true)
- else
- form[#form+1] = F.S_label(0.5, 1.5, "This signal does not belong to a signal aspect group.")
- form[#form+1] = F.S_label(0.5, 2, "You can not use a predefined signal aspect.")
- end
- form[#form+1] = F.S_label(0.5, 1, "Signal at @1", minetest.pos_to_string(pos))
- form[#form+1] = F.dropdown(0.5, 3.5, 7, "asp_mainsel", t.main, t.main_current, true)
- form[#form+1] = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 7, 7)
- form[#form+1] = advtrains.interlocking.make_short_dst_formspec_component(pos, 0.5, 8.5, 7)
- end
+ minetest.show_formspec(pname, "at_il_ipsaform_"..minetest.pos_to_string(pos), table.concat(form))
+end
- if formmode == 2 and t.shunt_const then
- form[#form+1] = F.label(0.5, 3.5+h0, t.shunt[t.shunt_current])
- form[#form+1] = F.S_label(0.5, 4+h0, "The shunt aspect cannot be changed.")
- else
- form[#form+1] = F.dropdown(0.5, 3.5+h0, 7, "asp_shunt", t.shunt, t.shunt_current, true)
+function advtrains.interlocking.handle_ip_sa_formspec_fields(pname, pos, fields)
+ if not (pos and minetest.check_player_privs(pname, {train_operator=true, interlocking=true})) then
+ return
+ end
+ if fields.ip_set then
+ advtrains.interlocking.init_ip_assign(pos, pname)
+ elseif fields.ip_clear then
+ advtrains.interlocking.db.clear_ip_by_signalpos(pos)
+ elseif fields.sa_dst_assign then
+ advtrains.interlocking.init_distant_assign(pos, pname)
+ elseif fields.sa_tmp_mainfree then
+ local ma, rpos = advtrains.interlocking.signal.get_aspect(pos)
+ advtrains.interlocking.signal.set_aspect(pos, "_free", -1, rpos)
+ elseif fields.sa_tmp_mainhalt then
+ local ma, rpos = advtrains.interlocking.signal.get_aspect(pos)
+ advtrains.interlocking.signal.set_aspect(pos, nil, nil, rpos)
end
-
- return table.concat(form)
end
-function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_purpose, callback, isasp)
- local suppasp = p_suppasp or {
- main = {0, -1},
- dst = {false},
- shunt = false,
- info = {},
- }
- local purpose = p_purpose or ""
+minetest.register_on_player_receive_fields(function(player, formname, fields)
+ local pname = player:get_player_name()
+ local pts = string.match(formname, "^at_il_ipsaform_([^_]+)$")
local pos
- if type(p_purpose) == "table" then
- pos = p_purpose
- purpose = {pname = pname, pos = pos}
+ if pts then
+ pos = minetest.string_to_pos(pts)
end
-
- local form = make_signal_aspect_selector(suppasp, purpose, isasp)
- if not form then
- return
+ if pos then
+ advtrains.interlocking.handle_ip_sa_formspec_fields(pname, pos, fields)
end
+end)
- local token = advtrains.random_id()
- minetest.show_formspec(pname, "at_il_sigaspdia_"..token, form)
- minetest.after(0, function()
- players_aspsel[pname] = {
- purpose = purpose,
- suppasp = suppasp,
- callback = callback,
- token = token,
- }
- end)
-end
-
-local function usebool(sup, val, free)
- if sup == nil then
- return val == free
- else
- return sup
+-- inits the signal IP assignment process
+function advtrains.interlocking.init_ip_assign(pos, pname)
+ if not minetest.check_player_privs(pname, "interlocking") then
+ minetest.chat_send_player(pname, "Insufficient privileges to use this!")
+ return
end
+ --remove old IP
+ --advtrains.interlocking.db.clear_ip_by_signalpos(pos)
+ minetest.chat_send_player(pname, "Configuring Signal: Please look in train's driving direction and punch rail to set influence point.")
+
+ players_assign_ip[pname] = pos
end
-local function get_aspect_from_formspec(suppasp, fields, psl)
- local namei, group, name = tonumber(fields.asp_namesel), suppasp.group, nil
- local gdef = advtrains.interlocking.aspect.get_group_definition(group)
- if gdef then
- local names = suppasp.name or {}
- name = names[namei] or names[names]
- else
- group = nil
- end
- local maini = tonumber(fields.asp_mainsel)
- local main = (suppasp.main or {})[(maini or 0)-1]
- if not maini then
- local mainval = fields.asp_mainval
- if mainval == "-1" then
- main = -1
- elseif mainval == "x" then
- main = false
- elseif string.match(mainval, "^%d+$") then
- main = tonumber(mainval)
- else
- main = nil
- end
- elseif maini <= 1 then
- main = nil
- end
- local shunti = tonumber(fields.asp_shunt)
- local shunt = suppasp.shunt
- if shunt == nil then
- shunt = shunti == 2
- end
- local proceed_as_main = suppasp.proceed_as_main
- if proceed_as_main == nil then
- proceed_as_main = shunti == 3
+-- inits the distant signal assignment process
+function advtrains.interlocking.init_distant_assign(pos, pname)
+ if not minetest.check_player_privs(pname, "interlocking") then
+ minetest.chat_send_player(pname, "Insufficient privileges to use this!")
+ return
end
- return advtrains.interlocking.aspect {
- main = main,
- shunt = shunt,
- proceed_as_main = proceed_as_main,
- info = {},
- name = name,
- group = group,
- }
+ minetest.chat_send_player(pname, "Set distant signal: Punch the main signal to assign!")
+
+ players_assign_distant[pname] = pos
end
-minetest.register_on_player_receive_fields(function(player, formname, fields)
+minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
local pname = player:get_player_name()
- local psl = players_aspsel[pname]
- if psl then
- if formname == "at_il_sigaspdia_"..psl.token then
- local suppasp = psl.suppasp
- if fields.asp_save then
- local asp
- asp = get_aspect_from_formspec(suppasp, fields, psl)
- if asp then
- psl.callback(pname, asp)
+ if not minetest.check_player_privs(pname, "interlocking") then
+ return
+ end
+ -- IP assignment
+ local signalpos = players_assign_ip[pname]
+ if signalpos then
+ if vector.distance(pos, signalpos)<=50 then
+ local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
+ if node_ok and #conns == 2 then
+
+ local yaw = player:get_look_horizontal()
+ local plconnid = advtrains.yawToClosestConn(yaw, conns)
+
+ -- add assignment if not already present.
+ local pts = advtrains.roundfloorpts(pos)
+ if not advtrains.interlocking.db.get_ip_signal_asp(pts, plconnid) then
+ advtrains.interlocking.db.set_ip_signal(pts, plconnid, signalpos)
+ ipmarker(pos, plconnid)
+ minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point")
+ else
+ minetest.chat_send_player(pname, "Configuring Signal: Influence point of another signal is already present!")
end
- end
- if type(psl.purpose) == "table" then
- local pos = psl.purpose.pos
- advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields)
- advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields)
+ else
+ minetest.chat_send_player(pname, "Configuring Signal: This is not a normal two-connection rail! Aborted.")
end
else
- players_aspsel[pname] = nil
+ minetest.chat_send_player(pname, "Configuring Signal: Node is too far away. Aborted.")
end
+ players_assign_ip[pname] = nil
+ end
+ -- DST assignment
+ signalpos = players_assign_distant[pname]
+ if signalpos then
+ -- get current mainaspect
+ local ma, rpos = advtrains.interlocking.signal.get_aspect(signalpos)
+ -- if punched pos is valid signal then set it as the new remote, otherwise nil
+ local nrpos
+ if advtrains.interlocking.signal.get_signal_cap_level(pos) > 1 then nrpos = pos end
+ advtrains.interlocking.signal.set_aspect(signalpos, ma.name, ma.speed, nrpos)
+ players_assign_distant[pname] = nil
end
end)
+
diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua
index bfec648..caf22e3 100755
--- a/advtrains_interlocking/tcb_ts_ui.lua
+++ b/advtrains_interlocking/tcb_ts_ui.lua
@@ -186,7 +186,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
local is_signal = minetest.get_item_group(node.name, "advtrains_signal") >= 2
if is_signal then
local ndef = minetest.registered_nodes[node.name]
- if ndef and ndef.advtrains and ndef.advtrains.set_aspect then
+ if ndef and ndef.advtrains and ndef.advtrains.apply_aspect then
local tcbs = ildb.get_tcbs(sigd)
if tcbs then
tcbs.signal = pos
@@ -464,7 +464,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
ts.route = nil
for _, sigd in ipairs(ts.tc_breaks) do
local tcbs = ildb.get_tcbs(sigd)
- advtrains.interlocking.update_signal_aspect(tcbs)
+ advtrains.interlocking.signal.update_route_aspect(tcbs)
end
minetest.chat_send_player(pname, "Reset track section "..ts_id.."!")
end
@@ -642,7 +642,6 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle
form = form.."button[0.5,8;2.5,1;newroute;New Route]"
form = form.."button[ 3,8;2.5,1;unassign;Unassign Signal]"
form = form..string.format("checkbox[0.5,8.75;ars;Automatic routesetting;%s]", not tcbs.ars_disabled)
- form = form..string.format("checkbox[0.5,9.25;dst;Distant signalling;%s]", not tcbs.nodst)
end
elseif sigd_equal(tcbs.route_origin, sigd) then
-- something has gone wrong: tcbs.routeset should have been set...
@@ -660,7 +659,7 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle
-- always a good idea to update the signal aspect
if not called_from_form_update then
-- FIX prevent a callback loop
- advtrains.interlocking.update_signal_aspect(tcbs)
+ advtrains.interlocking.signal.update_route_aspect(tcbs)
end
end
@@ -769,10 +768,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.ars then
tcbs.ars_disabled = not minetest.is_yes(fields.ars)
end
-
- if fields.dst then
- tcbs.nodst = not minetest.is_yes(fields.dst)
- end
if fields.auto then
tcbs.route_auto = true