From 5c8962b39bd4f6871ec87a988ac43d7bfad04d2b Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Mon, 11 Apr 2022 16:55:50 +0200 Subject: Implement basic route signaling with Japanese signals for demo --- advtrains/formspec.lua | 37 +++++++++++++++++++++++++++++++++++++ advtrains/init.lua | 4 ++++ 2 files changed, 41 insertions(+) create mode 100644 advtrains/formspec.lua (limited to 'advtrains') diff --git a/advtrains/formspec.lua b/advtrains/formspec.lua new file mode 100644 index 0000000..91e300d --- /dev/null +++ b/advtrains/formspec.lua @@ -0,0 +1,37 @@ +local sformat = string.format +local fsescape = minetest.formspec_escape + +local function f_button_exit(x, y, w, h, id, text) + return sformat("button_exit[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) +end + +local function S_button_exit(x, y, w, h, id, ...) + return f_button_exit(x, y, w, h, id, attrans(...)) +end + +local function f_dropdown(x, y, w, id, entries, sel, indexed) + local t = {} + for k, v in pairs(entries) do + t[k] = fsescape(v) + end + return sformat("dropdown[%f,%f;%f;%s;%s;%d%s]", + x, y, w, id, table.concat(t, ","), + sel or 1, + indexed and ";true" or "") +end + +local function f_label(x, y, text) + return sformat("label[%f,%f;%s]", x, y, fsescape(text)) +end + +local function S_label(x, y, ...) + return f_label(x, y, attrans(...)) +end + +return { + button_exit = f_button_exit, + S_button_exit = S_button_exit, + dropdown = f_dropdown, + label = f_label, + S_label = S_label, +} diff --git a/advtrains/init.lua b/advtrains/init.lua index a7e5764..1cba255 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -24,6 +24,9 @@ minetest.log("action", "[advtrains] Loading...") -- There is no need to support 0.4.x anymore given that the compatitability with it is already broken by 1bb1d825f46af3562554c12fba35a31b9f7973ff attrans = minetest.get_translator ("advtrains") +function attrans_formspec(...) + return minetest.formspec_escape(attrans(...)) +end --advtrains advtrains = {trains={}, player_to_train_mapping={}} @@ -199,6 +202,7 @@ advtrains.meseconrules = advtrains.fpath=minetest.get_worldpath().."/advtrains" advtrains.speed = dofile(advtrains.modpath.."/speed.lua") +advtrains.formspec = dofile(advtrains.modpath.."/formspec.lua") dofile(advtrains.modpath.."/path.lua") dofile(advtrains.modpath.."/trainlogic.lua") -- cgit v1.2.3 From d1a0d8f2654d6ee64c1a43de7958b1eadfaff6b0 Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Fri, 10 Jun 2022 22:21:54 +0200 Subject: Use tabs to switch between signaling and IP forms --- .gitignore | 1 + advtrains/formspec.lua | 36 +++++- advtrains_interlocking/signal_api.lua | 11 +- advtrains_interlocking/signal_aspect_ui.lua | 165 ++++++++++++++++++------ advtrains_interlocking/signal_aspects.lua | 2 +- advtrains_interlocking/signal_main_ui.lua | 0 advtrains_interlocking/spec/signal_api_spec.lua | 3 +- advtrains_interlocking/tcb_ts_ui.lua | 25 ++-- 8 files changed, 176 insertions(+), 67 deletions(-) create mode 100644 advtrains_interlocking/signal_main_ui.lua (limited to 'advtrains') diff --git a/.gitignore b/.gitignore index b3180de..bef77f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ ## Eclipse project files & directories .project .settings +luacov.* diff --git a/advtrains/formspec.lua b/advtrains/formspec.lua index 91e300d..aa5aa69 100644 --- a/advtrains/formspec.lua +++ b/advtrains/formspec.lua @@ -1,6 +1,14 @@ local sformat = string.format local fsescape = minetest.formspec_escape +local function make_list(entries) + local t = {} + for k, v in ipairs(entries) do + t[k] = fsescape(v) + end + return table.concat(t, ",") +end + local function f_button_exit(x, y, w, h, id, text) return sformat("button_exit[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) end @@ -10,12 +18,8 @@ local function S_button_exit(x, y, w, h, id, ...) end local function f_dropdown(x, y, w, id, entries, sel, indexed) - local t = {} - for k, v in pairs(entries) do - t[k] = fsescape(v) - end return sformat("dropdown[%f,%f;%f;%s;%s;%d%s]", - x, y, w, id, table.concat(t, ","), + x, y, w, id, make_list(entries), sel or 1, indexed and ";true" or "") end @@ -28,10 +32,32 @@ local function S_label(x, y, ...) return f_label(x, y, attrans(...)) end +local function f_tabheader(x, y, w, h, id, entries, sel, transparent, border) + local st = {string.format("%f,%f",x, y)} + if h then + if w then + st[#st+1] = string.format("%f,%f", w, h) + else + st[#st+1] = tostring(h) + end + end + st[#st+1] = tostring(id) + st[#st+1] = make_list(entries) + st[#st+1] = tostring(sel) + if transparent ~= nil then + st[#st+1] = tostring(transparent) + if border ~= nil then + st[#st+1] = tostring(border) + end + end + return string.format("tabheader[%s]", table.concat(st, ";")) +end + return { button_exit = f_button_exit, S_button_exit = S_button_exit, dropdown = f_dropdown, label = f_label, S_label = S_label, + tabheader = f_tabheader, } diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index a25e1f6..5b3baf8 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -272,7 +272,10 @@ function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack, 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) @@ -288,7 +291,7 @@ function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack, advtrains.interlocking.show_signal_aspect_selector( pname, ndef.advtrains.supported_aspects, - "Set aspect manually", callback, + pos, callback, isasp) else --static signal - only IP @@ -332,7 +335,7 @@ function advtrains.interlocking.signal_get_aspect(pos) local asp = get_supposed_aspect(pos) if not asp then asp = advtrains.interlocking.signal_get_real_aspect(pos) - set_supposed_aspect(pos) + set_supposed_aspect(pos, asp) end return asp end @@ -372,6 +375,7 @@ function advtrains.interlocking.show_ip_form(pos, pname, only_notset) return end local form = "size[7,5]label[0.5,0.5;Signal at "..minetest.pos_to_string(pos).."]" + form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 2) advtrains.interlocking.db.check_for_duplicate_ip(pos) local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) if pts then @@ -394,6 +398,9 @@ end minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() + if advtrains.interlocking.handle_signal_formspec_tabheader_fields(pname, fields) then + return true + end if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then return end diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua index edf3ab1..4b41187 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -1,63 +1,136 @@ 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) +local function describe_t1_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") + else + return attrans("Continue with the speed limit of @1", tostring(spv)) + end +end + +local function describe_t1_shunt_aspect(shunt) + if shunt then + return attrans("Shunting allowed") + else + return attrans("No shunting") + end +end + +local function describe_t1_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 +end + +advtrains.interlocking.describe_t1_main_aspect = describe_t1_main_aspect +advtrains.interlocking.describe_t1_shunt_aspect = describe_t1_shunt_aspect +advtrains.interlocking.describe_t1_distant_aspect = describe_t1_distant_aspect + +local function describe_supported_aspects_t1(suppasp, isasp) + local t = {} - 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 + entries[idx] = describe_t1_main_aspect(spv) end - form[#form+1] = F.dropdown(0.5, 2, 6, "main", entries, selid, true) + t.main = entries + t.main_current = selid - 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"), + selid = 1 + if isasp and isasp.shunt then + selid = 2 + end + entries = { + describe_t1_shunt_aspect(false), + describe_t1_shunt_aspect(true), } - form[#form+1] = F.dropdown(0.5, 3.5, 6, "shunt_free", entries, st, true) + t.shunt = entries + t.shunt_current = selid end - form[#form+1] = F.S_label(0.5, 4.5, "Distant aspect") - local entries = {} - local selid = 1 + entries = {} + 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 + entries[idx] = describe_t1_distant_aspect(spv) end - form[#form+1] = F.dropdown(0.5, 5, 6, "dst", entries, selid, true) + t.dst = entries + t.dst_current = selid + return t +end + +advtrains.interlocking.describe_supported_aspects_t1 = describe_supported_aspects_t1 + +local signal_tabheader_map = {} + +local function make_signal_formspec_tabheader(pname, pos, width, selid) + signal_tabheader_map[pname] = pos + local options = { + attrans("Signal aspect"), + attrans("Influence point"), + attrans("Distant signalling"), + } + return F.tabheader(0, 0, nil, nil, "signal_tab", options, selid) +end + +local function handle_signal_formspec_tabheader_fields(pname, fields) + local n = tonumber(fields.signal_tab) + local pos = signal_tabheader_map[pname] + if not (n and pos) then + return false + end + if n == 1 then + local node = advtrains.ndb.get_node(pos) + advtrains.interlocking.show_signal_form(pos, node, pname) + elseif n == 2 then + advtrains.interlocking.show_ip_form(pos, pname) + end + return true +end + +advtrains.interlocking.make_signal_formspec_tabheader = make_signal_formspec_tabheader +advtrains.interlocking.handle_signal_formspec_tabheader_fields = handle_signal_formspec_tabheader_fields + +local function make_signal_aspect_selector_t1(suppasp, purpose, isasp) + local form = {"size[7,7.25]"} + local t = describe_supported_aspects_t1(suppasp, isasp) + if type(purpose) == "table" then + form[#form+1] = make_signal_formspec_tabheader(purpose.pname, purpose.pos, 7, 1) + purpose = "" + end + 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") + form[#form+1] = F.dropdown(0.5, 2, 6, "main", t.main, t.main_current, true) + + form[#form+1] = F.S_label(0.5, 3, "Shunt aspect") + if t.shunt then + form[#form+1] = F.dropdown(0.5, 3.5, 6, "shunt_free", t.shunt, t.shunt_current, true) + else + form[#form+1] = F.S_label(0.5, 3.5, "The shunt aspect cannot be changed") + end + + form[#form+1] = F.S_label(0.5, 4.5, "Distant aspect") + form[#form+1] = F.dropdown(0.5, 5, 6, "dst", t.dst, t.dst_current, true) form[#form+1] = F.S_button_exit(0.5, 6, 6, 1, "save", "Save signal aspect") return table.concat(form) @@ -69,6 +142,10 @@ local function make_signal_aspect_selector_t2(suppasp, purpose, isasp) if not def then return nil end + if type(purpose) == "table" then + form[#form+1] = make_signal_formspec_tabheader(purpose.pname, purpose.pos, 7, 1) + purpose = "" + end form[#form+1] = F.S_label(0.5, 0.5, "Select signal aspect") form[#form+1] = F.label(0.5, 1, purpose) @@ -93,6 +170,9 @@ function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_ info = {}, } local purpose = p_purpose or "" + if type(p_purpose) == "table" then + purpose = {pname = pname, pos = p_purpose} + end local form if suppasp.type == 2 then @@ -106,13 +186,13 @@ function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_ local token = advtrains.random_id() minetest.show_formspec(pname, "at_il_sigaspdia_"..token, form) - minetest.after(1, function() + --minetest.after(1, function() players_aspsel[pname] = { suppasp = suppasp, callback = callback, token = token, } - end) + --end) end local function usebool(sup, val, free) @@ -147,6 +227,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if psl then if formname == "at_il_sigaspdia_"..psl.token then local suppasp = psl.suppasp + if handle_signal_formspec_tabheader_fields(pname, fields) then + return true + end if fields.save then local asp if suppasp.type == 2 then diff --git a/advtrains_interlocking/signal_aspects.lua b/advtrains_interlocking/signal_aspects.lua index a70d176..eebb4ba 100644 --- a/advtrains_interlocking/signal_aspects.lua +++ b/advtrains_interlocking/signal_aspects.lua @@ -1,4 +1,4 @@ -type2defs = {} +local type2defs = {} local function register_type2(def) local t = {type = 2} diff --git a/advtrains_interlocking/signal_main_ui.lua b/advtrains_interlocking/signal_main_ui.lua new file mode 100644 index 0000000..e69de29 diff --git a/advtrains_interlocking/spec/signal_api_spec.lua b/advtrains_interlocking/spec/signal_api_spec.lua index 2659380..cd7a1d1 100644 --- a/advtrains_interlocking/spec/signal_api_spec.lua +++ b/advtrains_interlocking/spec/signal_api_spec.lua @@ -4,8 +4,7 @@ mineunit("core") _G.advtrains = { interlocking = { - aspects = sourcefile("signal_aspects"), - --aspects = fixture("../../signal_aspects"), + aspects = fixture("../../signal_aspects"), }, ndb = { get_node = minetest.get_node, diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 0cc10da..b3b8221 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -608,7 +608,8 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle if not tcbs.signal_name then tcbs.signal_name = "Signal at "..minetest.pos_to_string(sigd.p) end if not tcbs.routes then tcbs.routes = {} end - local form = "size[7,10]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" + local form = "size[7,9.75]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" + form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, tcbs.signal, 7, 1) form = form.."field[0.8,1.5;5.2,1;name;Signal name;"..minetest.formspec_escape(tcbs.signal_name).."]" form = form.."button[5.5,1.2;1,1;setname;Set]" @@ -668,12 +669,7 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle if hasprivs then 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.."button[ 3,9;2.5,1;influp;Influence Point]" - end - if tcbs.ars_disabled then - form = form.."button[0.5,9;2.5,1;arsenable;Enable ARS]" - else - form = form.."button[0.5,9;2.5,1;arsdisable;Disable ARS]" + form = form..string.format("checkbox[0.5,8.75;ars;Automatic routesetting;%s]", not tcbs.ars_disabled) end elseif sigd_equal(tcbs.route_origin, sigd) then -- something has gone wrong: tcbs.routeset should have been set... @@ -706,6 +702,10 @@ end minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() + if string.find(formname, "^at_il_signalling_") + and advtrains.interlocking.handle_signal_formspec_tabheader_fields(pname, fields) then + return true + end if not minetest.check_player_privs(pname, "train_operator") then return end @@ -792,16 +792,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.chat_send_player(pname, "Please cancel route first!") end end - if fields.influp and hasprivs then - advtrains.interlocking.show_ip_form(tcbs.signal, pname) - return - end - if tcbs.ars_disabled and fields.arsenable then - tcbs.ars_disabled = nil - end - if not tcbs.ars_disabled and fields.arsdisable then - tcbs.ars_disabled = true + if fields.ars then + tcbs.ars_disabled = not minetest.is_yes(fields.ars) end if fields.auto then -- cgit v1.2.3 From 98c37108762c6d7c9f1d691b84f49bfa65b81b28 Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Sat, 11 Jun 2022 18:07:00 +0200 Subject: Implement primitive distant signaling --- advtrains/formspec.lua | 10 ++ advtrains_interlocking/database.lua | 4 + advtrains_interlocking/demosignals.lua | 6 +- advtrains_interlocking/distant.lua | 137 +++++++++++++++++++++ advtrains_interlocking/distant_ui.lua | 76 ++++++++++++ advtrains_interlocking/init.lua | 3 + advtrains_interlocking/routesetting.lua | 21 ++++ advtrains_interlocking/signal_api.lua | 85 +------------ advtrains_interlocking/signal_aspect_accessors.lua | 127 +++++++++++++++++++ advtrains_interlocking/signal_aspect_ui.lua | 6 +- advtrains_interlocking/signal_aspects.lua | 26 +++- advtrains_interlocking/signal_main_ui.lua | 0 .../spec/basic_signalling_spec.lua | 87 +++++++++++++ advtrains_interlocking/spec/signal_api_spec.lua | 49 -------- advtrains_interlocking/tcb_ts_ui.lua | 8 +- 15 files changed, 507 insertions(+), 138 deletions(-) create mode 100644 advtrains_interlocking/distant.lua create mode 100644 advtrains_interlocking/distant_ui.lua create mode 100644 advtrains_interlocking/signal_aspect_accessors.lua delete mode 100644 advtrains_interlocking/signal_main_ui.lua create mode 100644 advtrains_interlocking/spec/basic_signalling_spec.lua delete mode 100644 advtrains_interlocking/spec/signal_api_spec.lua (limited to 'advtrains') diff --git a/advtrains/formspec.lua b/advtrains/formspec.lua index aa5aa69..20dab59 100644 --- a/advtrains/formspec.lua +++ b/advtrains/formspec.lua @@ -9,6 +9,14 @@ local function make_list(entries) return table.concat(t, ",") end +local function f_button(x, y, w, h, id, text) + return sformat("button[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) +end + +local function S_button(x, y, w, h, id, ...) + return f_button(x, y, w, h, id, attrans(...)) +end + local function f_button_exit(x, y, w, h, id, text) return sformat("button_exit[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) end @@ -54,6 +62,8 @@ local function f_tabheader(x, y, w, h, id, entries, sel, transparent, border) end return { + button = f_button, + S_button = S_button, button_exit = f_button_exit, S_button_exit = S_button_exit, dropdown = f_dropdown, diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index efa5eb8..c5ae906 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -134,6 +134,9 @@ function ildb.load(data) if data.supposed_aspects then advtrains.interlocking.load_supposed_aspects(data.supposed_aspects) end + if data.distant then + advtrains.distant.load(data.distant) + end --COMPATIBILITY to Signal aspect format -- TODO remove in time... @@ -177,6 +180,7 @@ function ildb.save() influence_points = influence_points, npr_rails = advtrains.interlocking.npr_rails, supposed_aspects = advtrains.interlocking.save_supposed_aspects(), + distant = advtrains.distant.save(), } end diff --git a/advtrains_interlocking/demosignals.lua b/advtrains_interlocking/demosignals.lua index 1c1b8b2..de6926a 100644 --- a/advtrains_interlocking/demosignals.lua +++ b/advtrains_interlocking/demosignals.lua @@ -50,7 +50,7 @@ minetest.register_node("advtrains_interlocking:ds_danger", { }, on_rightclick = advtrains.interlocking.signal_rc_handler, can_dig = advtrains.interlocking.signal_can_dig, - after_dig_node = advtrains.interlocking.signal_after_dig, + after_destruct = advtrains.interlocking.signal_after_dig, }) minetest.register_node("advtrains_interlocking:ds_free", { description = "Demo signal at Free", @@ -71,7 +71,7 @@ minetest.register_node("advtrains_interlocking:ds_free", { }, on_rightclick = advtrains.interlocking.signal_rc_handler, can_dig = advtrains.interlocking.signal_can_dig, - after_dig_node = advtrains.interlocking.signal_after_dig, + after_destruct = advtrains.interlocking.signal_after_dig, }) minetest.register_node("advtrains_interlocking:ds_slow", { description = "Demo signal at Slow", @@ -92,6 +92,6 @@ minetest.register_node("advtrains_interlocking:ds_slow", { }, on_rightclick = advtrains.interlocking.signal_rc_handler, can_dig = advtrains.interlocking.signal_can_dig, - after_dig_node = advtrains.interlocking.signal_after_dig, + after_destruct = advtrains.interlocking.signal_after_dig, }) diff --git a/advtrains_interlocking/distant.lua b/advtrains_interlocking/distant.lua new file mode 100644 index 0000000..ffa9e08 --- /dev/null +++ b/advtrains_interlocking/distant.lua @@ -0,0 +1,137 @@ +local db_distant = {} +local db_distant_of = {} + +local A = advtrains.interlocking.aspects +local pts = advtrains.encode_pos +local stp = advtrains.decode_pos + +local function db_load(x) + if type(x) ~= "table" then + return + end + db_distant = x.distant + db_distant_of = x.distant_of +end + +local function db_save() + return { + distant = db_distant, + distant_of = db_distant_of, + } +end + +local update_signal, update_main, update_dst + +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 + +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 + +local function unassign_all(pos, force) + unassign_main(pos) + unassign_dst(pos, force) +end + +local function assign(main, dst, by) + 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} + update_dst(dst) +end + +local function pre_occupy(dst, by) + local pts_dst = pts(dst) + unassign_dst(dst) + db_distant_of[pts_dst] = {nil, by} +end + +local function get_distant(main) + local pts_main = pts(main) + return db_distant[pts_main] or {} +end + +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_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_dst = function(dst) + advtrains.interlocking.signal_readjust_aspect(dst) +end + +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, +} diff --git a/advtrains_interlocking/distant_ui.lua b/advtrains_interlocking/distant_ui.lua new file mode 100644 index 0000000..4ec2255 --- /dev/null +++ b/advtrains_interlocking/distant_ui.lua @@ -0,0 +1,76 @@ +local F = advtrains.formspec +local D = advtrains.distant +local I = advtrains.interlocking + +function advtrains.interlocking.show_distant_signal_form(pos, pname) + local form = {"size[7,7]"} + form[#form+1] = advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 3) + local main, set_by = D.get_main(pos) + if main then + local pts_main = minetest.pos_to_string(main) + form[#form+1] = F.S_label(0.5, 0.5, "This signal is a distant signal of @1.", pts_main) + if set_by == "manual" then + form[#form+1] = F.S_label(0.5, 1, "The assignment is made manually.") + elseif set_by == "routesetting" then + form[#form+1] = F.S_label(0.5, 1, "The assignment is made by the routesetting system.") + end + else + form[#form+1] = F.S_label(0.5, 0.5, "This signal is not assigned to a main signal.") + form[#form+1] = F.S_label(0.5, 1, "The distant aspect of the signal is not used.") + end + if set_by ~= nil then + form[#form+1] = F.S_button_exit(0.5, 1.5, 3, 1, "unassign_dst", "Unassign") + form[#form+1] = F.S_button_exit(3.5, 1.5, 3, 1, "assign_dst", "Reassign") + else + form[#form+1] = F.S_button_exit(0.5, 1.5, 6, 1, "assign_dst", "Assign") + end + minetest.show_formspec(pname, "advtrains:distant_" .. minetest.pos_to_string(pos), table.concat(form)) +end + +local signal_pos = {} +local function init_signal_assignment(pname, pos) + if not minetest.check_player_privs(pname, "interlocking") then + minetest.chat_send_player(pname, attrans("This operation is not allowed without the @1 privilege.", "interlocking")) + return + end + signal_pos[pname] = pos + minetest.chat_send_player(pname, attrans("Please punch the signal to use as the main signal.")) +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 + local spos = signal_pos[pname] + if not spos then + return + end + signal_pos[pname] = nil + local is_signal = minetest.get_item_group(node.name, "advtrains_signal") >= 2 + if not is_signal then + minetest.chat_send_player(pname, attrans("Incompatible signal.")) + return + end + minetest.chat_send_player(pname, attrans("Successfully assigned signal.")) + D.assign(pos, spos, "manual") +end) + +minetest.register_on_player_receive_fields(function(player, formname, fields) + local pname = player:get_player_name() + local pos = minetest.string_to_pos(string.match(formname, "^advtrains:distant_(.+)$") or "") + if not pos then + return + end + if not minetest.check_player_privs(pname, "interlocking") then + return + end + if advtrains.interlocking.handle_signal_formspec_tabheader_fields(pname, fields) then + return true + end + if fields.unassign_dst then + D.unassign_dst(pos) + elseif fields.assign_dst then + init_signal_assignment(pname, pos) + end +end) diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua index d0b75a8..908d998 100644 --- a/advtrains_interlocking/init.lua +++ b/advtrains_interlocking/init.lua @@ -15,6 +15,9 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELI advtrains.interlocking.aspects = dofile(modpath.."signal_aspects.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/routesetting.lua b/advtrains_interlocking/routesetting.lua index 67efaea..f1b4455 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -45,6 +45,7 @@ function ilrs.set_route(signal, route, try) local rtename = route.name local signalname = ildb.get_tcbs(signal).signal_name local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp + local signals = {} while c_sigd and i<=#route do c_tcbs = ildb.get_tcbs(c_sigd) if not c_tcbs then @@ -115,6 +116,7 @@ function ilrs.set_route(signal, route, try) c_tcbs.aspect = route.aspect or advtrains.interlocking.GENERIC_FREE c_tcbs.route_origin = signal advtrains.interlocking.update_signal_aspect(c_tcbs) + signals[#signals+1] = c_tcbs.signal end end -- advance @@ -122,6 +124,25 @@ function ilrs.set_route(signal, route, try) c_sigd = c_rseg.next i = i + 1 end + + -- Distant signaling + local lastsig = nil + if c_sigd then + local e_tcbs = ildb.get_tcbs(c_sigd) + local pos = e_tcbs and e_tcbs.signal + if pos then + lastsig = pos + end + end + for i = #signals, 1, -1 do + if lastsig then + local pos = signals[i] + local _, assigned_by = advtrains.distant.get_main(pos) + if assigned_by ~= "manual" then + advtrains.distant.assign(lastsig, signals[i], "routesetting") + end + end + end return true end diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index 5b3baf8..1fd4e34 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -167,7 +167,6 @@ This function will query get_aspect to retrieve the new aspect. local DANGER = { main = 0, - dst = false, shunt = false, } advtrains.interlocking.DANGER = DANGER @@ -178,8 +177,6 @@ advtrains.interlocking.GENERIC_FREE = { dst = false, } -local supposed_aspects = {} - local function convert_aspect_if_necessary(asp) if type(asp.main) == "table" then local newasp = {} @@ -200,24 +197,7 @@ local function convert_aspect_if_necessary(asp) end return asp end - -function advtrains.interlocking.load_supposed_aspects(tbl) - if tbl then - supposed_aspects = tbl - end -end - -function advtrains.interlocking.save_supposed_aspects() - return supposed_aspects -end - -local function set_supposed_aspect(pos, asp) - supposed_aspects[advtrains.roundfloorpts(pos)] = asp -end - -local function get_supposed_aspect(pos) - return supposed_aspects[advtrains.roundfloorpts(pos)] -end +advtrains.interlocking.signal_convert_aspect_if_necessary = convert_aspect_if_necessary function advtrains.interlocking.update_signal_aspect(tcbs) if tcbs.signal then @@ -233,27 +213,8 @@ end function advtrains.interlocking.signal_after_dig(pos) -- clear influence point advtrains.interlocking.db.clear_ip_by_signalpos(pos) - set_supposed_aspect(pos, nil) -end - -function advtrains.interlocking.signal_set_aspect(pos, asp) - asp = convert_aspect_if_necessary(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 {} - local newasp = asp - if suppasp.type == 2 then - asp = advtrains.interlocking.aspects.type1_to_type2main(asp, suppasp.group) - end - set_supposed_aspect(pos, newasp) - ndef.advtrains.set_aspect(pos, node, asp) - local aspect_changed = advtrains.interlocking.aspects.not_equalp(oldasp, newasp) - if aspect_changed then - advtrains.interlocking.signal_on_aspect_changed(pos) - end - end + advtrains.interlocking.signal_clear_aspect(pos) + advtrains.distant.unassign_all(pos, true) end -- should be called when aspect has changed on this signal. @@ -312,46 +273,6 @@ function advtrains.interlocking.signal_get_supposed_aspect(pos) return DANGER; end --- Returns the actual aspect of the signal at position, as returned by the nodedef. --- returns nil when there's no signal at the position -function advtrains.interlocking.signal_get_real_aspect(pos) - local node=advtrains.ndb.get_node(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 - asp = convert_aspect_if_necessary(asp) - return asp - end - return nil -end - --- Returns the signal aspect as reported in the suppasp table. -function advtrains.interlocking.signal_get_aspect(pos) - local asp = get_supposed_aspect(pos) - if not asp then - asp = advtrains.interlocking.signal_get_real_aspect(pos) - set_supposed_aspect(pos, asp) - end - return asp -end - --- Returns the "supported_aspects" of the signal at position, as returned by the nodedef. --- returns nil when there's no signal at the position -function advtrains.interlocking.signal_get_supported_aspects(pos) - local node=advtrains.ndb.get_node(pos) - local ndef=minetest.registered_nodes[node.name] - if ndef and ndef.advtrains and ndef.advtrains.supported_aspects then - local asp = ndef.advtrains.supported_aspects - return asp - end - return nil -end - local players_assign_ip = {} local function ipmarker(ipos, connid) diff --git a/advtrains_interlocking/signal_aspect_accessors.lua b/advtrains_interlocking/signal_aspect_accessors.lua new file mode 100644 index 0000000..02a03ea --- /dev/null +++ b/advtrains_interlocking/signal_aspect_accessors.lua @@ -0,0 +1,127 @@ +local A = advtrains.interlocking.aspects +local D = advtrains.distant +local I = advtrains.interlocking +local N = advtrains.ndb +local pts = advtrains.roundfloorpts + +local get_aspect + +local supposed_aspects = {} + +function I.load_supposed_aspects(tbl) + if tbl then + supposed_aspects = tbl + end +end + +function I.save_supposed_aspects() + return supposed_aspects +end + +local function get_supposed_aspect(pos) + return supposed_aspects[pts(pos)] +end + +local function set_supposed_aspect(pos, asp) + supposed_aspects[pts(pos)] = asp +end + +local function get_ndef(pos) + local node = N.get_node(pos) + return minetest.registered_nodes[node.name] or {} +end + +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 + +local function adjust_aspect(pos, asp) + asp = table.copy(I.signal_convert_aspect_if_necessary(asp)) + + local mainpos = D.get_main(pos) + local nxtasp + if asp.main ~= 0 and mainpos then + nxtasp = get_aspect(mainpos) + asp.dst = nxtasp.main + else + asp.dst = nil + end + + local suppasp = get_supported_aspects(pos) + if not suppasp then + return asp, asp + end + local stype = suppasp.type + if stype == 2 then + local group = suppasp.group + local name + if asp.main ~= 0 and nxtasp and nxtasp.type2group == group and nxtasp.type2name then + name = A.get_type2_dst(group, nxtasp.type2name) + else + name = A.type1_to_type2main(asp, group) + end + asp.type2group = group + asp.type2name = name + return asp, name + end + asp.type2name = nil + asp.type2group = nil + return asp, asp +end + +local function get_real_aspect(pos) + local ndef = get_ndef(pos) + if ndef.advtrains and ndef.advtrains.get_aspect then + local asp = ndef.advtrains.get_aspect(pos, node) or I.DANGER + local suppasp = get_supported_aspects(pos) + if suppasp.type == 2 then + asp = A.type2main_to_type1(suppasp.group, asp) + end + return adjust_aspect(pos, asp) + end + return nil +end + +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 + +local function set_aspect(pos, asp) + 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, aspval = adjust_aspect(pos, asp) + set_supposed_aspect(pos, newasp) + ndef.advtrains.set_aspect(pos, node, aspval) + local aspect_changed = A.not_equalp(oldasp, newasp) + if aspect_changed then + I.signal_on_aspect_changed(pos) + D.update_main(pos) + end + end +end + +local function clear_aspect(pos) + set_supposed_aspect(pos, nil) +end + +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 4b41187..30b5165 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -43,7 +43,7 @@ local function describe_supported_aspects_t1(suppasp, isasp) local entries = {} local selid = 1 for idx, spv in ipairs(suppasp.main) do - if isasp and spv == isasp.main then + if isasp and spv == (isasp.main or false) then selid = idx end entries[idx] = describe_t1_main_aspect(spv) @@ -67,7 +67,7 @@ local function describe_supported_aspects_t1(suppasp, isasp) entries = {} selid = 1 for idx, spv in ipairs(suppasp.dst) do - if isasp and spv == isasp.dst then + if isasp and spv == (isasp.dst or false) then selid = idx end entries[idx] = describe_t1_distant_aspect(spv) @@ -102,6 +102,8 @@ local function handle_signal_formspec_tabheader_fields(pname, fields) advtrains.interlocking.show_signal_form(pos, node, pname) elseif n == 2 then advtrains.interlocking.show_ip_form(pos, pname) + elseif n == 3 then + advtrains.interlocking.show_distant_signal_form(pos, pname) end return true end diff --git a/advtrains_interlocking/signal_aspects.lua b/advtrains_interlocking/signal_aspects.lua index eebb4ba..2866ae1 100644 --- a/advtrains_interlocking/signal_aspects.lua +++ b/advtrains_interlocking/signal_aspects.lua @@ -44,6 +44,27 @@ local function get_type2_definition(name) return type2defs[name] end +local function get_type2_danger(group) + local def = type2defs[group] + if not def then + return nil + end + local main = def.main + return main[#main] +end + +local function get_type2_dst(group, name) + local def = type2defs[group] + if not def then + return nil + end + local aspidx = name + if type(name) ~= "number" then + aspidx = def.main[name] or 1 + end + return def.main[math.max(1, aspidx-1)].name +end + local function type2main_to_type1(name, asp) local def = type2defs[name] if not def then @@ -53,7 +74,7 @@ local function type2main_to_type1(name, asp) if type(asp) == "number" then aspidx = asp else - aspidx = def.main[asp] + aspidx = def.main[asp] or 2 end local asptbl = def.main[aspidx] if not asptbl then @@ -62,11 +83,13 @@ local function type2main_to_type1(name, asp) if type(asp) == "number" then asp = asptbl.name end + local dst = def.main[math.min(#def.main, aspidx+1)].main local t = { main = asptbl.main, type2name = asp, type2group = name, + dst = dst, } if aspidx > 1 and aspidx < #asptbl then t.dst = asptbl[aspidx+1].main @@ -116,6 +139,7 @@ end return { register_type2 = register_type2, get_type2_definition = get_type2_definition, + get_type2_dst = get_type2_dst, type2main_to_type1 = type2main_to_type1, type1_to_type2main = type1_to_type2main, equalp = equalp, diff --git a/advtrains_interlocking/signal_main_ui.lua b/advtrains_interlocking/signal_main_ui.lua deleted file mode 100644 index e69de29..0000000 diff --git a/advtrains_interlocking/spec/basic_signalling_spec.lua b/advtrains_interlocking/spec/basic_signalling_spec.lua new file mode 100644 index 0000000..0b79972 --- /dev/null +++ b/advtrains_interlocking/spec/basic_signalling_spec.lua @@ -0,0 +1,87 @@ +--[[ +This file tests a large part of the signaling system, as a lot of tests for the +signaling system tend to overlap for various parts of the system. +]] + +require("mineunit") +mineunit("core") + +_G.advtrains = { + interlocking = { + aspects = fixture("../../signal_aspects"), + }, + ndb = { + get_node = minetest.get_node, + swap_node = minetest.swap_node, + } +} + +fixture("advtrains_helpers") +fixture("../../database") +sourcefile("distant") +sourcefile("signal_api") +sourcefile("signal_aspect_accessors") +fixture("../../demosignals") + +local D = advtrains.distant +local I = advtrains.interlocking + +local stub_aspect_t1 = { + free = {main = -1}, + slow = {main = 6}, + danger = {main = 0, shunt = false}, +} +local stub_pos_t1 = {} +for i = 1, 3 do + stub_pos_t1[i] = {x = 1, y = 0, z = i} +end + +world.layout { + {stub_pos_t1[1], "advtrains_interlocking:ds_danger"}, + {stub_pos_t1[2], "advtrains_interlocking:ds_slow"}, + {stub_pos_t1[3], "advtrains_interlocking:ds_free"}, +} + +describe("API for supposed signal aspects", function() + it("should load and save data properly", function() + local tbl = {_foo = true} + I.load_supposed_aspects(tbl) + assert.same(tbl, I.save_supposed_aspects()) + end) + it("should set and get type 1 signals properly", function () + local pos = stub_pos_t1[2] + local asp = stub_aspect_t1.slow + local newasp = { main = math.random(1,5) } + assert.same(asp, I.signal_get_aspect(pos)) + I.signal_set_aspect(pos, newasp) + assert.same(newasp, I.signal_get_aspect(pos)) + assert.same(asp, I.signal_get_real_aspect(pos)) + I.signal_set_aspect(pos, asp) + end) +end) + +describe("Distant signaling", function() + it("should assign distant signals and set the distant aspect correspondingly", function() + for i = 1, 2 do + D.assign(stub_pos_t1[i], stub_pos_t1[i+1]) + end + assert.same(stub_aspect_t1.danger, I.signal_get_aspect(stub_pos_t1[1])) + assert.same({main = 6, dst = 0}, I.signal_get_aspect(stub_pos_t1[2])) + assert.same({main = -1, dst = 6}, I.signal_get_aspect(stub_pos_t1[3])) + end) + it("should report assignments properly", function() + assert.same({stub_pos_t1[1], "manual"}, {D.get_main(stub_pos_t1[2])}) + assert.same({[advtrains.encode_pos(stub_pos_t1[3])] = "manual"}, D.get_dst(stub_pos_t1[2])) + end) + it("should update distant aspects automatically", function() + I.signal_set_aspect(stub_pos_t1[2], {main = 2, dst = -1}) + assert.same({main = 2, dst = 0}, I.signal_get_aspect(stub_pos_t1[2])) + assert.same({main = -1, dst = 2}, I.signal_get_aspect(stub_pos_t1[3])) + end) + it("should unassign signals when one is removed", function() + world.set_node(stub_pos_t1[2], "air") + assert.same({}, D.get_dst(stub_pos_t1[1])) + assert.same({}, {D.get_main(stub_pos_t1[3])}) + assert.same(stub_aspect_t1.free, I.signal_get_aspect(stub_pos_t1[3])) + end) +end) diff --git a/advtrains_interlocking/spec/signal_api_spec.lua b/advtrains_interlocking/spec/signal_api_spec.lua deleted file mode 100644 index cd7a1d1..0000000 --- a/advtrains_interlocking/spec/signal_api_spec.lua +++ /dev/null @@ -1,49 +0,0 @@ -require("mineunit") - -mineunit("core") - -_G.advtrains = { - interlocking = { - aspects = fixture("../../signal_aspects"), - }, - ndb = { - get_node = minetest.get_node, - } -} - -fixture("advtrains_helpers") -fixture("../../database") -sourcefile("signal_api") - -local stub_aspect_t1 = { main = math.random() } -local stub_pos_t1 = {x = 1, y = 0, z = 1} - -minetest.register_node(":stubsignal_t1", { - advtrains = { - supported_aspects = {}, - get_aspect = function () return stub_aspect_t1 end, - set_aspect = function () end, - }, - groups = { advtrains_signal = 2 }, -}) - -world.layout { - {stub_pos_t1, "stubsignal_t1"}, -} - -describe("API for supposed signal aspects", function() - it("should load and save data properly", function() - local tbl = {_foo = true} - advtrains.interlocking.load_supposed_aspects(tbl) - assert.same(tbl, advtrains.interlocking.save_supposed_aspects()) - end) - it("should set and get type 1 signals properly", function () - local pos = stub_pos_t1 - local asp = stub_aspect_t1 - local newasp = { dst = math.random() } - assert.same(asp, advtrains.interlocking.signal_get_aspect(pos)) - advtrains.interlocking.signal_set_aspect(pos, newasp) - assert.same(newasp, advtrains.interlocking.signal_get_aspect(pos)) - assert.same(asp, advtrains.interlocking.signal_get_real_aspect(pos)) - end) -end) diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index b3b8221..9f88296 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -14,6 +14,7 @@ local lntrans = { "A", "B" } local function sigd_to_string(sigd) return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] end +advtrains.interlocking.sigd_to_string = sigd_to_string minetest.register_node("advtrains_interlocking:tcb_node", { drawtype = "mesh", @@ -608,7 +609,7 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle if not tcbs.signal_name then tcbs.signal_name = "Signal at "..minetest.pos_to_string(sigd.p) end if not tcbs.routes then tcbs.routes = {} end - local form = "size[7,9.75]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" + local form = "size[7,10.25]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, tcbs.signal, 7, 1) form = form.."field[0.8,1.5;5.2,1;name;Signal name;"..minetest.formspec_escape(tcbs.signal_name).."]" form = form.."button[5.5,1.2;1,1;setname;Set]" @@ -670,6 +671,7 @@ 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... @@ -796,6 +798,10 @@ 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 -- cgit v1.2.3 From 34405b8431b7c758988b56cc09d21d6de74b727b Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Sat, 13 Aug 2022 21:16:15 +0200 Subject: Allow assigning distant signals from the main signal --- advtrains/formspec.lua | 27 +++++++++++++++++ advtrains_interlocking/distant_ui.lua | 55 +++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 2 deletions(-) (limited to 'advtrains') diff --git a/advtrains/formspec.lua b/advtrains/formspec.lua index 20dab59..58968da 100644 --- a/advtrains/formspec.lua +++ b/advtrains/formspec.lua @@ -32,6 +32,21 @@ local function f_dropdown(x, y, w, id, entries, sel, indexed) indexed and ";true" or "") end +local function f_image_button(x, y, w, h, texture, id, label, noclip, drawborder, pressed) + local st = {string.format("%f,%f;%f,%f;%s;%s;%s", x, y, w, h, fsescape(texture), fsescape(id), fsescape(label))} + if pressed then + st[#st+1] = tostring(noclip or false) + st[#st+1] = tostring(drawborder or false) + st[#st+1] = fsescape(pressed) + end + return sformat("image_button[%s]", table.concat(st, ";")) +end + +local function f_image_button_exit(x, y, w, h, texture, id, label) + local st = {string.format("%f,%f;%f,%f;%s;%s;%s", x, y, w, h, fsescape(texture), fsescape(id), fsescape(label))} + return sformat("image_button_exit[%s]", table.concat(st, ";")) +end + local function f_label(x, y, text) return sformat("label[%f,%f;%s]", x, y, fsescape(text)) end @@ -61,13 +76,25 @@ local function f_tabheader(x, y, w, h, id, entries, sel, transparent, border) return string.format("tabheader[%s]", table.concat(st, ";")) end +local function f_textlist(x, y, w, h, id, entries, sel, transparent) + local st = {string.format("%f,%f;%f,%f;%s;%s", x, y, w, h, id, make_list(entries))} + if sel then + st[#st+1] = tostring(sel) + st[#st+1] = tostring(transparent or false) + end + return string.format("textlist[%s]", table.concat(st, ";")) +end + return { button = f_button, S_button = S_button, button_exit = f_button_exit, S_button_exit = S_button_exit, dropdown = f_dropdown, + image_button = f_image_button, + image_button_exit = f_image_button_exit, label = f_label, S_label = S_label, tabheader = f_tabheader, + textlist = f_textlist, } diff --git a/advtrains_interlocking/distant_ui.lua b/advtrains_interlocking/distant_ui.lua index 4ec2255..e1e14b7 100644 --- a/advtrains_interlocking/distant_ui.lua +++ b/advtrains_interlocking/distant_ui.lua @@ -24,6 +24,16 @@ function advtrains.interlocking.show_distant_signal_form(pos, pname) else form[#form+1] = F.S_button_exit(0.5, 1.5, 6, 1, "assign_dst", "Assign") end + + local dsts = D.get_dst(pos) + local dstlist = {} + for pos, _ in pairs(dsts) do + dstlist[#dstlist+1] = minetest.pos_to_string(advtrains.decode_pos(pos)) + end + form[#form+1] = F.S_label(0.5, 2.5, "This signal has the following distant signals:") + form[#form+1] = F.textlist(0.5, 3, 4.5, 3.5, "dstlist", dstlist) + form[#form+1] = F.image_button_exit(5.5, 3.5, 1, 1, "cdb_add.png", "dst_add", "") + form[#form+1] = F.image_button_exit(5.5, 5, 1, 1, "cdb_clear.png", "dst_del", "") minetest.show_formspec(pname, "advtrains:distant_" .. minetest.pos_to_string(pos), table.concat(form)) end @@ -37,25 +47,46 @@ local function init_signal_assignment(pname, pos) minetest.chat_send_player(pname, attrans("Please punch the signal to use as the main signal.")) end +local distant_pos = {} +local function init_distant_assignment(pname, pos) + if not minetest.check_player_privs(pname, "interlocking") then + minetest.send_chat_player(pname, attrans("This operation is now allowed without the @1 privilege.", "interlocking")) + return + end + distant_pos[pname] = pos + minetest.chat_send_player(pname, attrans("Please punch the signal to use as the distant signal.")) +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 local spos = signal_pos[pname] + local distant = false if not spos then - return + spos = distant_pos[pname] + if not spos then + return + end + distant = true end signal_pos[pname] = nil + distant_pos[pname] = nil local is_signal = minetest.get_item_group(node.name, "advtrains_signal") >= 2 if not is_signal then minetest.chat_send_player(pname, attrans("Incompatible signal.")) return end minetest.chat_send_player(pname, attrans("Successfully assigned signal.")) - D.assign(pos, spos, "manual") + if distant then + D.assign(spos, pos, "manual") + else + D.assign(pos, spos, "manual") + end end) +local dstsel = {} minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() local pos = minetest.string_to_pos(string.match(formname, "^advtrains:distant_(.+)$") or "") @@ -72,5 +103,25 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) D.unassign_dst(pos) elseif fields.assign_dst then init_signal_assignment(pname, pos) + elseif fields.dst_add then + init_distant_assignment(pname, pos) + elseif fields.dstlist then + dstsel[pname] = minetest.explode_textlist_event(fields.dstlist).index + elseif fields.dst_del then + local selid = dstsel[pname] + if selid then + local dsts = D.get_dst(pos) + local pos + for p, _ in pairs(dsts) do + selid = selid-1 + if selid <= 0 then + pos = p + break + end + end + if pos then + D.unassign_dst(advtrains.decode_pos(pos)) + end + end end end) -- cgit v1.2.3 From 67efae9c9ad6f93aaa418d512c4ae1817eed6d24 Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Sun, 18 Dec 2022 11:01:26 +0100 Subject: Adjust signal aspect formspecs to be of similar size --- advtrains/formspec.lua | 10 +++++ advtrains_interlocking/distant_ui.lua | 8 ++-- advtrains_interlocking/signal_api.lua | 14 ++++--- advtrains_interlocking/signal_aspect_ui.lua | 57 +++++++++++++++-------------- 4 files changed, 53 insertions(+), 36 deletions(-) (limited to 'advtrains') diff --git a/advtrains/formspec.lua b/advtrains/formspec.lua index 58968da..743d3f3 100644 --- a/advtrains/formspec.lua +++ b/advtrains/formspec.lua @@ -17,6 +17,14 @@ local function S_button(x, y, w, h, id, ...) return f_button(x, y, w, h, id, attrans(...)) end +local function f_checkbox(x, y, name, selected, label) + return sformat("checkbox[%f,%f;%s;%s;%s]", x, y, name, label, selected and "true" or "false") +end + +local function S_checkbox(x, y, name, selected, ...) + return f_checkbox(x, y, name, selected, attrans(...)) +end + local function f_button_exit(x, y, w, h, id, text) return sformat("button_exit[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) end @@ -88,6 +96,8 @@ end return { button = f_button, S_button = S_button, + checkbox = f_checkbox, + S_checkbox = S_checkbox, button_exit = f_button_exit, S_button_exit = S_button_exit, dropdown = f_dropdown, diff --git a/advtrains_interlocking/distant_ui.lua b/advtrains_interlocking/distant_ui.lua index e1e14b7..a7ff406 100644 --- a/advtrains_interlocking/distant_ui.lua +++ b/advtrains_interlocking/distant_ui.lua @@ -3,7 +3,7 @@ local D = advtrains.distant local I = advtrains.interlocking function advtrains.interlocking.show_distant_signal_form(pos, pname) - local form = {"size[7,7]"} + local form = {"size[7,6.5]"} form[#form+1] = advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 3) local main, set_by = D.get_main(pos) if main then @@ -31,9 +31,9 @@ function advtrains.interlocking.show_distant_signal_form(pos, pname) dstlist[#dstlist+1] = minetest.pos_to_string(advtrains.decode_pos(pos)) end form[#form+1] = F.S_label(0.5, 2.5, "This signal has the following distant signals:") - form[#form+1] = F.textlist(0.5, 3, 4.5, 3.5, "dstlist", dstlist) - form[#form+1] = F.image_button_exit(5.5, 3.5, 1, 1, "cdb_add.png", "dst_add", "") - form[#form+1] = F.image_button_exit(5.5, 5, 1, 1, "cdb_clear.png", "dst_del", "") + form[#form+1] = F.textlist(0.5, 3, 4.5, 3, "dstlist", dstlist) + form[#form+1] = F.image_button_exit(5.5, 3.25, 1, 1, "cdb_add.png", "dst_add", "") + form[#form+1] = F.image_button_exit(5.5, 4.75, 1, 1, "cdb_clear.png", "dst_del", "") minetest.show_formspec(pname, "advtrains:distant_" .. minetest.pos_to_string(pos), table.concat(form)) end diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index 54202f0..8bb92bf 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -137,14 +137,18 @@ function advtrains.interlocking.show_ip_form(pos, pname, only_notset) if not minetest.check_player_privs(pname, "interlocking") then return end - local form = "size[7,5]label[0.5,0.5;Signal at "..minetest.pos_to_string(pos).."]" - form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 2) + local form = "size[7,6.5]label[0.5,0.5;Signal at "..minetest.pos_to_string(pos).."]" + local node = advtrains.ndb.get_node(pos) + local ndef = minetest.registered_nodes[node.name] or {} + if ndef.advtrains and ndef.advtrains.set_aspect then + form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 2) + end advtrains.interlocking.db.check_for_duplicate_ip(pos) local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) if pts then form = form.."label[0.5,1.5;Influence point is set at "..pts.."/"..connid.."]" - form = form.."button_exit[0.5,2.5; 5,1;set;Move]" - form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]" + form = form.."button_exit[0.5,4.25; 6,1;set;Move]" + form = form.."button_exit[0.5,5.25; 6,1;clear;Clear]" local ipos = minetest.string_to_pos(pts) ipmarker(ipos, connid) else @@ -152,7 +156,7 @@ function advtrains.interlocking.show_ip_form(pos, pname, only_notset) form = form.."label[0.5,2.0;It is recommended to set an influence point.]" form = form.."label[0.5,2.5;This is the point where trains will obey the signal.]" - form = form.."button_exit[0.5,3.5; 5,1;set;Set]" + form = form.."button_exit[0.5,5.25; 6,1;set;Set]" end if not only_notset or not pts then minetest.show_formspec(pname, "at_il_ipassign_"..minetest.pos_to_string(pos), form) diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua index ccedb01..6de21e6 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -52,16 +52,8 @@ local function describe_supported_aspects_t1(suppasp, isasp) t.main_current = selid if suppasp.shunt == nil then - selid = 1 - if isasp and isasp.shunt then - selid = 2 - end - entries = { - describe_t1_shunt_aspect(false), - describe_t1_shunt_aspect(true), - } - t.shunt = entries - t.shunt_current = selid + t.shunt = true + t.shunt_current = isasp and isasp.shunt end entries = {} @@ -83,8 +75,12 @@ local signal_tabheader_map = {} local function make_signal_formspec_tabheader(pname, pos, width, selid) signal_tabheader_map[pname] = pos + local firstlabel = attrans("Signal aspect") + if advtrains.interlocking.db.get_sigd_for_signal(pos) then + firstlabel = attrans("Routesetting") + end local options = { - attrans("Signal aspect"), + firstlabel, attrans("Influence point"), attrans("Distant signalling"), } @@ -112,7 +108,7 @@ advtrains.interlocking.make_signal_formspec_tabheader = make_signal_formspec_tab advtrains.interlocking.handle_signal_formspec_tabheader_fields = handle_signal_formspec_tabheader_fields local function make_signal_aspect_selector_t1(suppasp, purpose, isasp) - local form = {"size[7,7.25]"} + local form = {"size[7,6.5]"} local t = describe_supported_aspects_t1(suppasp, isasp) if type(purpose) == "table" then form[#form+1] = make_signal_formspec_tabheader(purpose.pname, purpose.pos, 7, 1) @@ -124,22 +120,21 @@ local function make_signal_aspect_selector_t1(suppasp, purpose, isasp) form[#form+1] = F.S_label(0.5, 1.5, "Main aspect") form[#form+1] = F.dropdown(0.5, 2, 6, "main", t.main, t.main_current, true) - form[#form+1] = F.S_label(0.5, 3, "Shunt aspect") + form[#form+1] = F.S_label(0.5, 3, "Distant aspect") + form[#form+1] = F.dropdown(0.5, 3.5, 6, "dst", t.dst, t.dst_current, true) + if t.shunt then - form[#form+1] = F.dropdown(0.5, 3.5, 6, "shunt_free", t.shunt, t.shunt_current, true) + form[#form+1] = F.S_checkbox(0.5, 4.25, "shunt", t.shunt_current, "Allow shunting") else - form[#form+1] = F.S_label(0.5, 3.5, "The shunt aspect cannot be changed") + form[#form+1] = F.S_label(0.5, 4.5, "The shunt aspect cannot be changed.") end - form[#form+1] = F.S_label(0.5, 4.5, "Distant aspect") - form[#form+1] = F.dropdown(0.5, 5, 6, "dst", t.dst, t.dst_current, true) - - form[#form+1] = F.S_button_exit(0.5, 6, 6, 1, "save", "Save signal aspect") + form[#form+1] = F.S_button_exit(0.5, 5.25, 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 form = {"size[7,6.5]"} local def = advtrains.interlocking.aspects.get_type2_definition(suppasp.group) if not def then return nil @@ -159,8 +154,13 @@ local function make_signal_aspect_selector_t2(suppasp, purpose, isasp) 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") + form[#form+1] = F.S_label(0.5, 1.5, "Signal group: @1", def.label) + form[#form+1] = F.dropdown(0.5, 2, 6, "asp", entries, selid, true) + form[#form+1] = F.S_label(0.5, 3, "Aspect in effect:") + form[#form+1] = F.label(0.5, 3.5, describe_t1_main_aspect(isasp.main)) + form[#form+1] = F.label(0.5, 4, describe_t1_distant_aspect(isasp.dst)) + form[#form+1] = F.label(0.5, 4.5, describe_t1_shunt_aspect(isasp.shunt)) + form[#form+1] = F.S_button_exit(0.5, 5.25, 6, 1, "save", "Save signal aspect") return table.concat(form) end @@ -205,7 +205,7 @@ local function usebool(sup, val, free) end end -local function get_aspect_from_formspec_t1(suppasp, fields) +local function get_aspect_from_formspec_t1(suppasp, fields, psl) local maini = tonumber(fields.main) if not maini then return end local dsti = tonumber(fields.dst) @@ -213,12 +213,12 @@ local function get_aspect_from_formspec_t1(suppasp, fields) return { main = suppasp.main[maini], dst = suppasp.dst[dsti], - shunt = usebool(suppasp.shunt, fields.shunt_free, "2"), + shunt = usebool(suppasp.shunt, psl.shunt, "true"), info = {}, } end -local function get_aspect_from_formspec_t2(suppasp, fields) +local function get_aspect_from_formspec_t2(suppasp, fields, psl) local asp = advtrains.interlocking.aspects.type2_to_type1(suppasp, tonumber(fields.asp)) return asp end @@ -232,12 +232,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if handle_signal_formspec_tabheader_fields(pname, fields) then return true end + if fields.shunt then + psl.shunt = fields.shunt + end if fields.save then local asp if suppasp.type == 2 then - asp = get_aspect_from_formspec_t2(suppasp, fields) + asp = get_aspect_from_formspec_t2(suppasp, fields, psl) else - asp = get_aspect_from_formspec_t1(suppasp, fields) + asp = get_aspect_from_formspec_t1(suppasp, fields, psl) end if asp then psl.callback(pname, asp) -- cgit v1.2.3 From 2d072cdc67ec3018eaba36b7c79c2b19e574a94d Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Fri, 6 Jan 2023 14:45:03 +0100 Subject: Rework formspecs; add description to JP signal group --- advtrains/formspec.lua | 47 +++--- advtrains_interlocking/distant_ui.lua | 76 ++++----- advtrains_interlocking/signal_api.lua | 75 +++++---- advtrains_interlocking/signal_aspect_ui.lua | 232 +++++++++++++++++----------- advtrains_interlocking/tcb_ts_ui.lua | 5 - advtrains_signals_japan/init.lua | 1 + 6 files changed, 252 insertions(+), 184 deletions(-) (limited to 'advtrains') diff --git a/advtrains/formspec.lua b/advtrains/formspec.lua index 743d3f3..8894354 100644 --- a/advtrains/formspec.lua +++ b/advtrains/formspec.lua @@ -9,32 +9,28 @@ local function make_list(entries) return table.concat(t, ",") end -local function f_button(x, y, w, h, id, text) - return sformat("button[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) +local function S_wrapper(f, i0) + return function(...) + local args = {...} + args[i0] = attrans(unpack(args,i0)) + return f(unpack(args,1,i0)) + end end -local function S_button(x, y, w, h, id, ...) - return f_button(x, y, w, h, id, attrans(...)) +local function f_button(x, y, w, id, text) + return sformat("button[%f,%f;%f,0.75;%s;%s]", x, y, w, id, text) end local function f_checkbox(x, y, name, selected, label) - return sformat("checkbox[%f,%f;%s;%s;%s]", x, y, name, label, selected and "true" or "false") -end - -local function S_checkbox(x, y, name, selected, ...) - return f_checkbox(x, y, name, selected, attrans(...)) + return sformat("checkbox[%f,%f;%s;%s;%s]", x, y+0.25, name, label, selected and "true" or "false") end -local function f_button_exit(x, y, w, h, id, text) - return sformat("button_exit[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text) -end - -local function S_button_exit(x, y, w, h, id, ...) - return f_button_exit(x, y, w, h, id, attrans(...)) +local function f_button_exit(x, y, w, id, text) + return sformat("button_exit[%f,%f;%f,0.75;%s;%s]", x, y, w, id, text) end local function f_dropdown(x, y, w, id, entries, sel, indexed) - return sformat("dropdown[%f,%f;%f;%s;%s;%d%s]", + return sformat("dropdown[%f,%f;%f,0.75;%s;%s;%d%s]", x, y, w, id, make_list(entries), sel or 1, indexed and ";true" or "") @@ -56,11 +52,15 @@ local function f_image_button_exit(x, y, w, h, texture, id, label) end local function f_label(x, y, text) - return sformat("label[%f,%f;%s]", x, y, fsescape(text)) + return sformat("label[%f,%f;%s]", x, y+0.25, fsescape(text)) +end + +local function f_field_aux(x, y, w, id, default) + return sformat("field[%f,%f;%f,0.75;%s;;%s]", x, y, w, id, default) end -local function S_label(x, y, ...) - return f_label(x, y, attrans(...)) +local function f_field(x, y, w, id, label, default) + return f_label(x, y-0.5, label) .. f_field_aux(x, y, w, id, default) end local function f_tabheader(x, y, w, h, id, entries, sel, transparent, border) @@ -95,16 +95,17 @@ end return { button = f_button, - S_button = S_button, + S_button = S_wrapper(f_button, 5), checkbox = f_checkbox, - S_checkbox = S_checkbox, + S_checkbox = S_wrapper(f_checkbox, 5), button_exit = f_button_exit, - S_button_exit = S_button_exit, + S_button_exit = S_wrapper(f_button_exit, 5), dropdown = f_dropdown, + field = f_field, image_button = f_image_button, image_button_exit = f_image_button_exit, label = f_label, - S_label = S_label, + S_label = S_wrapper(f_label, 3), tabheader = f_tabheader, textlist = f_textlist, } diff --git a/advtrains_interlocking/distant_ui.lua b/advtrains_interlocking/distant_ui.lua index a7ff406..0907684 100644 --- a/advtrains_interlocking/distant_ui.lua +++ b/advtrains_interlocking/distant_ui.lua @@ -2,39 +2,52 @@ local F = advtrains.formspec local D = advtrains.distant local I = advtrains.interlocking -function advtrains.interlocking.show_distant_signal_form(pos, pname) - local form = {"size[7,6.5]"} - form[#form+1] = advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 3) +function I.make_short_dst_formspec_component(pos, x, y, w) local main, set_by = D.get_main(pos) if main then local pts_main = minetest.pos_to_string(main) - form[#form+1] = F.S_label(0.5, 0.5, "This signal is a distant signal of @1.", pts_main) + local desc = attrans("The assignment is made with an unknown method.") if set_by == "manual" then - form[#form+1] = F.S_label(0.5, 1, "The assignment is made manually.") + desc = attrans("The assignment is made manually.") elseif set_by == "routesetting" then - form[#form+1] = F.S_label(0.5, 1, "The assignment is made by the routesetting system.") + desc = attrans("The assignment is made by the routesetting system.") end + return table.concat { + F.S_label(x, y, "This signal is a distant signal of @1.", pts_main), + F.label(x, y+0.5, desc), + F.S_button_exit(x, y+1, w/2-0.125, "dst_assign", "Reassign"), + F.S_button_exit(x+w/2+0.125, y+1, w/2-0.125, "dst_unassign", "Unassign"), + } else - form[#form+1] = F.S_label(0.5, 0.5, "This signal is not assigned to a main signal.") - form[#form+1] = F.S_label(0.5, 1, "The distant aspect of the signal is not used.") - end - if set_by ~= nil then - form[#form+1] = F.S_button_exit(0.5, 1.5, 3, 1, "unassign_dst", "Unassign") - form[#form+1] = F.S_button_exit(3.5, 1.5, 3, 1, "assign_dst", "Reassign") - else - form[#form+1] = F.S_button_exit(0.5, 1.5, 6, 1, "assign_dst", "Assign") + return table.concat { + F.S_label(x, y, "This signal is not assigned to a main signal."), + F.S_label(x, y+0.5, "The distant aspect of the signal is not used."), + F.S_button_exit(x, y+1, w, "dst_assign", "Assign") + } end +end - local dsts = D.get_dst(pos) +function I.make_dst_list_formspec_component(pos, x, y, w, h) + local ymid = y+0.25+h/2 local dstlist = {} - for pos, _ in pairs(dsts) do - dstlist[#dstlist+1] = minetest.pos_to_string(advtrains.decode_pos(pos)) + for pos, _ in pairs(D.get_dst(pos)) do + table.insert(dstlist, minetest.pos_to_string(advtrains.decode_pos(pos))) end - form[#form+1] = F.S_label(0.5, 2.5, "This signal has the following distant signals:") - form[#form+1] = F.textlist(0.5, 3, 4.5, 3, "dstlist", dstlist) - form[#form+1] = F.image_button_exit(5.5, 3.25, 1, 1, "cdb_add.png", "dst_add", "") - form[#form+1] = F.image_button_exit(5.5, 4.75, 1, 1, "cdb_clear.png", "dst_del", "") - minetest.show_formspec(pname, "advtrains:distant_" .. minetest.pos_to_string(pos), table.concat(form)) + return table.concat { + F.S_label(x, y, "Distant signals:"), + F.textlist(x, y+0.5, w-1, h-0.5, "dstlist", dstlist), + F.image_button_exit(x+w-0.75, ymid-0.875, 0.75, 0.75, "cdb_add.png", "dst_add", ""), + F.image_button_exit(x+w-0.75, ymid+0.125, 0.75, 0.75, "cdb_clear.png", "dst_del", ""), + } +end + +function I.make_dst_formspec_component(pos, x, y, w, h) + return I.make_short_dst_formspec_component(pos, x, y, w, h) + .. I.make_dst_list_formspec_component(pos, x, y+2, w, h-2) +end + +function I.show_distant_signal_form(pos, pname) + return I.show_ip_form(pos, pname) end local signal_pos = {} @@ -87,21 +100,14 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) end) local dstsel = {} -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - local pos = minetest.string_to_pos(string.match(formname, "^advtrains:distant_(.+)$") or "") - if not pos then - return - end - if not minetest.check_player_privs(pname, "interlocking") then + +function advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields) + if not (pos and minetest.check_player_privs(pname, "interlocking")) then return end - if advtrains.interlocking.handle_signal_formspec_tabheader_fields(pname, fields) then - return true - end - if fields.unassign_dst then + if fields.dst_unassign then D.unassign_dst(pos) - elseif fields.assign_dst then + elseif fields.dst_assign then init_signal_assignment(pname, pos) elseif fields.dst_add then init_distant_assignment(pname, pos) @@ -124,4 +130,4 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end end -end) +end diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index 8bb92bf..cd408d7 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -1,5 +1,7 @@ -- Signal API implementation +local F = advtrains.formspec + local DANGER = { main = 0, shunt = false, @@ -131,57 +133,68 @@ local function ipmarker(ipos, connid) }) end --- shows small info form for signal IP state/assignment +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 form = "size[7,6.5]label[0.5,0.5;Signal at "..minetest.pos_to_string(pos).."]" - local node = advtrains.ndb.get_node(pos) - local ndef = minetest.registered_nodes[node.name] or {} - if ndef.advtrains and ndef.advtrains.set_aspect then - form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, pos, 7, 2) - end - advtrains.interlocking.db.check_for_duplicate_ip(pos) - local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) + local ipform, pts, connid = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7) + local form = table.concat { + "formspec_version[4]", + "size[8,6.75]", + ipform, + advtrains.interlocking.make_dst_formspec_component(pos, 0.5, 2, 7, 4.25), + } if pts then - form = form.."label[0.5,1.5;Influence point is set at "..pts.."/"..connid.."]" - form = form.."button_exit[0.5,4.25; 6,1;set;Move]" - form = form.."button_exit[0.5,5.25; 6,1;clear;Clear]" local ipos = minetest.string_to_pos(pts) ipmarker(ipos, connid) - else - form = form.."label[0.5,1.5;Influence point is not set.]" - form = form.."label[0.5,2.0;It is recommended to set an influence point.]" - form = form.."label[0.5,2.5;This is the point where trains will obey the signal.]" - - form = form.."button_exit[0.5,5.25; 6,1;set;Set]" end if not only_notset or not pts then - minetest.show_formspec(pname, "at_il_ipassign_"..minetest.pos_to_string(pos), form) + minetest.show_formspec(pname, "at_il_propassign_"..minetest.pos_to_string(pos), form) end end -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if advtrains.interlocking.handle_signal_formspec_tabheader_fields(pname, fields) then - return true - end - if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then +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 - local pts = string.match(formname, "^at_il_ipassign_([^_]+)$") + 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 - if fields.set then - advtrains.interlocking.signal_init_ip_assign(pos, pname) - elseif fields.clear then - advtrains.interlocking.db.clear_ip_by_signalpos(pos) - end + advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields) + advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields) end end) diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua index 6de21e6..ddab793 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -37,6 +37,20 @@ advtrains.interlocking.describe_t1_main_aspect = describe_t1_main_aspect advtrains.interlocking.describe_t1_shunt_aspect = describe_t1_shunt_aspect advtrains.interlocking.describe_t1_distant_aspect = describe_t1_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 + else + return y + end +end + local function describe_supported_aspects_t1(suppasp, isasp) local t = {} @@ -50,11 +64,22 @@ local function describe_supported_aspects_t1(suppasp, isasp) end t.main = entries t.main_current = selid + t.main_string = tostring(isasp.main) + if t.main == nil then + t.main_string = "" + end - if suppasp.shunt == nil then - t.shunt = true - t.shunt_current = isasp and isasp.shunt + t.shunt = { + attrans("No shunting"), + attrans("Shunting allowed"), + attrans("Proceed as main"), + } + + 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 entries = {} selid = 1 @@ -71,96 +96,98 @@ end advtrains.interlocking.describe_supported_aspects_t1 = describe_supported_aspects_t1 -local signal_tabheader_map = {} - -local function make_signal_formspec_tabheader(pname, pos, width, selid) - signal_tabheader_map[pname] = pos - local firstlabel = attrans("Signal aspect") - if advtrains.interlocking.db.get_sigd_for_signal(pos) then - firstlabel = attrans("Routesetting") - end - local options = { - firstlabel, - attrans("Influence point"), - attrans("Distant signalling"), - } - return F.tabheader(0, 0, nil, nil, "signal_tab", options, selid) -end - -local function handle_signal_formspec_tabheader_fields(pname, fields) - local n = tonumber(fields.signal_tab) - local pos = signal_tabheader_map[pname] - if not (n and pos) then - return false - end - if n == 1 then - local node = advtrains.ndb.get_node(pos) - advtrains.interlocking.show_signal_form(pos, node, pname) - elseif n == 2 then - advtrains.interlocking.show_ip_form(pos, pname) - elseif n == 3 then - advtrains.interlocking.show_distant_signal_form(pos, pname) - end - return true -end - -advtrains.interlocking.make_signal_formspec_tabheader = make_signal_formspec_tabheader -advtrains.interlocking.handle_signal_formspec_tabheader_fields = handle_signal_formspec_tabheader_fields - local function make_signal_aspect_selector_t1(suppasp, purpose, isasp) - local form = {"size[7,6.5]"} local t = describe_supported_aspects_t1(suppasp, isasp) + local formmode = 1 + + local pos if type(purpose) == "table" then - form[#form+1] = make_signal_formspec_tabheader(purpose.pname, purpose.pos, 7, 1) - purpose = "" + formmode = 2 + pos = purpose.pos end - 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") - form[#form+1] = F.dropdown(0.5, 2, 6, "main", t.main, t.main_current, true) + local form = { + "formspec_version[4]", + string.format("size[8,%f]", ({5.75, 9.25})[formmode]), + F.S_label(0.5, 0.5, "Select signal aspect"), + } + if formmode == 1 then + form[#form+1] = F.label(0.5, 1, purpose) + else + form[#form+1] = F.S_label(0.5, 1, "Signal at @1", minetest.pos_to_string(pos)) + end - form[#form+1] = F.S_label(0.5, 3, "Distant aspect") - form[#form+1] = F.dropdown(0.5, 3.5, 6, "dst", t.dst, t.dst_current, true) + form[#form+1] = F.S_label(0.5, 1.5, "Main aspect") + if formmode == 1 then + form[#form+1] = F.field(0.5, 2, 7, "asp_mainval", "", t.main_string) + else + form[#form+1] = F.dropdown(0.5, 2, 7, "asp_mainsel", t.main, t.main_current, true) + end - if t.shunt then - form[#form+1] = F.S_checkbox(0.5, 4.25, "shunt", t.shunt_current, "Allow shunting") + form[#form+1] = F.S_label(0.5, 3, "Shunt aspect") + if formmode == 2 and t.shunt_const then + form[#form+1] = F.label(0.5, 3.5, t.shunt[t.shunt_current]) + form[#form+1] = F.S_label(0.5, 4, "The shunt aspect cannot be changed.") else - form[#form+1] = F.S_label(0.5, 4.5, "The shunt aspect cannot be changed.") + form[#form+1] = F.dropdown(0.5, 3.5, 7, "asp_shunt", t.shunt, t.shunt_current, true) + end + + form[#form+1] = F.S_button_exit(0.5, 4.5, 7, "asp_save", "Save signal aspect") + + if formmode == 2 then + form[#form+1] = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 5.5, 7) + form[#form+1] = advtrains.interlocking.make_short_dst_formspec_component(pos, 0.5, 7, 7) end - form[#form+1] = F.S_button_exit(0.5, 5.25, 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,6.5]"} local def = advtrains.interlocking.aspects.get_type2_definition(suppasp.group) if not def then return nil end + local formmode = 1 + + local pos if type(purpose) == "table" then - form[#form+1] = make_signal_formspec_tabheader(purpose.pname, purpose.pos, 7, 1) - purpose = "" + formmode = 2 + pos = purpose.pos + end + local form = { + "formspec_version[4]", + string.format("size[8,%f]", ({4.25, 10.25})[formmode]), + F.S_label(0.5, 0.5, "Select signal aspect") + } + if formmode == 1 then + form[#form+1] = F.label(0.5, 1, purpose) + else + form[#form+1] = F.S_label(0.5, 1, "Signal at @1", minetest.pos_to_string(pos)) 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 + local selid = #def.main + if isasp then + if isasp.type2name ~= def.main[selid].name then + selid = 1 end - entries[idx] = spv.label end + if selid > 1 then + selid = 2 + end + local entries = { + def.main[1].label, + def.main[#def.main].label, + } form[#form+1] = F.S_label(0.5, 1.5, "Signal group: @1", def.label) - form[#form+1] = F.dropdown(0.5, 2, 6, "asp", entries, selid, true) - form[#form+1] = F.S_label(0.5, 3, "Aspect in effect:") - form[#form+1] = F.label(0.5, 3.5, describe_t1_main_aspect(isasp.main)) - form[#form+1] = F.label(0.5, 4, describe_t1_distant_aspect(isasp.dst)) - form[#form+1] = F.label(0.5, 4.5, describe_t1_shunt_aspect(isasp.shunt)) - form[#form+1] = F.S_button_exit(0.5, 5.25, 6, 1, "save", "Save signal aspect") + form[#form+1] = F.dropdown(0.5, 2, 7, "asp_sel", entries, selid, true) + form[#form+1] = F.S_button_exit(0.5, 3, 7, "asp_save", "Save signal aspect") + + if formmode == 2 then + form[#form+1] = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 4, 7) + form[#form+1] = advtrains.interlocking.make_dst_formspec_component(pos, 0.5, 5.5, 7, 4.25) + end + return table.concat(form) end @@ -188,13 +215,14 @@ function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_ 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) + minetest.after(0, function() + players_aspsel[pname] = { + purpose = purpose, + suppasp = suppasp, + callback = callback, + token = token, + } + end) end local function usebool(sup, val, free) @@ -206,20 +234,45 @@ local function usebool(sup, val, free) end local function get_aspect_from_formspec_t1(suppasp, fields, psl) - local maini = tonumber(fields.main) - if not maini then return end - local dsti = tonumber(fields.dst) - if not dsti then return end + local maini = tonumber(fields.asp_mainsel) + local main = suppasp.main[maini] + if not maini then + local mainval = fields.asp_mainval + if mainval == "-1" then + main = -1 + elseif string.match(mainval, "^%d+$") then + main = tonumber(mainval) + else + main = nil + end + 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 + end return { - main = suppasp.main[maini], - dst = suppasp.dst[dsti], - shunt = usebool(suppasp.shunt, psl.shunt, "true"), + main = main, + shunt = shunt, + proceed_as_main = proceed_as_main, info = {}, } end local function get_aspect_from_formspec_t2(suppasp, fields, psl) - local asp = advtrains.interlocking.aspects.type2_to_type1(suppasp, tonumber(fields.asp)) + local sel = tonumber(fields.asp_sel) + local def = advtrains.interlocking.aspects.get_type2_definition(suppasp.group) + if not (sel and def) then + return + end + if sel ~= 1 then + sel = #def.main + end + local asp = advtrains.interlocking.aspects.type2_to_type1(suppasp, sel) return asp end @@ -229,13 +282,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if psl then if formname == "at_il_sigaspdia_"..psl.token then local suppasp = psl.suppasp - if handle_signal_formspec_tabheader_fields(pname, fields) then - return true - end - if fields.shunt then - psl.shunt = fields.shunt - end - if fields.save then + if fields.asp_save then local asp if suppasp.type == 2 then asp = get_aspect_from_formspec_t2(suppasp, fields, psl) @@ -246,6 +293,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) psl.callback(pname, asp) 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) + end else players_aspsel[pname] = nil end diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 3898d73..9aea18c 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -614,7 +614,6 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle if not tcbs.routes then tcbs.routes = {} end local form = "size[7,10.25]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" - form = form .. advtrains.interlocking.make_signal_formspec_tabheader(pname, tcbs.signal, 7, 1) form = form.."field[0.8,1.5;5.2,1;name;Signal name;"..minetest.formspec_escape(tcbs.signal_name).."]" form = form.."button[5.5,1.2;1,1;setname;Set]" @@ -708,10 +707,6 @@ end minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() - if string.find(formname, "^at_il_signalling_") - and advtrains.interlocking.handle_signal_formspec_tabheader_fields(pname, fields) then - return true - end if not minetest.check_player_privs(pname, "train_operator") then return end diff --git a/advtrains_signals_japan/init.lua b/advtrains_signals_japan/init.lua index a28478f..fe74259 100644 --- a/advtrains_signals_japan/init.lua +++ b/advtrains_signals_japan/init.lua @@ -294,6 +294,7 @@ local function process_signal(name, sigdata, isrpt) local type2def = {} type2def.name = typename type2def.main = {} + type2def.label = S(string.format("Japanese signal (type %s)", string.upper(name))) local def = {} local tx = {} def.typename = typename -- cgit v1.2.3