diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 6c6047f..6d86e39 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -122,13 +122,15 @@ TCB data structure ts_id = -- ID of the assigned track section signal = -- optional: when set, routes can be set from this tcb/direction and signal -- aspect will be set accordingly. - routetar = -- Route set from this signal. This is the entry that is cleared once + routeset = -- Route set from this signal. This is the entry that is cleared once -- train has passed the signal. (which will set the aspect to "danger" again) route_committed = -- When setting/requesting a route, routetar will be set accordingly, -- while the signal still displays danger and nothing is written to the TCs -- As soon as the route can actually be set, all relevant TCs and turnouts are set and this field -- is set true, clearing the signal aspect = -- The aspect the signal should show. If this is nil, should show the most restrictive aspect (red) + signal_name = -- The human-readable name of the signal, only for documenting purposes + routes = { } -- a collection of routes from this signal }, [2] = { -- Variant: end of track-circuited area (initial state of TC) ts_id = nil, -- this is the indication for end_of_interlocking @@ -263,6 +265,8 @@ end local function merge_ts(root_id, merge_id) local rts = ildb.get_ts(root_id) local mts = ildb.get_ts(merge_id) + if not mts then return end -- This may be the case when sync_tcb_neighbors + -- inserts the same id twice. do nothing. -- cobble together the list of TCBs for _, msigd in ipairs(mts.tc_breaks) do @@ -292,7 +296,7 @@ function ildb.sync_tcb_neighbors(pos, connid) end atdebug("Traversing from ",pos, connid) - local counter_hit = traverser(found_tcbs, pos, conns, connid, 0, hit_counter) + local counter_hit = traverser(found_tcbs, pos, conns, connid, 0) local ts_id local list_eoi = {} diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua index 72f72ca..e67abd7 100644 --- a/advtrains_interlocking/route_prog.lua +++ b/advtrains_interlocking/route_prog.lua @@ -126,6 +126,14 @@ end -- e.g. prog_ or vis for later visualizations function advtrains.interlocking.visualize_route(origin, route, context) advtrains.interlocking.clear_visu_context(context) + + local oyaw = 0 + local onode_ok, oconns, orhe = advtrains.get_rail_info_at(origin.p, advtrains.all_tracktypes) + if onode_ok then + oyaw = advtrains.dir_to_angle(oconns[origin.s].c) + end + routemarker(context, origin.p, "rte_origin", "at_il_route_start.png", oyaw, route.name) + for k,sigd in ipairs(route.tcbpath) do local yaw = 0 local node_ok, conns, rhe = advtrains.get_rail_info_at(sigd.p, advtrains.all_tracktypes) @@ -154,7 +162,9 @@ function advtrains.interlocking.init_route_prog(pname, sigd) pcfix = {}, } } + advtrains.interlocking.visualize_route(sigd, player_rte_prog[pname].route, "prog_"..pname) minetest.chat_send_player(pname, "Route programming mode active. Punch TCBs to add route segments, punch turnouts to lock them.") + minetest.chat_send_player(pname, "Type /at_rp_set when you are done, /at_rp_discard to cancel route programming") end local function get_last_route_item(origin, route) @@ -255,9 +265,16 @@ minetest.register_chatcommand("at_rp_set", return false, "Cannot program route without a target" end rp.route.name = param - -- TODO save that route somewhere in origin - atdebug("ROUTE RESULT:",rp) + + local tcbs = advtrains.interlocking.db.get_tcbs(rp.origin) + if not tcbs then + return false, "The origin TCB of this route doesn't exist!" + end + + table.insert(tcbs.routes, rp.route) + advtrains.interlocking.clear_visu_context("prog_"..pname) + player_rte_prog[pname] = nil return true, "Successfully programmed route" end return false, "You were not programming a route!" diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 454e2c4..8745626 100644 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -122,6 +122,8 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) local tcbs = advtrains.interlocking.db.get_tcbs(sigd) if tcbs then tcbs.signal = pos + tcbs.signal_name = "Signal at "..minetest.pos_to_string(sigd.p) + tcbs.routes = {} advtrains.interlocking.db.set_sigd_for_signal(pos, sigd) minetest.chat_send_player(pname, "Configuring TCB: Successfully assigned signal.") else @@ -133,7 +135,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) else minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.") end - players_assign_tcb[pname] = nil + players_assign_signal[pname] = nil end end) @@ -223,8 +225,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end else if f_makeil[connid] then - advtrains.interlocking.db.create_ts({p=pos, s=connid}) + -- try sinc_tcb_neighbors first advtrains.interlocking.db.sync_tcb_neighbors(pos, connid) + -- if that didn't work, create new section + if not tcbs.ts_id then + advtrains.interlocking.db.create_ts({p=pos, s=connid}) + advtrains.interlocking.db.sync_tcb_neighbors(pos, connid) + end end -- non-interlocked if f_setfree[connid] then @@ -255,6 +262,9 @@ end) -- TS Formspec +-- textlist selection temporary storage +local ts_pselidx = {} + function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb) local ts = advtrains.interlocking.db.get_ts(ts_id) if not ts_id then return end @@ -304,6 +314,7 @@ function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb) --form = form.."label[0.5,1;Trying to unlink a TCB directly connected to this track will not work.]" end + ts_pselidx[pname]=sel_tcb minetest.show_formspec(pname, "at_il_tsconfig_"..ts_id, form) end @@ -311,6 +322,10 @@ end minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() + -- independent of the formspec, clear this whenever some formspec event happens + local tpsi = ts_pselidx[pname] + ts_pselidx[pname] = nil + local ts_id = string.match(formname, "^at_il_tsconfig_(.+)$") if ts_id and not fields.quit then local ts = advtrains.interlocking.db.get_ts(ts_id) @@ -320,6 +335,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.tcblist then local tev = minetest.explode_textlist_event(fields.tcblist) sel_tcb = tev.index + ts_pselidx[pname] = sel_tcb + elseif tpsi then + sel_tcb = tpsi end if players_link_ts[pname] then @@ -333,6 +351,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.del_tcb and sel_tcb and sel_tcb > 0 and sel_tcb <= #ts.tc_breaks then advtrains.interlocking.db.remove_from_interlocking(ts.tc_breaks[sel_tcb]) + sel_tcb = nil end if fields.link then @@ -352,7 +371,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb) end - end) -- TCB marker entities @@ -420,21 +438,61 @@ end -- Signalling formspec - set routes a.s.o -function advtrains.interlocking.show_signalling_form(sigd, pname) - local form = "size[10,10]label[0.5,0.5;Track Section Detail - ]" - form = form.."field[0.8,2;5.2,1;name;Section name;]" - form = form.."button[5.5,1.7;1,1;setname;Set]" +-- textlist selection temporary storage +local sig_pselidx = {} + +function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte) + local tcbs = advtrains.interlocking.db.get_tcbs(sigd) - --minetest.show_formspec(pname, "at_il_signalling_"..sigd.p.."_"..sigd.s, form) - --TODO this is temporary - advtrains.interlocking.init_route_prog(pname, sigd) + if not tcbs.signal then return end + 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]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" + form = form.."field[0.8,1.5;5.2,1;name;Signal name;"..tcbs.signal_name.."]" + form = form.."button[5.5,1.2;1,1;setname;Set]" + + if tcbs.routeset then + local rte = tcbs.routes[tcbs.routeset] + form = form.."label[0.5,2.5;A route is requested from this signal:]" + form = form.."label[0.5,3.0;"..rte.name.."]" + if tcbs.route_committed then + form = form.."label[0.5,2.5;Route has been set.]" + else + form = form.."label[0.5,2.5;Waiting for route to be set...]" + end + + form = form.."button[0.5,6.5;1,6;cancelroute;Cancel Route]" + else + local strtab = {} + for idx, route in ipairs(tcbs.routes) do + strtab[#strtab+1] = minetest.formspec_escape(route.name) + end + form = form.."label[0.5,2.5;Routes:]" + form = form.."textlist[0.5,3;5,3;rtelist;"..table.concat(strtab, ",").."]" + if sel_rte then + form = form.."button[0.5,6; 5,1;setroute;Set Route]" + form = form.."button[0.5,7;2,1;dsproute;Show]" + form = form.."button[2.5,7;1,1;delroute;Delete]" + form = form.."button[3.5,7;2,1;renroute;Rename]" + end + form = form.."button[0.5,8;2.5,1;newroute;New Route]" + form = form.."button[ 3,8;2.5,1;unassign;Unassign Signal]" + + end + sig_pselidx[pname] = sel_rte + minetest.show_formspec(pname, "at_il_signalling_"..minetest.pos_to_string(sigd.p).."_"..sigd.s, form) end minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() + + -- independent of the formspec, clear this whenever some formspec event happens + local tpsi = sig_pselidx[pname] + sig_pselidx[pname] = nil + local pts, connids = string.match(formname, "^at_il_signalling_([^_]+)_(%d)$") - local pts = string.match(formname, "^at_il_tcbconfig_(.+)$") local pos, connid if pts then pos = minetest.string_to_pos(pts) @@ -442,8 +500,69 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if not connid or connid<1 or connid>2 then return end end if pos and connid and not fields.quit then + local sigd = {p=pos, s=connid} + local tcbs = advtrains.interlocking.db.get_tcbs(sigd) + if not tcbs then return end - advtrains.interlocking.show_signalling_form(ts_id, pname, sel_tcb) + local sel_rte + if fields.rtelist then + local tev = minetest.explode_textlist_event(fields.rtelist) + sel_rte = tev.index + elseif tpsi then + sel_rte = tpsi + end + if fields.setname and fields.name then + tcbs.signal_name = fields.name + end + if tcbs.routeset and fields.cancelroute then + --TODO + end + if not tcbs.routeset then + if fields.newroute then + advtrains.interlocking.init_route_prog(pname, sigd) + minetest.close_formspec(pname, formname) + return + end + if sel_rte and tcbs.routes[sel_rte] then + if fields.setroute then + --TODO + end + if fields.dsproute then + local t = os.clock() + advtrains.interlocking.visualize_route(sigd, tcbs.routes[sel_rte], "disp_"..t) + minetest.after(10, function() advtrains.interlocking.clear_visu_context("disp_"..t) end) + end + if fields.renroute then + local rte = tcbs.routes[sel_rte] + minetest.show_formspec(pname, formname.."_renroute_"..sel_rte, "field[name;Enter new route name;"..rte.name.."]") + return + end + if fields.delroute then + table.remove(tcbs.routes, sel_rte) + sel_rte = nil + end + end + end + + advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte) + return + end + + -- rename route + local rind, rte_id + pts, connids, rind = string.match(formname, "^at_il_signalling_([^_]+)_(%d)_renroute_(%d+)$") + if pts then + pos = minetest.string_to_pos(pts) + connid = tonumber(connids) + rte_id = tonumber(rind) + if not connid or connid<1 or connid>2 then return end + end + if pos and connid and rind and fields.name then + local sigd = {p=pos, s=connid} + local tcbs = advtrains.interlocking.db.get_tcbs(sigd) + if tcbs.routes[rte_id] then + tcbs.routes[rte_id].name = fields.name + advtrains.interlocking.show_signalling_form(sigd, pname) + end end - end)