Add proper UI aspect selection for static dialog

This commit is contained in:
orwell 2024-05-23 20:25:35 +02:00
parent 6fd845baec
commit 9af6e32e64
5 changed files with 111 additions and 61 deletions

View File

@ -212,6 +212,10 @@ function ndb.get_node(pos)
end
return n
end
function ndb.get_ndef(pos)
local n=ndb.get_node_or_nil(pos)
return n and minetest.registered_nodes[n.name]
end
function ndb.get_node_raw(pos)
local cid=ndbget(pos.x, pos.y, pos.z)
if cid then

View File

@ -159,15 +159,8 @@ function ilrs.set_route(signal, route, try)
end
end
for i = #signals, 1, -1 do
if lastsig then
local tcbs = signals[i]
local pos = tcbs.signal
local _, assigned_by = advtrains.distant.get_main(pos)
if (not nodst) and (not assigned_by or assigned_by == "routesetting") then
advtrains.distant.assign(lastsig, pos, "routesetting", true)
end
advtrains.interlocking.signal.update_route_aspect(tcbs, i ~= 1)
end
-- TODO add logic for distant signal assign
advtrains.interlocking.signal.update_route_aspect(signals[i], i ~= 1)
end
return true

View File

@ -5,13 +5,14 @@ local F = advtrains.formspec
local signal = {}
signal.MASP_HALT = {
name = nil,
speed = nil,
name = "_halt",
speed = 0,
halt = true,
remote = nil,
}
signal.MASP_FREE = {
name = "_free",
name = "_default",
speed = -1,
remote = nil,
}
@ -44,9 +45,9 @@ b) the distant signal's aspect group name & aspect table
One concrete combination of lights/shapes that a signal signal shows. Handling these is at the discretion of
the signal mod defining the signal, and they are typically combinations of main aspect and distant aspect
Example:
- A Ks signal has the aspect_group="proceed_12" set for a route
- The signal at the end of the route shows aspect_group="proceed_8", advtrains also passes on that this means {main=8, shunt=false}
- The ndef.advtrains.apply_aspect(pos, asp_group, dst_aspgrp, dst_aspinfo) determines that the signal should now show
- A Ks signal has the main_aspect="proceed_12" set for a route
- The signal at the end of the route shows main_aspect="proceed_8", advtrains also passes on that this means {main=8, shunt=false}
- The ndef.afunction(pos, node, main_aspect, rem_aspect, rem_aspinfo) determines that the signal should now show
blinking green with main indicator 12 and dst indicator 8, and sets the nodes accordingly.
This function can now return the Aspect Info table, which will be cached by advtrains until the aspect changes again
and will be used when a train approaches the signal. If nil is returned, then the aspect will be queried next time
@ -56,10 +57,14 @@ 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)
Apply_aspect may also receive nil as the main aspect. It usually means that the signal is not assigned to anything particular,
Apply_aspect may also receive the special main aspect { name = "_halt", halt = true }. 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".
A special case occurs for pure distant signals: Such signals must set apply_aspect, but must not set main_aspects. Behavior is as follows:
- Signal is uninitialized, distant signal is not assigned to a main signal, or no route is set: main_aspect == { name = "_halt", halt = true } and rem_aspect == nil
- A remote main signal is assigned (either by user or by route): main_aspect is always { name = "_default" } and rem_aspect / rem_aspinfo give the correct information
Main aspect names starting with underscore (e.g. "_default") are reserved and must not be used!
== Aspect Info ==
@ -90,6 +95,7 @@ ndef.advtrains = {
apply_aspect = function(pos, node, main_aspect, rem_aspect, rem_aspinfo)
-- set the node to show the desired aspect
-- called by advtrains when this signal's aspect group or the remote signal's aspect changes
-- main_aspect is never nil, but can be one of the special aspects { name = "_halt", halt = true } or { name = "_default" }
-- MAY return the aspect_info. If it returns nil then get_aspect_info will be queried at a later point.
get_aspect_info(pos, main_aspect)
-- Returns the aspect info table (main, shunt, dst etc.)
@ -268,36 +274,41 @@ end
function signal.get_aspect_internal(pos, aspt)
if not aspt then
-- oh, no main aspect, nevermind
return nil, nil, nil
return signal.MASP_HALT, nil, nil
end
atdebug("get_aspect_internal",pos,aspt)
-- look aspect in nodedef
local node = advtrains.ndb.get_node_or_nil(pos)
local ndef = node and minetest.registered_nodes[node.name]
local ndefat = ndef and ndef.advtrains
-- only if signal defines main aspect and its set in aspt
if ndefat and ndefat.main_aspects and aspt.name then
if not ndefat.main_aspects_lookup then
cache_mainaspects(ndefat)
if ndefat and ndefat.apply_aspect then
-- only if signal defines main aspect and its set in aspt
if ndefat.main_aspects and aspt.name then
if not ndefat.main_aspects_lookup then
cache_mainaspects(ndefat)
end
local masp = ndefat.main_aspects_lookup[aspt.name]
-- special handling for the default free aspect ("_default")
if aspt.name == "_default" 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 signal.MASP_HALT, aspt.remote, node, ndef
end
-- if speed, then apply speed
if masp.speed and aspt.speed then
masp = table.copy(masp)
masp.speed = aspt.speed
end
return masp, aspt.remote, node, ndef
elseif aspt.name then
-- Distant-only signal, still supports kind of default aspect
return { name = aspt.name, speed = aspt.speed }, aspt.remote, node, ndef
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
end
-- if speed, then apply speed
if masp.speed and aspt.speed then
masp = table.copy(masp)
masp.speed = aspt.speed
end
return masp, aspt.remote, node, ndef
end
-- invalid node or no main aspect, return nil for masp
return nil, aspt.remote, node, ndef
-- invalid node or no main aspect, return default halt aspect for masp
return signal.MASP_HALT, aspt.remote, node, ndef
end
-- For the signal at pos, get the "aspect info" table. This contains the speed signalling information at this location
@ -327,7 +338,6 @@ function signal.reapply_aspect(pts)
end
-- resolve mainaspect table by name
local pos = advtrains.decode_pos(pts)
-- note: masp may be nil, when aspt.name was nil. Valid case for distant-only signals
local masp, remote, node, ndef = signal.get_aspect_internal(pos, aspt)
-- if we have remote, resolve remote
local rem_masp, rem_aspi

View File

@ -75,14 +75,34 @@ function advtrains.interlocking.show_ip_sa_form(pos, pname)
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]",
"size[8,4]",
"size[8,4.5]",
ipform,
saform,
}
-- Create Signal aspect formspec elements
local ndef = advtrains.ndb.get_ndef(pos)
if ndef and ndef.advtrains then
-- main aspect list
if ndef.advtrains.main_aspects then
local entries = { "<none>" }
local sel = 1
for i, mae in ipairs(ndef.advtrains.main_aspects) do
entries[i+1] = mae.description
if ma and ma.name == mae.name then
sel = i+1
end
end
form[#form+1] = F.dropdown(0.5, 2.5, 4, "sa_mainaspect", entries, sel, true)
end
-- distant signal assign (is shown either when main_aspect is not none, or when pure distant signal)
if rpos then
form[#form+1] = F.button_exit(0.5, 3.5, 4, "sa_undistant", "Dst: " .. minetest.pos_to_string(rpos))
elseif (ma and not ma.halt) or not ndef.advtrains.main_aspects then
form[#form+1] = F.button_exit(0.5, 3.5, 4, "sa_distant", "<assign distant>")
end
end
minetest.show_formspec(pname, "at_il_ipsaform_"..minetest.pos_to_string(pos), table.concat(form))
end
@ -90,18 +110,42 @@ 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
local ma, rpos = advtrains.interlocking.signal.get_aspect(pos)
-- mainaspect dropdown
if fields.sa_mainaspect then
local idx = tonumber(fields.sa_mainaspect)
local new_ma = nil
if idx > 1 then
local ndef = advtrains.ndb.get_ndef(pos)
if ndef and ndef.advtrains and ndef.advtrains.main_aspects then
new_ma = ndef.advtrains.main_aspects[idx - 1]
end
end
if new_ma and (new_ma.name ~= ma.name or new_ma.speed ~= ma.speed) then
advtrains.interlocking.signal.set_aspect(pos, new_ma.name, new_ma.speed, rpos)
elseif not new_ma then
-- reset everything
advtrains.interlocking.signal.set_aspect(pos, nil, nil, nil)
end
end
-- buttons
if fields.ip_set then
advtrains.interlocking.init_ip_assign(pos, pname)
return
elseif fields.ip_clear then
advtrains.interlocking.db.clear_ip_by_signalpos(pos)
elseif fields.sa_dst_assign then
return
elseif fields.sa_distant 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)
return
elseif fields.sa_undistant then
advtrains.interlocking.signal.set_aspect(pos, ma.name, ma.speed, nil)
return
end
-- show the form again unless one of the buttons was clicked
if not fields.quit then
advtrains.interlocking.show_ip_sa_form(pos, pname)
end
end
@ -180,8 +224,13 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
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)
if advtrains.interlocking.signal.get_signal_cap_level(pos) > 1 then
nrpos = pos
if not ma then -- make sure that dst is never set without a main aspect (esp. for pure distant signal case)
ma = { name = "_default" }
end
advtrains.interlocking.signal.set_aspect(signalpos, ma.name, ma.speed, nrpos)
end
players_assign_distant[pname] = nil
end
end)

View File

@ -50,7 +50,7 @@ end
local applyaspectf_main = function(rot)
return function(pos, node, main_aspect, dst_aspect, dst_aspect_info)
if not main_aspect then
if main_aspect.halt then
-- halt aspect, set red and don't do anything further
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_danger_"..rot, param2 = node.param2})
setzs3v(pos, nil, rot)
@ -119,20 +119,14 @@ local mainaspects_main = {
description = "Proceed (speed 4)",
zs3 = "4",
},
{
name = "halt",
description = "Halt",
zs3 = "off",
halt = true,
},
}
--Rangiersignal
local applyaspectf_ra = function(rot)
-- we get here the full main_aspect table
return function(pos, node, main_aspect, dst_aspect, dst_aspect_info)
if main_aspect then
-- any main aspect is fine, there's only one anyway
if not main_aspect.halt then
-- any non-halt main aspect is fine, there's only one anyway
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_shuntd_"..rot, param2 = node.param2})
else
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_danger_"..rot, param2 = node.param2})