aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking
diff options
context:
space:
mode:
authorY. Wang <yw05@forksworld.de>2022-04-11 16:55:50 +0200
committerY. Wang <yw05@forksworld.de>2023-03-23 20:06:02 +0100
commit5c8962b39bd4f6871ec87a988ac43d7bfad04d2b (patch)
treeb30a30fdfa905ef5eabce1528829fa41a8c127a3 /advtrains_interlocking
parent0b4cdbb455b5ba43b3c8d3fd7af5112021144eaa (diff)
downloadadvtrains-5c8962b39bd4f6871ec87a988ac43d7bfad04d2b.tar.gz
advtrains-5c8962b39bd4f6871ec87a988ac43d7bfad04d2b.tar.bz2
advtrains-5c8962b39bd4f6871ec87a988ac43d7bfad04d2b.zip
Implement basic route signaling with Japanese signals for demo
Diffstat (limited to 'advtrains_interlocking')
-rw-r--r--advtrains_interlocking/init.lua3
-rw-r--r--advtrains_interlocking/signal_api.lua146
-rw-r--r--advtrains_interlocking/signal_aspect_ui.lua165
-rw-r--r--advtrains_interlocking/signal_aspects.lua123
4 files changed, 308 insertions, 129 deletions
diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua
index a2f5882..d0b75a8 100644
--- a/advtrains_interlocking/init.lua
+++ b/advtrains_interlocking/init.lua
@@ -12,8 +12,11 @@ end
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
+advtrains.interlocking.aspects = dofile(modpath.."signal_aspects.lua")
+
dofile(modpath.."database.lua")
dofile(modpath.."signal_api.lua")
+dofile(modpath.."signal_aspect_ui.lua")
dofile(modpath.."demosignals.lua")
dofile(modpath.."train_sections.lua")
dofile(modpath.."route_prog.lua")
diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua
index 83fae4a..0a9e6ea 100644
--- a/advtrains_interlocking/signal_api.lua
+++ b/advtrains_interlocking/signal_api.lua
@@ -220,8 +220,17 @@ function advtrains.interlocking.signal_set_aspect(pos, asp)
local node=advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
if ndef and ndef.advtrains and ndef.advtrains.set_aspect then
+ local oldasp = advtrains.interlocking.signal_get_aspect(pos) or DANGER
+ local suppasp = advtrains.interlocking.signal_get_supported_aspects(pos) or {}
+ if suppasp.type == 2 then
+ asp = advtrains.interlocking.aspects.type1_to_type2main(asp, suppasp.group)
+ end
ndef.advtrains.set_aspect(pos, node, asp)
- advtrains.interlocking.signal_on_aspect_changed(pos)
+ local actualasp = advtrains.interlocking.signal_get_aspect(pos) or DANGER
+ local aspect_changed = advtrains.interlocking.aspects.not_equalp(oldasp, actualasp)
+ if aspect_changed then
+ advtrains.interlocking.signal_on_aspect_changed(pos)
+ end
end
end
@@ -252,7 +261,7 @@ function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack,
local function callback(pname, aspect)
advtrains.interlocking.signal_set_aspect(pos, aspect)
end
- local isasp = ndef.advtrains.get_aspect(pos, node)
+ local isasp = advtrains.interlocking.signal_get_aspect(pos, node)
advtrains.interlocking.show_signal_aspect_selector(
pname,
@@ -285,8 +294,13 @@ function advtrains.interlocking.signal_get_aspect(pos)
local ndef=minetest.registered_nodes[node.name]
if ndef and ndef.advtrains and ndef.advtrains.get_aspect then
local asp = ndef.advtrains.get_aspect(pos, node)
+ local suppasp = advtrains.interlocking.signal_get_supported_aspects(pos) or {}
+ if suppasp.type == 2 then
+ asp = advtrains.interlocking.aspects.type2main_to_type1(suppasp.group, asp)
+ end
if not asp then asp = DANGER end
- return convert_aspect_if_necessary(asp)
+ asp = convert_aspect_if_necessary(asp)
+ return asp
end
return nil
end
@@ -411,129 +425,3 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
players_assign_ip[pname] = nil
end
end)
-
-
---== aspect selector ==--
-
-local players_aspsel = {}
-
---[[
-suppasp: "supported_aspects" table
-purpose: form title string
-callback: func(pname, aspect) called on form submit
-isasp: aspect currently set
-]]
-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 ""
-
- local form = "size[7,7]label[0.5,0.5;Select Signal Aspect:]"
- form = form.."label[0.5,1;"..purpose.."]"
-
- form = form.."label[0.5,1.5;== Main Signal ==]"
- local selid = 1
- local entries = {}
- for idx, spv in ipairs(suppasp.main) do
- local entry
- if spv == 0 then
- entry = "Halt"
- elseif spv == -1 then
- entry = "Continue at maximum speed"
- elseif not spv then
- entry = "Continue\\, speed limit unchanged (no info)"
- else
- entry = "Continue at speed of "..spv
- end
- -- hack: the crappy formspec system returns the label, not the index. save the index in it.
- entries[idx] = idx.."| "..entry
- if isasp and spv == (isasp.main or false) then
- selid = idx
- end
- end
- form = form.."dropdown[0.5,2;6;main;"..table.concat(entries, ",")..";"..selid.."]"
-
-
- form = form.."label[0.5,3;== Shunting ==]"
- if suppasp.shunt == nil then
- local st = 1
- if isasp and isasp.shunt then st=2 end
- form = form.."dropdown[0.5,3.5;6;shunt_free;---,allowed;"..st.."]"
- end
-
- form = form.."label[0.5,4.5;== Distant Signal ==]"
- local selid = 1
- local entries = {}
- for idx, spv in ipairs(suppasp.dst) do
- local entry
- if spv == 0 then
- entry = "Expect to stop at the next signal"
- elseif spv == -1 then
- entry = "Expect to pass the next signal at maximum speed"
- elseif not spv then
- entry = "No info"
- else
- entry = string.format("Expect to pass the next signal at speed of %d", spv)
- end
- entries[idx] = idx.."| "..entry
- if isasp and spv == (isasp.dst or false) then
- selid = idx
- end
- end
- form = form.."dropdown[0.5,5;6;dst;"..table.concat(entries, ",")..";"..selid.."]"
-
- form = form.."button_exit[0.5,6;5,1;save;Save signal aspect]"
-
- local token = advtrains.random_id()
-
- minetest.show_formspec(pname, "at_il_sigaspdia_"..token, form)
-
- minetest.after(1, function()
- players_aspsel[pname] = {
- suppasp = suppasp,
- callback = callback,
- token = token,
- }
- end)
-end
-
-local function usebool(sup, val, free)
- if sup == nil then
- return val==free
- else
- return sup
- end
-end
-
--- other side of hack: extract the index
-local function ddindex(val)
- return tonumber(string.match(val, "^(%d+)|"))
-end
-
--- TODO use non-hacky way to parse outputs
-
-minetest.register_on_player_receive_fields(function(player, formname, fields)
- local pname = player:get_player_name()
- local psl = players_aspsel[pname]
- if psl then
- if formname == "at_il_sigaspdia_"..psl.token then
- if fields.save then
- local maini = ddindex(fields.main)
- if not maini then return end
- local dsti = ddindex(fields.dst)
- if not dsti then return end
- local asp = {
- main = psl.suppasp.main[maini],
- dst = psl.suppasp.dst[dsti],
- shunt = usebool(psl.suppasp.shunt, fields.shunt_free, "allowed"),
- info = {}
- }
- psl.callback(pname, asp)
- end
- else
- players_aspsel[pname] = nil
- end
- end
-
-end)
diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua
new file mode 100644
index 0000000..edf3ab1
--- /dev/null
+++ b/advtrains_interlocking/signal_aspect_ui.lua
@@ -0,0 +1,165 @@
+local F = advtrains.formspec
+local players_aspsel = {}
+
+local function make_signal_aspect_selector_t1(suppasp, purpose, isasp)
+ local form = {"size[7,7.5]"}
+ form[#form+1] = F.S_label(0.5, 0.5, "Select signal aspect")
+ form[#form+1] = F.label(0.5, 1, purpose)
+
+ form[#form+1] = F.S_label(0.5, 1.5, "Main aspect")
+ local entries = {}
+ local selid = 1
+ for idx, spv in ipairs(suppasp.main) do
+ local entry
+ if isasp and spv == isasp.main then
+ selid = idx
+ end
+ if spv == 0 then
+ entry = attrans("Danger (halt)")
+ elseif spv == -1 then
+ entry = attrans("Continue at maximum speed")
+ elseif not spv then
+ entry = attrans("Continue with current speed limit")
+ else
+ entry = attrans("Continue with the speed limit of @1", spv)
+ end
+ entries[idx] = entry
+ end
+ form[#form+1] = F.dropdown(0.5, 2, 6, "main", entries, selid, true)
+
+ form[#form+1] = F.S_label(0.5, 3, "Shunt aspect")
+ if suppasp.shunt == nil then
+ local st = 1
+ if isasp and isasp.shunt then st = 2 end
+ local entries = {
+ attrans("No shunting"),
+ attrans("Shunting allowed"),
+ }
+ form[#form+1] = F.dropdown(0.5, 3.5, 6, "shunt_free", entries, st, true)
+ end
+
+ form[#form+1] = F.S_label(0.5, 4.5, "Distant aspect")
+ local entries = {}
+ local selid = 1
+ for idx, spv in ipairs(suppasp.dst) do
+ local entry
+ if isasp and spv == isasp.dst then
+ selid = idx
+ end
+ if spv == 0 then
+ entry = attrans("Expect to stop at the next signal")
+ elseif spv == -1 then
+ entry = attrans("Expect to continue at maximum speed")
+ elseif not spv then
+ entry = attrans("No information on distant signal")
+ else
+ entry = attrans("Expect to continue with a speed limit of @1", spv)
+ end
+ entries[idx] = entry
+ end
+ form[#form+1] = F.dropdown(0.5, 5, 6, "dst", entries, selid, true)
+
+ form[#form+1] = F.S_button_exit(0.5, 6, 6, 1, "save", "Save signal aspect")
+ return table.concat(form)
+end
+
+local function make_signal_aspect_selector_t2(suppasp, purpose, isasp)
+ local form = {"size[7,4]"}
+ local def = advtrains.interlocking.aspects.get_type2_definition(suppasp.group)
+ if not def then
+ return nil
+ end
+ form[#form+1] = F.S_label(0.5, 0.5, "Select signal aspect")
+ form[#form+1] = F.label(0.5, 1, purpose)
+
+ local entries = {}
+ local selid = 1
+ for idx, spv in ipairs(def.main) do
+ if isasp and isasp.type2name == spv.name then
+ selid = idx
+ end
+ entries[idx] = spv.label
+ end
+ form[#form+1] = F.dropdown(0.5, 1.5, 6, "asp", entries, selid, true)
+ form[#form+1] = F.S_button_exit(0.5, 2.5, 6, 1, "save", "Save signal aspect")
+ 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 ""
+
+ local form
+ if suppasp.type == 2 then
+ form = make_signal_aspect_selector_t2(suppasp, purpose, isasp)
+ else
+ form = make_signal_aspect_selector_t1(suppasp, purpose, isasp)
+ end
+ if not form then
+ return
+ end
+
+ local token = advtrains.random_id()
+ minetest.show_formspec(pname, "at_il_sigaspdia_"..token, form)
+ minetest.after(1, function()
+ players_aspsel[pname] = {
+ suppasp = suppasp,
+ callback = callback,
+ token = token,
+ }
+ end)
+end
+
+local function usebool(sup, val, free)
+ if sup == nil then
+ return val == free
+ else
+ return sup
+ end
+end
+
+local function get_aspect_from_formspec_t1(suppasp, fields)
+ local maini = tonumber(fields.main)
+ if not maini then return end
+ local dsti = tonumber(fields.dst)
+ if not dsti then return end
+ return {
+ main = suppasp.main[maini],
+ dst = suppasp.dst[dsti],
+ shunt = usebool(suppasp.shunt, fields.shunt_free, "2"),
+ info = {},
+ }
+end
+
+local function get_aspect_from_formspec_t2(suppasp, fields)
+ local asp = advtrains.interlocking.aspects.type2main_to_type1(suppasp.group, tonumber(fields.asp))
+ return asp
+end
+
+minetest.register_on_player_receive_fields(function(player, formname, fields)
+ 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.save then
+ local asp
+ if suppasp.type == 2 then
+ asp = get_aspect_from_formspec_t2(suppasp, fields)
+ else
+ asp = get_aspect_from_formspec_t1(suppasp, fields)
+ end
+ if asp then
+ psl.callback(pname, asp)
+ end
+ end
+ else
+ players_aspsel[pname] = nil
+ end
+ end
+end)
diff --git a/advtrains_interlocking/signal_aspects.lua b/advtrains_interlocking/signal_aspects.lua
new file mode 100644
index 0000000..a70d176
--- /dev/null
+++ b/advtrains_interlocking/signal_aspects.lua
@@ -0,0 +1,123 @@
+type2defs = {}
+
+local function register_type2(def)
+ local t = {type = 2}
+ local name = def.name
+ if type2defs[name] then
+ return error("Name " .. name .. " already used")
+ elseif type(name) ~= "string" then
+ return error("Name is not a string")
+ end
+ t.name = name
+
+ local label = def.label or name
+ if type(label) ~= "string" then
+ return error("Label is not a string")
+ end
+ t.label = label
+
+ local mainasps = {}
+ for idx, asp in ipairs(def.main) do
+ local t = {}
+ local name = asp.name
+ if type(name) ~= "string" then
+ return error("Aspect name is not a string")
+ end
+ t.name = name
+
+ local label = asp.label or name
+ if type(label) ~= "string" then
+ return error("Aspect label is not a string")
+ end
+ t.label = label
+
+ t.main = asp.main
+ mainasps[idx] = t
+ mainasps[name] = idx
+ end
+ t.main = mainasps
+
+ type2defs[name] = t
+end
+
+local function get_type2_definition(name)
+ return type2defs[name]
+end
+
+local function type2main_to_type1(name, asp)
+ local def = type2defs[name]
+ if not def then
+ return nil
+ end
+ local aspidx
+ if type(asp) == "number" then
+ aspidx = asp
+ else
+ aspidx = def.main[asp]
+ end
+ local asptbl = def.main[aspidx]
+ if not asptbl then
+ return nil
+ end
+ if type(asp) == "number" then
+ asp = asptbl.name
+ end
+
+ local t = {
+ main = asptbl.main,
+ type2name = asp,
+ type2group = name,
+ }
+ if aspidx > 1 and aspidx < #asptbl then
+ t.dst = asptbl[aspidx+1].main
+ end
+ return t
+end
+
+local function type1_to_type2main(asp, group)
+ local def = type2defs[group]
+ if not def then
+ return nil
+ end
+ if group == asp.type2group and def.main[asp.type2name] then
+ return asp.type2name
+ end
+ local t_main = def.main
+ local idx
+ if not asp.main or asp.main == -1 then
+ idx = 1
+ elseif asp.main == 0 then
+ idx = #t_main
+ else
+ idx = math.max(#t_main-1, 1)
+ end
+ return t_main[idx].name
+end
+
+local function equalp(asp1, asp2)
+ if asp1 == asp2 then -- same reference
+ return true
+ elseif asp1.type2group and asp1.type2group == asp2.type2group then -- type2 with the same group
+ return asp1.type2name == asp2.type2name
+ else
+ for _, k in pairs {"main", "shunt", "dst"} do
+ if asp1[k] ~= asp2[k] then
+ return false
+ end
+ end
+ end
+ return true
+end
+
+local function not_equalp(asp1, asp2)
+ return not equalp(asp1, asp2)
+end
+
+return {
+ register_type2 = register_type2,
+ get_type2_definition = get_type2_definition,
+ type2main_to_type1 = type2main_to_type1,
+ type1_to_type2main = type1_to_type2main,
+ equalp = equalp,
+ not_equalp = not_equalp,
+}