diff options
Diffstat (limited to 'advtrains_interlocking')
39 files changed, 0 insertions, 3975 deletions
diff --git a/advtrains_interlocking/approach.lua b/advtrains_interlocking/approach.lua deleted file mode 100644 index 8e90b5a..0000000 --- a/advtrains_interlocking/approach.lua +++ /dev/null @@ -1,113 +0,0 @@ --- Interlocking counterpart of LZB, which has been moved into the core... --- Registers LZB callback for signal management. - ---[[ -usage of lzbdata: -{ - travsht = boolean indicating whether the train will be a shunt move at "trav" - travspd = speed restriction at end of traverser - travwspd = warning speed res.t -} -]] - -local SHUNT_SPEED_MAX = advtrains.SHUNT_SPEED_MAX - -local il = advtrains.interlocking - -local function get_over_function(speed, shunt) - return function(pos, id, train, index, speed, lzbdata) - if speed == 0 and minetest.settings:get_bool("at_il_force_lzb_halt") then - atwarn(id,"overrun LZB 0 restriction (red signal) ",pos) - -- Set train 1 index backward. Hope this does not lead to bugs... - train.index = index - 0.5 - train.velocity = 0 - train.ctrl.lzb = 0 - minetest.after(0, advtrains.invalidate_path, id) - else - train.speed_restriction = speed - train.is_shunt = shunt - end - end -end - -advtrains.tnc_register_on_approach(function(pos, id, train, index, lzbdata) - - --atdebug(id,"IL ApprC",pos,index,lzbdata) - --train.debug = advtrains.print_concat_table({train.is_shunt,"|",index,"|",lzbdata}) - - local pts = advtrains.roundfloorpts(pos) - local cn = train.path_cn[index] - local travsht = lzbdata.travsht - - if travsht==nil then - travsht = train.is_shunt - end - - local travspd = lzbdata.travspd - local travwspd = lzbdata.travwspd - - -- check for signal - local asp, spos = il.db.get_ip_signal_asp(pts, cn) - - -- do ARS if needed - if spos then - --atdebug(id,"IL Spos (ARS)",spos,asp) - local sigd = il.db.get_sigd_for_signal(spos) - if sigd then - il.ars_check(sigd, train) - end - end - --atdebug("trav: ",pos, cn, asp, spos, "travsht=", lzb.travsht) - local lspd - if asp then - --atdebug(id,"IL Signal",spos,asp) - local nspd = 0 - --interpreting aspect and determining speed to proceed - if travsht then - --shunt move - if asp.shunt.free then - nspd = SHUNT_SPEED_MAX - elseif asp.shunt.proceed_as_main and asp.main.free then - nspd = asp.main.speed - travsht = false - end - else - --train move - if asp.main.free then - nspd = asp.main.speed - elseif asp.shunt.free then - nspd = SHUNT_SPEED_MAX - travsht = true - end - end - -- nspd can now be: 1. !=0: new speed restriction, 2. =0: stop here or 3. nil: keep travspd - if nspd then - if nspd == -1 then - travspd = nil - else - travspd = nspd - end - end - - local nwspd = asp.info.w_speed - if nwspd then - if nwspd == -1 then - travwspd = nil - else - travwspd = nwspd - end - end - --atdebug("ns,wns,ts,wts", nspd, nwspd, travspd, travwspd) - lspd = travspd - if travwspd and (not lspd or lspd>travwspd) then - lspd = travwspd - end - - local udata = {signal_pos = spos} - local callback = get_over_function(lspd, travsht) - advtrains.lzb_add_checkpoint(train, index, lspd, callback, udata) - end - lzbdata.travsht = travsht - lzbdata.travspd = travspd - lzbdata.travwspd = travwspd -end) diff --git a/advtrains_interlocking/ars.lua b/advtrains_interlocking/ars.lua deleted file mode 100644 index 434ae2c..0000000 --- a/advtrains_interlocking/ars.lua +++ /dev/null @@ -1,155 +0,0 @@ --- ars.lua --- automatic routesetting - ---[[ - The "ARS table" and its effects: - Every route has (or can have) an associated ARS table. This can either be - ars = { [n] = {ln="<line>"}/{rc="<routingcode>"}/{c="<a comment>"} } - a list of rules involving either line or routingcode matchers (or comments, those are ignored) - The first matching rule determines the route to set. - - or - - ars = {default = true} - this means that all trains that no other rule matches on should use this route - - Compound ("and") conjunctions are not supported (--TODO should they?) - - For editing, those tables are transformed into lines in a text area: - {ln=...} -> LN ... - {rc=...} -> RC ... - {c=...} -> #... - {default=true} -> * - See also route_ui.lua -]] - -local il = advtrains.interlocking - --- The ARS data are saved in a table format, but are entered in text format. Utility functions to transform between both. -function il.ars_to_text(arstab) - if not arstab then - return "" - end - - local txt = {} - - for i, arsent in ipairs(arstab) do - local n = "" - if arsent.n then - n = "!" - end - if arsent.ln then - txt[#txt+1] = n.."LN "..arsent.ln - elseif arsent.rc then - txt[#txt+1] = n.."RC "..arsent.rc - elseif arsent.c then - txt[#txt+1] = "#"..arsent.c - end - end - - if arstab.default then - return "*\n" .. table.concat(txt, "\n") - end - return table.concat(txt, "\n") -end - -function il.text_to_ars(t) - if t=="" then - return nil - elseif t=="*" then - return {default=true} - end - local arstab = {} - for line in string.gmatch(t, "[^\r\n]+") do - if line=="*" then - arstab.default = true - else - local c, v = string.match(line, "^(...?)%s(.*)$") - if c and v then - local n = nil - if string.sub(c,1,1) == "!" then - n = true - c = string.sub(c,2) - end - local tt=string.upper(c) - if tt=="LN" then - arstab[#arstab+1] = {ln=v, n=n} - elseif tt=="RC" then - arstab[#arstab+1] = {rc=v, n=n} - end - else - local ct = string.match(line, "^#(.*)$") - if ct then arstab[#arstab+1] = {c = ct} end - end - end - end - return arstab -end - -local function find_rtematch(routes, train) - local default - for rteid, route in ipairs(routes) do - if route.ars then - if route.ars.default then - default = rteid - else - if il.ars_check_rule_match(route.ars, train) then - return rteid - end - end - end - end - return default -end - --- Checks whether ARS rule explicitly matches. This does not take into account the "default" field, since a wider context is required for this. --- Returns the rule number that matched, or nil if nothing matched -function il.ars_check_rule_match(ars, train) - if not ars then - return nil - end - local line = train.line - local routingcode = train.routingcode - for arskey, arsent in ipairs(ars) do - --atdebug(arsent, line, routingcode) - if arsent.n then - -- rule is inverse... - if arsent.ln and (not line or arsent.ln ~= line) then - return arskey - elseif arsent.rc and (not routingcode or not string.find(" "..routingcode.." ", " "..arsent.rc.." ", nil, true)) then - return arskey - end - return nil - end - - if arsent.ln and line and arsent.ln == line then - return arskey - elseif arsent.rc and routingcode and string.find(" "..routingcode.." ", " "..arsent.rc.." ", nil, true) then - return arskey - end - end - return nil -end - -function advtrains.interlocking.ars_check(sigd, train) - local tcbs = il.db.get_tcbs(sigd) - if not tcbs or not tcbs.routes then return end - - if tcbs.ars_disabled then - -- No-ARS mode of signal. - -- ignore... - return - end - - if tcbs.routeset then - -- ARS is not in effect when a route is already set - -- just "punch" routesetting, just in case callback got lost. - minetest.after(0, il.route.update_route, sigd, tcbs, nil, nil) - return - end - - local rteid = find_rtematch(tcbs.routes, train) - if rteid then - --delay routesetting, it should not occur inside train step - -- using after here is OK because that gets called on every path recalculation - minetest.after(0, il.route.update_route, sigd, tcbs, rteid, nil) - end -end diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua deleted file mode 100644 index e2c9edc..0000000 --- a/advtrains_interlocking/database.lua +++ /dev/null @@ -1,599 +0,0 @@ --- interlocking/database.lua --- saving the location of TCB's, their neighbors and their state ---[[ - -== THIS COMMENT IS PARTIALLY INCORRECT AND OUTDATED! == - -The interlocking system is based on track circuits. -Track circuit breaks must be manually set by the user. Signals must be assigned to track circuit breaks and to a direction(connid). -To simplify the whole system, there is no overlap. -== Trains == -Trains always occupy certain track circuits. These are shown red in the signalbox view (TRAIN occupation entry). -== Database storage == -The things that are actually saved are the Track Circuit Breaks. Each TCB holds a list of the TCBs that are adjacent in each direction. -TC occupation/state is then saved inside each (TCB,Direction) and held in sync across all TCBs adjacent to this one. If something should not be in sync, -all entries are merged to perform the most restrictive setup. -== Traverser function == -To determine and update the list of neighboring TCBs, we need a traverser function. -It will start at one TCB in a specified direction (connid) and use get_adjacent_rail to crawl along the track. When encountering a turnout or a crossing, -it needs to branch(call itself recursively) to find all required TCBs. Those found TCBs are then saved in a list as tuples (TCB,Dir) -In the last step, they exchange their neighbors. -== TC states == -A track circuit does not have a state as such, but has more or less a list of "reservations" -type can be one of these: -TRAIN See Trains obove -ROUTE Route set from a signal, but no train has yet passed that signal. -Not implemented (see note by reversible): OWNED - former ROUTE segments that a train has begun passing (train_id assigned) - - Space behind a train up to the next signal, when a TC is set as REVERSIBLE -Certain TCs can be marked as "allow call-on". -== Route setting: == -Routes are set from a signal (the entry signal) to another signal facing the same direction (the exit signal) -Remember that signals are assigned to a TCB and a connid. -Whenever this is done, the following track circuits are set "reserved" by the train by saving the entry signal's ID: -- all TCs on the direct way of the route - set as ROUTE -Route setting fails whenever any TC that we want to set ROUTE to is already set ROUTE or TRAIN from another signal (except call-on, see below) -Apart from this, we need to set turnouts -- Turnouts on the track are set held as ROUTE -- Turnouts that purpose as flank protection are set held as FLANK (NOTE: left as an idea for later, because it's not clear how to do this properly without an engineer) -Note: In SimSig, it is possible to set a route into an still occupied section on the victoria line sim. (at the depot exit at seven sisters), although - there are still segments set ahead of the first train passing, remaining from another route. - Because our system will be able to remember "requested routes" and set them automatically once ready, this is not necessary here. -== Call-On/Multiple Trains == -It will be necessary to join and split trains using call-on routes. A call-on route may be set when: -- there are no ROUTE reservations -- there are TRAIN reservations only inside TCs that have "allow call-on" set -== TC Properties == -Note: Reversible property will not be implemented, assuming everything as non-rev. -This is sufficient to cover all use cases, and is done this way in reality. - REVERSIBLE - Whether trains are allowed to reverse while on track circuit - This property is supposed to be set for station tracks, where there is a signal at each end, and for sidings. - It should in no case be set for TCs covering turnouts, or for main running lines. - When a TC is not set as reversible, the OWNED status is cleared from the TC right after the train left it, - to allow other trains to pass it. - If it is set reversible, interlocking will keep the OWNED state behind the train up to the next signal, clearing it - as soon as the train passes another signal or enters a non-reversible section. -CALL_ON_ALLOWED - Whether this TC being blocked (TRAIN or ROUTE) does not prevent shunt routes being set through this TC -== More notes == -- It may not be possible to switch turnouts when their TC has any state entry - -== Route releasing (TORR) == -A train passing through a route happens as follows: -Route set from entry to exit signal -Train passes entry signal and enters first TC past the signal --> Route from signal cleared (TCs remain locked) --> ROUTE status of first TC past signal cleared -Train continues along the route. -Whenever train leaves a TC --> Clearing any routes set from this TC outward recursively - see "Reversing problem" -Whenever train enters a TC --> Clear route status from the just entered TC -Note that this prohibits by design that the train clears the route ahead of it. -== Reversing Problem == -Encountered at the Royston simulation in SimSig. It is solved there by imposing a time limit on the set route. Call-on routes can somehow be set anyway. -Imagine this setup: (T=Train, R=Route, >=in_dir TCB) - O-| Royston P2 |-O -T->---|->RRR-|->RRR-|-- -Train T enters from the left, the route is set to the right signal. But train is supposed to reverse here and stops this way: - O-| Royston P2 |-O -------|-TTTT-|->RRR-|-- -The "Route" on the right is still set. Imposing a timeout here is a thing only professional engineers can determine, not an algorithm. - O-| Royston P2 |-O -<-T---|------|->RRR-|-- -The train has left again, while route on the right is still set. -So, we have to clear the set route when the train has left the left TC. -This does not conflict with call-on routes, because both station tracks are set as "allow call-on" -Because none of the routes extends past any non-call-on sections, call-on route would be allowed here, even though the route -is locked in opposite direction at the time of routesetting. -Another case of this: ---TTT/--|->RRR-- -The / here is a non-interlocked turnout (to a non-frequently used siding). For some reason, there is no exit node there, -so the route is set to the signal at the right end. The train is taking the exit to the siding and frees the TC, without ever -having touched the right TC. -]]-- - -local TRAVERSER_LIMIT = 1000 - - -local ildb = {} - -local track_circuit_breaks = {} -local track_sections = {} - --- Assignment of signals to TCBs -local signal_assignments = {} - --- track+direction -> signal position -local influence_points = {} - -advtrains.interlocking.npr_rails = {} - - -function ildb.load(data) - if not data then return end - if data.tcbs then - track_circuit_breaks = data.tcbs - end - if data.ts then - track_sections = data.ts - end - if data.signalass then - signal_assignments = data.signalass - end - if data.rs_locks then - advtrains.interlocking.route.rte_locks = data.rs_locks - end - if data.rs_callbacks then - advtrains.interlocking.route.rte_callbacks = data.rs_callbacks - end - if data.influence_points then - influence_points = data.influence_points - end - if data.npr_rails then - advtrains.interlocking.npr_rails = data.npr_rails - end -end - -function ildb.save() - return { - tcbs = track_circuit_breaks, - ts=track_sections, - signalass = signal_assignments, - rs_locks = advtrains.interlocking.route.rte_locks, - rs_callbacks = advtrains.interlocking.route.rte_callbacks, - influence_points = influence_points, - npr_rails = advtrains.interlocking.npr_rails, - } -end - --- ---[[ -TCB data structure -{ -[1] = { -- Variant: with adjacent TCs. - ts_id = <id> -- ID of the assigned track section - signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal - -- aspect will be set accordingly. - routeset = <index in routes> -- 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 = <boolean> -- 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 = <asp> -- The aspect the signal should show. If this is nil, should show the most restrictive aspect (red) - signal_name = <string> -- The human-readable name of the signal, only for documenting purposes - routes = { <route definition> } -- a collection of routes from this signal - route_auto = <boolean> -- When set, we will automatically re-set the route (designated by routeset) -}, -[2] = { -- Variant: end of track-circuited area (initial state of TC) - ts_id = nil, -- this is the indication for end_of_interlocking - section_free = <boolean>, --this can be set by an exit node via mesecons or atlatc, - -- or from the tc formspec. -} -} - -Track section -[id] = { - name = "Some human-readable name" - tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers) - -- Can be direct ends (auto-detected), conflicting routes or TCBs that are too far away from each other - route = { - origin = <signal>, -- route origin - entry = <sigd>, -- supposed train entry point - rsn = <string>, - first = <bool> - } - route_post = { - locks = {[n] = <pts>} - next = <sigd> - } - -- Set whenever a route has been set through this TC. It saves the origin tcb id and side - -- (=the origin signal). rsn is some description to be shown to the user - -- first says whether to clear the routesetting status from the origin signal. - -- locks contains the positions where locks are held by this ts. - -- 'route' is cleared when train enters the section, while 'route_post' cleared when train leaves section. - trains = {<id>, ...} -- Set whenever a train (or more) reside in this TC -} - - -Signal specifier (sigd) (a pair of TCB/Side): -{p = <pos>, s = <1/2>} - -Signal Assignments: reverse lookup of signals assigned to TCBs -signal_assignments = { -[<signal pts>] = <sigd> -} -]] - - --- -function ildb.create_tcb(pos) - local new_tcb = { - [1] = {}, - [2] = {}, - } - local pts = advtrains.roundfloorpts(pos) - if not track_circuit_breaks[pts] then - track_circuit_breaks[pts] = new_tcb - return true - else - return false - end -end - -function ildb.get_tcb(pos) - local pts = advtrains.roundfloorpts(pos) - return track_circuit_breaks[pts] -end - -function ildb.get_tcbs(sigd) - local tcb = ildb.get_tcb(sigd.p) - if not tcb then return nil end - return tcb[sigd.s] -end - - -function ildb.create_ts(sigd) - local tcbs = ildb.get_tcbs(sigd) - local id = advtrains.random_id() - - while track_sections[id] do - id = advtrains.random_id() - end - - track_sections[id] = { - name = "Section "..id, - tc_breaks = { sigd } - } - tcbs.ts_id = id -end - -function ildb.get_ts(id) - return track_sections[id] -end - - - --- various helper functions handling sigd's -local sigd_equal = advtrains.interlocking.sigd_equal -local function insert_sigd_nodouble(list, sigd) - for idx, cmp in pairs(list) do - if sigd_equal(sigd, cmp) then - return - end - end - table.insert(list, sigd) -end - - --- This function will actually handle the node that is in connid direction from the node at pos --- so, this needs the conns of the node at pos, since these are already calculated -local function traverser(found_tcbs, pos, conns, connid, count, brk_when_found_n) - local adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, conns, connid, advtrains.all_tracktypes) - if not adj_pos then - --atdebug("Traverser found end-of-track at",pos, connid) - return - end - -- look whether there is a TCB here - if #next_conns == 2 then --if not, don't even try! - local tcb = ildb.get_tcb(adj_pos) - if tcb then - -- done with this branch - --atdebug("Traverser found tcb at",adj_pos, adj_connid) - insert_sigd_nodouble(found_tcbs, {p=adj_pos, s=adj_connid}) - return - end - end - -- recursion abort condition - if count > TRAVERSER_LIMIT then - --atdebug("Traverser hit counter at",adj_pos, adj_connid) - return true - end - -- continue traversing - local counter_hit = false - for nconnid, nconn in ipairs(next_conns) do - if adj_connid ~= nconnid then - counter_hit = counter_hit or traverser(found_tcbs, adj_pos, next_conns, nconnid, count + 1, brk_when_found_n) - if brk_when_found_n and #found_tcbs>=brk_when_found_n then - break - end - end - end - return counter_hit -end - - - --- Merges the TS with merge_id into root_id and then deletes merge_id -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. - - if not ildb.may_modify_ts(rts) then return false end - if not ildb.may_modify_ts(mts) then return false end - - -- cobble together the list of TCBs - for _, msigd in ipairs(mts.tc_breaks) do - local tcbs = ildb.get_tcbs(msigd) - if tcbs then - insert_sigd_nodouble(rts.tc_breaks, msigd) - tcbs.ts_id = root_id - end - advtrains.interlocking.show_tcb_marker(msigd.p) - end - -- done - track_sections[merge_id] = nil -end - -local lntrans = { "A", "B" } -local function sigd_to_string(sigd) - return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] -end - --- Check for near TCBs and connect to their TS if they have one, and syncs their data. -function ildb.sync_tcb_neighbors(pos, connid) - local found_tcbs = { {p = pos, s = connid} } - local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - if not node_ok then - atwarn("update_tcb_neighbors but node is NOK: "..minetest.pos_to_string(pos)) - return - end - - --atdebug("Traversing from ",pos, connid) - local counter_hit = traverser(found_tcbs, pos, conns, connid, 0) - - local ts_id - local list_eoi = {} - local list_ok = {} - local list_mismatch = {} - local ts_to_merge = {} - - for idx, sigd in pairs(found_tcbs) do - local tcbs = ildb.get_tcbs(sigd) - if not tcbs.ts_id then - --atdebug("Sync: put",sigd_to_string(sigd),"into list_eoi") - table.insert(list_eoi, sigd) - elseif not ts_id and tcbs.ts_id then - if not ildb.get_ts(tcbs.ts_id) then - atwarn("Track section database is inconsistent, there's no TS with ID=",tcbs.ts_id) - tcbs.ts_id = nil - table.insert(list_eoi, sigd) - else - --atdebug("Sync: put",sigd_to_string(sigd),"into list_ok") - ts_id = tcbs.ts_id - table.insert(list_ok, sigd) - end - elseif ts_id and tcbs.ts_id and tcbs.ts_id ~= ts_id then - atwarn("Track section database is inconsistent, sections share track!") - atwarn("Merging",tcbs.ts_id,"into",ts_id,".") - table.insert(list_mismatch, sigd) - table.insert(ts_to_merge, tcbs.ts_id) - end - end - if ts_id then - local ts = ildb.get_ts(ts_id) - for _, sigd in ipairs(list_eoi) do - local tcbs = ildb.get_tcbs(sigd) - tcbs.ts_id = ts_id - table.insert(ts.tc_breaks, sigd) - advtrains.interlocking.show_tcb_marker(sigd.p) - end - for _, mts in ipairs(ts_to_merge) do - merge_ts(ts_id, mts) - end - end -end - -function ildb.link_track_sections(merge_id, root_id) - if merge_id == root_id then - return - end - merge_ts(root_id, merge_id) -end - -function ildb.remove_from_interlocking(sigd) - local tcbs = ildb.get_tcbs(sigd) - if not ildb.may_modify_tcbs(tcbs) then return false end - - if tcbs.ts_id then - local tsid = tcbs.ts_id - local ts = ildb.get_ts(tsid) - if not ts then - tcbs.ts_id = nil - return true - end - - -- remove entry from the list - local idx = 1 - while idx <= #ts.tc_breaks do - local cmp = ts.tc_breaks[idx] - if sigd_equal(sigd, cmp) then - table.remove(ts.tc_breaks, idx) - else - idx = idx + 1 - end - end - tcbs.ts_id = nil - - --ildb.sync_tcb_neighbors(sigd.p, sigd.s) - - if #ts.tc_breaks == 0 then - track_sections[tsid] = nil - end - end - advtrains.interlocking.show_tcb_marker(sigd.p) - if tcbs.signal then - return false - end - return true -end - -function ildb.remove_tcb(pos) - local pts = advtrains.roundfloorpts(pos) - if not track_circuit_breaks[pts] then return end - for connid=1,2 do - if not ildb.remove_from_interlocking({p=pos, s=connid}) then - return false - end - end - track_circuit_breaks[pts] = nil - return true -end - -function ildb.dissolve_ts(ts_id) - local ts = ildb.get_ts(ts_id) - if not ildb.may_modify_ts(ts) then return false end - local tcbr = advtrains.merge_tables(ts.tc_breaks) - for _,sigd in ipairs(tcbr) do - ildb.remove_from_interlocking(sigd) - end - -- Note: ts gets removed in the moment of the removal of the last TCB. - return true -end - --- Returns true if it is allowed to modify any property of a track section, such as --- - removing TCBs --- - merging and dissolving sections --- As of now the action will be denied if a route is set or if a train is in the section. -function ildb.may_modify_ts(ts) - if ts.route or ts.route_post or (ts.trains and #ts.trains>0) then - return false - end - return true -end - - -function ildb.may_modify_tcbs(tcbs) - if tcbs.ts_id then - local ts = ildb.get_ts(tcbs.ts_id) - if ts and not ildb.may_modify_ts(ts) then - return false - end - end - return true -end - --- Utilize the traverser to find the track section at the specified position --- Returns: --- ts_id, origin - the first found ts and the sigd of the found tcb --- nil - there were no TCBs in TRAVERSER_MAX range of the position --- false - the first found TCB stated End-Of-Interlocking, or track ends were reached -function ildb.get_ts_at_pos(pos) - local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - if not node_ok then - error("get_ts_at_pos but node is NOK: "..minetest.pos_to_string(pos)) - end - local limit_hit = false - local found_tcbs = {} - for connid, conn in ipairs(conns) do -- Note: a breadth-first-search would be better for performance - limit_hit = limit_hit or traverser(found_tcbs, pos, conns, connid, 0, 1) - if #found_tcbs >= 1 then - local tcbs = ildb.get_tcbs(found_tcbs[1]) - local ts - if tcbs.ts_id then - return tcbs.ts_id, found_tcbs[1] - else - return false - end - end - end - if limit_hit then - -- there was at least one limit hit - return nil - else - -- all traverser ends were track ends - return false - end -end - - --- returns the sigd the signal at pos belongs to, if this is known -function ildb.get_sigd_for_signal(pos) - local pts = advtrains.roundfloorpts(pos) - local sigd = signal_assignments[pts] - if sigd then - if not ildb.get_tcbs(sigd) then - signal_assignments[pts] = nil - return nil - end - return sigd - end - return nil -end -function ildb.set_sigd_for_signal(pos, sigd) - local pts = advtrains.roundfloorpts(pos) - signal_assignments[pts] = sigd -end - --- checks if there's any influence point set to this position -function ildb.is_ip_at(pos) - local pts = advtrains.roundfloorpts(pos) - if influence_points[pts] then - return true - end - return false -end - --- checks if a signal is influencing here -function ildb.get_ip_signal(pts, connid) - if influence_points[pts] then - return influence_points[pts][connid] - end -end - --- Tries to get aspect to obey here, if there --- is a signal ip at this location --- auto-clears invalid assignments -function ildb.get_ip_signal_asp(pts, connid) - local p = ildb.get_ip_signal(pts, connid) - if p then - local asp = advtrains.interlocking.signal_get_aspect(p) - if not asp then - atlog("Clearing orphaned signal influence point", pts, "/", connid) - ildb.clear_ip_signal(pts, connid) - return nil - end - return asp, p - end - return nil -end - --- set signal assignment. -function ildb.set_ip_signal(pts, connid, spos) - ildb.clear_ip_by_signalpos(spos) - if not influence_points[pts] then - influence_points[pts] = {} - end - influence_points[pts][connid] = spos -end --- clear signal assignment. -function ildb.clear_ip_signal(pts, connid) - influence_points[pts][connid] = nil - for _,_ in pairs(influence_points[pts]) do - return - end - influence_points[pts] = nil -end - -function ildb.get_ip_by_signalpos(spos) - for pts,tab in pairs(influence_points) do - for connid,pos in pairs(tab) do - if vector.equals(pos, spos) then - return pts, connid - end - end - end -end --- clear signal assignment given the signal position -function ildb.clear_ip_by_signalpos(spos) - local pts, connid = ildb.get_ip_by_signalpos(spos) - if pts then ildb.clear_ip_signal(pts, connid) end -end - - -advtrains.interlocking.db = ildb - - - - diff --git a/advtrains_interlocking/demosignals.lua b/advtrains_interlocking/demosignals.lua deleted file mode 100644 index ab7a8b6..0000000 --- a/advtrains_interlocking/demosignals.lua +++ /dev/null @@ -1,111 +0,0 @@ --- Demonstration signals --- Those can display the 3 main aspects of Ks signals - --- Note that the group value of advtrains_signal is 2, which means "step 2 of signal capabilities" --- advtrains_signal=1 is meant for signals that do not implement set_aspect. - - -local setaspect = function(pos, node, asp) - if not asp.main.free then - advtrains.ndb.swap_node(pos, {name="advtrains_interlocking:ds_danger"}) - else - if asp.dst.free and asp.main.speed == -1 then - advtrains.ndb.swap_node(pos, {name="advtrains_interlocking:ds_free"}) - else - advtrains.ndb.swap_node(pos, {name="advtrains_interlocking:ds_slow"}) - end - end - local meta = minetest.get_meta(pos) - if meta then - meta:set_string("infotext", minetest.serialize(asp)) - end -end - -local suppasp = { - main = { - free = nil, - speed = {6, -1}, - }, - dst = { - free = nil, - speed = nil, - }, - shunt = { - free = false, - proceed_as_main = true, - }, - info = { - call_on = false, - dead_end = false, - w_speed = nil, - } -} - -minetest.register_node("advtrains_interlocking:ds_danger", { - description = "Demo signal at Danger", - tiles = {"at_il_signal_asp_danger.png"}, - groups = { - cracky = 3, - advtrains_signal = 2, - save_in_at_nodedb = 1, - }, - sounds = default.node_sound_stone_defaults(), - advtrains = { - set_aspect = setaspect, - supported_aspects = suppasp, - get_aspect = function(pos, node) - return advtrains.interlocking.DANGER - end, - }, - on_rightclick = advtrains.interlocking.signal_rc_handler, - can_dig = advtrains.interlocking.signal_can_dig, -}) -minetest.register_node("advtrains_interlocking:ds_free", { - description = "Demo signal at Free", - tiles = {"at_il_signal_asp_free.png"}, - groups = { - cracky = 3, - advtrains_signal = 2, - save_in_at_nodedb = 1, - }, - sounds = default.node_sound_stone_defaults(), - advtrains = { - set_aspect = setaspect, - supported_aspects = suppasp, - get_aspect = function(pos, node) - return { - main = { - free = true, - speed = -1, - } - } - end, - }, - on_rightclick = advtrains.interlocking.signal_rc_handler, - can_dig = advtrains.interlocking.signal_can_dig, -}) -minetest.register_node("advtrains_interlocking:ds_slow", { - description = "Demo signal at Slow", - tiles = {"at_il_signal_asp_slow.png"}, - groups = { - cracky = 3, - advtrains_signal = 2, - save_in_at_nodedb = 1, - }, - sounds = default.node_sound_stone_defaults(), - advtrains = { - set_aspect = setaspect, - supported_aspects = suppasp, - get_aspect = function(pos, node) - return { - main = { - free = true, - speed = 6, - } - } - end, - }, - on_rightclick = advtrains.interlocking.signal_rc_handler, - can_dig = advtrains.interlocking.signal_can_dig, -}) - diff --git a/advtrains_interlocking/depends.txt b/advtrains_interlocking/depends.txt deleted file mode 100644 index fdf6b17..0000000 --- a/advtrains_interlocking/depends.txt +++ /dev/null @@ -1,2 +0,0 @@ -advtrains -advtrains_train_track?
\ No newline at end of file diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua deleted file mode 100644 index a2f5882..0000000 --- a/advtrains_interlocking/init.lua +++ /dev/null @@ -1,30 +0,0 @@ --- Advtrains interlocking system --- See database.lua for a detailed explanation - -advtrains.interlocking = {} - -advtrains.SHUNT_SPEED_MAX = 6 - -function advtrains.interlocking.sigd_equal(sigd, cmp) - return vector.equals(sigd.p, cmp.p) and sigd.s==cmp.s -end - - -local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM - -dofile(modpath.."database.lua") -dofile(modpath.."signal_api.lua") -dofile(modpath.."demosignals.lua") -dofile(modpath.."train_sections.lua") -dofile(modpath.."route_prog.lua") -dofile(modpath.."routesetting.lua") -dofile(modpath.."tcb_ts_ui.lua") -dofile(modpath.."route_ui.lua") -dofile(modpath.."tool.lua") - -dofile(modpath.."approach.lua") -dofile(modpath.."ars.lua") -dofile(modpath.."tsr_rail.lua") - - -minetest.register_privilege("interlocking", {description = "Can set up track sections, routes and signals.", give_to_singleplayer = true}) diff --git a/advtrains_interlocking/models/at_il_tcb_node.obj b/advtrains_interlocking/models/at_il_tcb_node.obj deleted file mode 100644 index bb6aab5..0000000 --- a/advtrains_interlocking/models/at_il_tcb_node.obj +++ /dev/null @@ -1,248 +0,0 @@ -# Blender v2.76 (sub 0) OBJ File: '' -# www.blender.org -mtllib at_il_tcb_node.mtl -o Cube -v 0.038370 -0.500000 -0.038370 -v 0.038370 -0.500000 0.038370 -v -0.038370 -0.500000 0.038370 -v -0.038370 -0.500000 -0.038370 -v 0.038370 0.098086 -0.038370 -v 0.038370 0.098086 0.038370 -v -0.038370 0.098086 0.038370 -v -0.038370 0.098086 -0.038370 -v -0.182395 0.065479 0.099357 -v -0.182395 0.182395 0.099357 -v -0.182395 0.065479 -0.171034 -v -0.182395 0.182395 -0.171034 -v 0.182395 0.065479 0.099357 -v 0.182395 0.182395 0.099357 -v 0.182395 0.065479 -0.171034 -v 0.182395 0.182395 -0.171034 -v -0.112374 0.070035 -0.139406 -v -0.112374 -0.500000 -0.139406 -v 0.112189 -0.500000 -0.139406 -v 0.112189 0.070035 -0.139406 -v 0.122883 -0.500000 -0.137278 -v 0.122883 0.070035 -0.137278 -v 0.131950 -0.500000 -0.131220 -v 0.131950 0.070035 -0.131220 -v 0.138008 -0.500000 -0.122154 -v 0.138008 0.070035 -0.122154 -v 0.140135 -0.500000 -0.111459 -v 0.140135 0.070035 -0.111459 -v 0.138008 -0.500000 -0.100765 -v 0.138008 0.070035 -0.100765 -v 0.131950 -0.500000 -0.091698 -v 0.131950 0.070035 -0.091698 -v 0.122883 -0.500000 -0.085640 -v 0.122883 0.070035 -0.085640 -v 0.112189 -0.500000 -0.083513 -v 0.112189 0.070035 -0.083513 -v 0.101494 -0.500000 -0.085640 -v 0.101494 0.070035 -0.085640 -v 0.092428 -0.500000 -0.091698 -v 0.092428 0.070035 -0.091698 -v 0.086370 -0.500000 -0.100765 -v 0.086370 0.070035 -0.100765 -v 0.084242 -0.500000 -0.111459 -v 0.084242 0.070035 -0.111459 -v 0.086370 -0.500000 -0.122154 -v 0.086370 0.070035 -0.122154 -v 0.092428 -0.500000 -0.131220 -v 0.092428 0.070035 -0.131220 -v 0.101494 -0.500000 -0.137278 -v 0.101494 0.070035 -0.137278 -v -0.101679 -0.500000 -0.137278 -v -0.101679 0.070035 -0.137278 -v -0.092613 -0.500000 -0.131220 -v -0.092613 0.070035 -0.131220 -v -0.086555 -0.500000 -0.122154 -v -0.086555 0.070035 -0.122154 -v -0.084428 -0.500000 -0.111459 -v -0.084428 0.070035 -0.111459 -v -0.086555 -0.500000 -0.100765 -v -0.086555 0.070035 -0.100765 -v -0.092613 -0.500000 -0.091698 -v -0.092613 0.070035 -0.091698 -v -0.101679 -0.500000 -0.085640 -v -0.101679 0.070035 -0.085640 -v -0.112374 -0.500000 -0.083513 -v -0.112374 0.070035 -0.083513 -v -0.123069 -0.500000 -0.085640 -v -0.123069 0.070035 -0.085640 -v -0.132135 -0.500000 -0.091698 -v -0.132135 0.070035 -0.091698 -v -0.138193 -0.500000 -0.100765 -v -0.138193 0.070035 -0.100765 -v -0.140320 -0.500000 -0.111459 -v -0.140320 0.070035 -0.111459 -v -0.138193 -0.500000 -0.122154 -v -0.138193 0.070035 -0.122154 -v -0.132135 -0.500000 -0.131220 -v -0.132135 0.070035 -0.131220 -v -0.123069 -0.500000 -0.137278 -v -0.123069 0.070035 -0.137278 -vt 0.876073 0.266665 -vt 0.876073 0.977812 -vt 0.784827 0.977812 -vt 0.784827 0.266665 -vt 0.693582 0.977812 -vt 0.693582 0.266665 -vt 0.602336 0.977812 -vt 0.602336 0.266665 -vt 0.967319 0.266665 -vt 0.967319 0.977812 -vt 0.147929 0.032040 -vt 0.469434 0.032040 -vt 0.469434 0.171057 -vt 0.147929 0.171057 -vt 0.903184 0.032040 -vt 0.903184 0.171057 -vt 0.147929 0.032751 -vt 0.469434 0.032751 -vt 0.469434 0.171768 -vt 0.147929 0.171768 -vt 0.903184 0.032751 -vt 0.903183 0.171768 -vt 0.263807 0.270252 -vt 0.585312 0.270252 -vt 0.585312 0.704001 -vt 0.263807 0.704001 -vt 0.584297 0.703059 -vt 0.262792 0.703059 -vt 0.262793 0.269309 -vt 0.584297 0.269309 -vt 0.108472 0.980897 -vt 0.108473 0.303114 -vt 0.121438 0.303114 -vt 0.121438 0.980897 -vt 0.081877 0.980125 -vt 0.081879 0.302342 -vt 0.094844 0.302342 -vt 0.094843 0.980125 -vt 0.095507 0.980897 -vt 0.095508 0.303114 -vt 0.107809 0.302342 -vt 0.107808 0.980125 -vt 0.082541 0.980897 -vt 0.082543 0.303114 -vt 0.120774 0.302342 -vt 0.120774 0.980125 -vt 0.069575 0.980897 -vt 0.069577 0.303114 -vt 0.133739 0.302342 -vt 0.133740 0.980125 -vt 0.056609 0.980897 -vt 0.056612 0.303114 -vt 0.146705 0.302342 -vt 0.146706 0.980125 -vt 0.043643 0.980897 -vt 0.043647 0.303114 -vt 0.159670 0.302342 -vt 0.159672 0.980125 -vt 0.030677 0.980897 -vt 0.030682 0.303113 -vt 0.172635 0.302342 -vt 0.172638 0.980125 -vt 0.017711 0.980897 -vt 0.017717 0.303113 -vt 0.185600 0.302342 -vt 0.185604 0.980125 -vt 0.212200 0.980896 -vt 0.212195 0.303113 -vt 0.225160 0.303113 -vt 0.225166 0.980896 -vt 0.198565 0.302342 -vt 0.198570 0.980125 -vt 0.199234 0.980897 -vt 0.199230 0.303114 -vt 0.211531 0.302342 -vt 0.211536 0.980125 -vt 0.186268 0.980897 -vt 0.186264 0.303114 -vt 0.224496 0.302342 -vt 0.224502 0.980125 -vt 0.173302 0.980897 -vt 0.173299 0.303114 -vt 0.017047 0.980125 -vt 0.017052 0.302342 -vt 0.030018 0.302342 -vt 0.030013 0.980125 -vt 0.134403 0.303114 -vt 0.134404 0.980897 -vt 0.160336 0.980897 -vt 0.160334 0.303114 -vt 0.042983 0.302342 -vt 0.042979 0.980125 -vt 0.147369 0.303114 -vt 0.147370 0.980897 -vt 0.055948 0.302342 -vt 0.055945 0.980125 -vt 0.068911 0.980125 -vt 0.068913 0.302342 -vn 1.000000 0.000000 0.000000 -vn -0.000000 -0.000000 1.000000 -vn -1.000000 -0.000000 -0.000000 -vn 0.000000 0.000000 -1.000000 -vn 0.000000 -1.000000 0.000000 -vn 0.000000 1.000000 0.000000 -vn -0.831500 0.000000 -0.555600 -vn 0.195100 0.000000 -0.980800 -vn -0.980800 0.000000 -0.195100 -vn 0.555600 0.000000 -0.831500 -vn -0.980800 0.000000 0.195100 -vn 0.831500 0.000000 -0.555600 -vn -0.831500 0.000000 0.555600 -vn 0.980800 0.000000 -0.195100 -vn -0.555600 0.000000 0.831500 -vn 0.980800 0.000000 0.195100 -vn -0.195100 0.000000 0.980800 -vn 0.831500 0.000000 0.555600 -vn 0.195100 0.000000 0.980800 -vn 0.555600 0.000000 0.831500 -vn -0.555600 0.000000 -0.831500 -vn -0.195100 0.000000 -0.980800 -usemtl Material -s off -f 1/1/1 5/2/1 6/3/1 2/4/1 -f 2/4/2 6/3/2 7/5/2 3/6/2 -f 3/6/3 7/5/3 8/7/3 4/8/3 -f 5/2/4 1/1/4 4/9/4 8/10/4 -f 10/11/3 12/12/3 11/13/3 9/14/3 -f 12/12/4 16/15/4 15/16/4 11/13/4 -f 16/17/1 14/18/1 13/19/1 15/20/1 -f 14/18/2 10/21/2 9/22/2 13/19/2 -f 9/23/5 11/24/5 15/25/5 13/26/5 -f 14/27/6 16/28/6 12/29/6 10/30/6 -f 75/31/7 76/32/7 78/33/7 77/34/7 -f 19/35/8 20/36/8 22/37/8 21/38/8 -f 73/39/9 74/40/9 76/32/9 75/31/9 -f 21/38/10 22/37/10 24/41/10 23/42/10 -f 71/43/11 72/44/11 74/40/11 73/39/11 -f 23/42/12 24/41/12 26/45/12 25/46/12 -f 69/47/13 70/48/13 72/44/13 71/43/13 -f 25/46/14 26/45/14 28/49/14 27/50/14 -f 67/51/15 68/52/15 70/48/15 69/47/15 -f 27/50/16 28/49/16 30/53/16 29/54/16 -f 65/55/17 66/56/17 68/52/17 67/51/17 -f 29/54/18 30/53/18 32/57/18 31/58/18 -f 63/59/19 64/60/19 66/56/19 65/55/19 -f 31/58/20 32/57/20 34/61/20 33/62/20 -f 61/63/20 62/64/20 64/60/20 63/59/20 -f 33/62/19 34/61/19 36/65/19 35/66/19 -f 59/67/18 60/68/18 62/69/18 61/70/18 -f 35/66/17 36/65/17 38/71/17 37/72/17 -f 57/73/16 58/74/16 60/68/16 59/67/16 -f 37/72/15 38/71/15 40/75/15 39/76/15 -f 55/77/14 56/78/14 58/74/14 57/73/14 -f 39/76/13 40/75/13 42/79/13 41/80/13 -f 53/81/12 54/82/12 56/78/12 55/77/12 -f 41/83/11 42/84/11 44/85/11 43/86/11 -f 77/34/21 78/33/21 80/87/21 79/88/21 -f 51/89/10 52/90/10 54/82/10 53/81/10 -f 43/86/9 44/85/9 46/91/9 45/92/9 -f 79/88/22 80/87/22 17/93/22 18/94/22 -f 18/94/8 17/93/8 52/90/8 51/89/8 -f 45/92/7 46/91/7 48/95/7 47/96/7 -f 49/97/22 50/98/22 20/36/22 19/35/22 -f 47/96/21 48/95/21 50/98/21 49/97/21 diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua deleted file mode 100644 index eadfd93..0000000 --- a/advtrains_interlocking/route_prog.lua +++ /dev/null @@ -1,548 +0,0 @@ --- Route programming system - ---[[ -Progamming routes: -1. Select "program new route" in the signalling dialog --> route_start marker will appear to designate route-program mode -2. Do those actions in any order: -A. punch a TCB marker node to proceed route along this TCB. This will only work if - this is actually a TCB bordering the current TS, and will place a - route_set marker and shift to the next TS -B. right-click a turnout to switch it (no impact to route programming -C. punch a turnout (or some other passive component) to fix its state (toggle) - for the route. A sprite telling "Route Fix" will show that fact. -3. To complete route setting, use the chat command '/at_program_route <route name>'. - The last punched TCB will get a 'route end' marker - The end of a route should be at another signal facing the same direction as the entrance signal, - however this is not enforced and left up to the signal engineer (the programmer) - -The route visualization will also be used to visualize routes after they have been programmed. -]]-- - - --- table with objectRefs -local markerent = {} - -minetest.register_entity("advtrains_interlocking:routemarker", { - visual = "mesh", - mesh = "trackplane.b3d", - textures = {"at_il_route_set.png"}, - collisionbox = {-1,-0.5,-1, 1,-0.4,1}, - visual_size = {x=10, y=10}, - on_punch = function(self) - self.object:remove() - end, - get_staticdata = function() return "STATIC" end, - on_activate = function(self, sdata) if sdata=="STATIC" then self.object:remove() end end, - static_save = false, -}) - - --- Spawn or update a route marker entity --- pos: position where this is going to be --- key: something unique to determine which entity to remove if this was set before --- img: texture -local function routemarker(context, pos, key, img, yaw, itex) - if not markerent[context] then - markerent[context] = {} - end - if markerent[context][key] then - markerent[context][key]:remove() - end - - local obj = minetest.add_entity(vector.add(pos, {x=0, y=0.3, z=0}), "advtrains_interlocking:routemarker") - if not obj then return end - obj:set_yaw(yaw) - obj:set_properties({ - infotext = itex, - textures = {img}, - }) - - markerent[context][key] = obj -end - -minetest.register_entity("advtrains_interlocking:routesprite", { - visual = "sprite", - textures = {"at_il_turnout_free.png"}, - collisionbox = {-0.2,-0.2,-0.2, 0.2,0.2,0.2}, - visual_size = {x=1, y=1}, - on_punch = function(self) - if self.callback then - self.callback() - end - self.object:remove() - end, - get_staticdata = function() return "STATIC" end, - on_activate = function(self, sdata) if sdata=="STATIC" then self.object:remove() end end, - static_save = false, -}) - - --- Spawn or update a route sprite entity --- pos: position where this is going to be --- key: something unique to determine which entity to remove if this was set before --- img: texture -local function routesprite(context, pos, key, img, itex, callback) - if not markerent[context] then - markerent[context] = {} - end - if markerent[context][key] then - markerent[context][key]:remove() - end - - local obj = minetest.add_entity(vector.add(pos, {x=0, y=0, z=0}), "advtrains_interlocking:routesprite") - if not obj then return end - obj:set_properties({ - infotext = itex, - textures = {img}, - }) - - if callback then - obj:get_luaentity().callback = callback - end - - markerent[context][key] = obj -end - ---[[ -Route definition: -route = { - name = <string> - [n] = { - next = <sigd>, -- of the next (note: next) TCB on the route - locks = {<pts> = "state"} -- route locks of this route segment - } - terminal = -} -The first item in the TCB path (namely i=0) is always the start signal of this route, -so this is left out. -All subsequent entries, starting from 1, contain: -- all route locks of the segment on TS between the (i-1). and the i. TCB -- the next TCB signal describer in proceeding direction of the route. -'Terminal' once again repeats the "next" entry of the last route segment. -It is needed for distant signal aspect determination. If it is not set, -the distant signal aspect is determined as DANGER. -]]-- - -local function chat(pname, message) - minetest.chat_send_player(pname, "[Route programming] "..message) -end -local function clear_lock(locks, pname, pts) - locks[pts] = nil - chat(pname, pts.." is no longer affected when this route is set.") -end - -local function otherside(s) - if s==1 then return 2 else return 1 end -end - -function advtrains.interlocking.clear_visu_context(context) - if not markerent[context] then return end - for key, obj in pairs(markerent[context]) do - obj:remove() - end - markerent[context] = nil -end - --- visualize route. 'context' is a string that identifies the context of this visualization --- e.g. prog_<player> or vis_<pts> for later visualizations --- last 2 parameters are only to be used in the context of route programming! -function advtrains.interlocking.visualize_route(origin, route, context, tmp_lcks, pname) - 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) - - local c_sigd = origin - for k,v in ipairs(route) do - c_sigd = v.next - -- display route path - -- Final "next" marker can be EOI, thus undefined. This is legitimate. - if c_sigd then - local yaw = 0 - local node_ok, conns, rhe = advtrains.get_rail_info_at(c_sigd.p, advtrains.all_tracktypes) - if node_ok then - yaw = advtrains.dir_to_angle(conns[c_sigd.s].c) - end - local img = "at_il_route_set.png" - if k==#route and not tmp_lcks then - img = "at_il_route_end.png" - end - routemarker(context, c_sigd.p, "rte"..k, img, yaw, route.name.." #"..k) - end - -- display locks - for pts, state in pairs(v.locks) do - local pos = minetest.string_to_pos(pts) - routesprite(context, pos, "fix"..k..pts, "at_il_route_lock.png", "Fixed in state '"..state.."' by route "..route.name.." until segment #"..k.." is freed.") - end - end - - -- The presence of tmp_lcks tells us that we are displaying during route programming. - if tmp_lcks then - -- display route end markers at appropriate places (check next TS, if it exists) - local terminal = c_sigd - if terminal then - local term_tcbs = advtrains.interlocking.db.get_tcbs(terminal) - if term_tcbs.ts_id then - local over_ts = advtrains.interlocking.db.get_ts(term_tcbs.ts_id) - for i, sigd in ipairs(over_ts.tc_breaks) do - if not vector.equals(sigd.p, terminal.p) then - local yaw = 0 - local node_ok, conns, rhe = advtrains.get_rail_info_at(sigd.p, advtrains.all_tracktypes) - if node_ok then - yaw = advtrains.dir_to_angle(conns[otherside(sigd.s)].c) - end - routemarker(context, sigd.p, "rteterm"..i, "at_il_route_end.png", yaw, route.name.." Terminal "..i) - end - end - end - end - -- display locks set by player - for pts, state in pairs(tmp_lcks) do - local pos = minetest.string_to_pos(pts) - routesprite(context, pos, "fixp"..pts, "at_il_route_lock_edit.png", "Fixed in state '"..state.."' by route "..route.name.." (punch to unfix)", - function() clear_lock(tmp_lcks, pname, pts) end) - end - end -end - - -local player_rte_prog = {} - -function advtrains.interlocking.init_route_prog(pname, sigd) - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - player_rte_prog[pname] = { - origin = sigd, - route = { - name = "PROG["..pname.."]", - }, - tmp_lcks = {}, - } - advtrains.interlocking.visualize_route(sigd, player_rte_prog[pname].route, "prog_"..pname, player_rte_prog[pname].tmp_lcks, pname) - minetest.chat_send_player(pname, "Route programming mode active. Punch TCBs to add route segments, punch turnouts to lock them.") -end - -local function get_last_route_item(origin, route) - if #route == 0 then - return origin - end - return route[#route].next -end - -local function do_advance_route(pname, rp, sigd, tsname) - table.insert(rp.route, {next = sigd, locks = rp.tmp_lcks}) - rp.tmp_lcks = {} - chat(pname, "Added track section '"..tsname.."' to the route.") -end - -local function finishrpform(pname) - local rp = player_rte_prog[pname] - if not rp then return end - - local form = "size[7,6]label[0.5,0.5;Finish programming route]" - local terminal = get_last_route_item(rp.origin, rp.route) - if terminal then - local term_tcbs = advtrains.interlocking.db.get_tcbs(terminal) - - if term_tcbs.signal then - form = form .. "label[0.5,1.5;Route ends at signal:]" - form = form .. "label[0.5,2 ;"..term_tcbs.signal_name.."]" - else - form = form .. "label[0.5,1.5;WARNING: Route does not end at a signal.]" - form = form .. "label[0.5,2 ;Routes should in most cases end at signals.]" - form = form .. "label[0.5,2.5;Cancel if you are unsure!]" - end - else - form = form .. "label[0.5,1.5;Route leads into]" - form = form .. "label[0.5,2 ;non-interlocked area]" - end - form = form.."field[0.8,3.5;5.2,1;name;Enter Route Name;]" - form = form.."button_exit[0.5,4.5; 5,1;save;Save Route]" - - - minetest.show_formspec(pname, "at_il_routepf", form) -end - - -local function check_advance_valid(tcbpos, rp) - -- track circuit break, try to advance route over it - local lri = get_last_route_item(rp.origin, rp.route) - if not lri then - return false, false - end - - local is_endpoint = false - - local this_sigd, this_ts, adv_side - - if vector.equals(lri.p, tcbpos) then - -- If the player just punched the last TCB again, it's of course possible to - -- finish the route here (although it can't be advanced by here. - -- Fun fact: you can now program routes that end exactly where they begin :) - is_endpoint = true - this_sigd = lri - else - -- else, we need to check whether this TS actually borders - local start_tcbs = advtrains.interlocking.db.get_tcbs(lri) - if not start_tcbs.ts_id then - return false, false - end - - this_ts = advtrains.interlocking.db.get_ts(start_tcbs.ts_id) - for _,sigd in ipairs(this_ts.tc_breaks) do - if vector.equals(sigd.p, tcbpos) then - adv_side = otherside(sigd.s) - end - end - if not adv_side then - -- this TCB is not bordering to the section - return false, false - end - this_sigd = {p=tcbpos, s=adv_side} - end - - -- check whether the ts at the other end is capable of "end over" - local adv_tcbs = advtrains.interlocking.db.get_tcbs(this_sigd) - local next_tsid = adv_tcbs.ts_id - local can_over, over_ts, next_tc_bs = false, nil, nil - local cannotover_rsn = "Next section is diverging (>2 TCBs)" - if next_tsid then - -- you may not advance over EOI. While this is technically possible, - -- in practise this just enters an unnecessary extra empty route item. - over_ts = advtrains.interlocking.db.get_ts(adv_tcbs.ts_id) - next_tc_bs = over_ts.tc_breaks - can_over = #next_tc_bs <= 2 - else - cannotover_rsn = "End of interlocking" - end - - local over_sigd = nil - if can_over then - if next_tc_bs and #next_tc_bs == 2 then - local sdt - if vector.equals(next_tc_bs[1].p, tcbpos) then - sdt = next_tc_bs[2] - end - if vector.equals(next_tc_bs[2].p, tcbpos) then - sdt = next_tc_bs[1] - end - if not sdt then - error("Inconsistency: "..dump(next_ts)) - end - -- swap TCB direction - over_sigd = {p = sdt.p, s = otherside(sdt.s) } - end - end - - return is_endpoint, true, this_sigd, this_ts, can_over, over_ts, over_sigd, cannotover_rsn -end - -local function show_routing_form(pname, tcbpos, message) - - local rp = player_rte_prog[pname] - - if not rp then return end - - local is_endpoint, advance_valid, this_sigd, this_ts, can_over, over_ts, over_sigd, cannotover_rsn = check_advance_valid(tcbpos, rp) - - -- at this place, advance_valid shows whether the current route can be advanced - -- over this TCB. - -- If it can: - -- Advance over (continue programming) - -- End here - -- Advance and end (only <=2 TCBs, terminal signal needs to be known) - -- if not: - -- show nothing at all - -- In all cases, Discard and Backtrack buttons needed. - - local form = "size[7,9.5]label[0.5,0.5;Advance/Complete Route]" - if message then - form = form .. "label[0.5,1;"..message.."]" - end - - if advance_valid and not is_endpoint then - form = form.. "label[0.5,1.8;Advance to next route section]" - form = form.."image_button[0.5,2.2; 5,1;at_il_routep_advance.png;advance;]" - - form = form.. "label[0.5,3.5;-------------------------]" - else - form = form.. "label[0.5,2.3;This TCB is not suitable as]" - form = form.. "label[0.5,2.8;route continuation.]" - end - if advance_valid or is_endpoint then - form = form.. "label[0.5,3.8;Finish route HERE]" - form = form.."image_button[0.5, 4.2; 5,1;at_il_routep_end_here.png;endhere;]" - if can_over then - form = form.. "label[0.5,5.3;Finish route at end of NEXT section]" - form = form.."image_button[0.5,5.7; 5,1;at_il_routep_end_over.png;endover;]" - else - form = form.. "label[0.5,5.3;Advancing over next section is]" - form = form.. "label[0.5,5.8;impossible at this place.]" - if cannotover_rsn then - form = form.. "label[0.5,6.3;"..cannotover_rsn.."]" - end - end - end - - form = form.. "label[0.5,7;-------------------------]" - if #rp.route > 0 then - form = form.."button[0.5,7.4; 5,1;retract;Step back one section]" - end - form = form.."button[0.5,8.4; 5,1;cancel;Cancel route programming]" - - minetest.show_formspec(pname, "at_il_rprog_"..minetest.pos_to_string(tcbpos), form) -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - - local tcbpts = string.match(formname, "^at_il_rprog_([^_]+)$") - local tcbpos - if tcbpts then - tcbpos = minetest.string_to_pos(tcbpts) - end - if tcbpos then - -- RPROG form - local rp = player_rte_prog[pname] - if not rp then - minetest.close_formspec(pname, formname) - return - end - - local is_endpoint, advance_valid, this_sigd, this_ts, can_over, over_ts, over_sigd = check_advance_valid(tcbpos, rp) - - if advance_valid then - if fields.advance then - -- advance route - if not is_endpoint then - do_advance_route(pname, rp, this_sigd, this_ts.name) - end - end - if fields.endhere then - if not is_endpoint then - do_advance_route(pname, rp, this_sigd, this_ts.name) - end - finishrpform(pname) - end - if can_over and fields.endover then - if not is_endpoint then - do_advance_route(pname, rp, this_sigd, this_ts.name) - end - do_advance_route(pname, rp, over_sigd, over_ts and over_ts.name or "--EOI--") - finishrpform(pname) - end - end - if fields.retract then - if #rp.route <= 0 then - minetest.close_formspec(pname, formname) - return - end - rp.tmp_locks = rp.route[#rp.route].locks - rp.route[#rp.route] = nil - chat(pname, "Route section "..(#rp.route+1).." removed.") - end - if fields.cancel then - player_rte_prog[pname] = nil - advtrains.interlocking.clear_visu_context("prog_"..pname) - chat(pname, "Route discarded.") - minetest.close_formspec(pname, formname) - return - end - - advtrains.interlocking.visualize_route(rp.origin, rp.route, "prog_"..pname, rp.tmp_lcks, pname) - minetest.close_formspec(pname, formname) - return - end - - if formname == "at_il_routepf" then - if not fields.save or not fields.name then return end - if fields.name == "" then - -- show form again - finishrpform(pname) - return - end - - local rp = player_rte_prog[pname] - if rp then - if #rp.route <= 0 then - chat(pname, "Cannot program route without a target") - return - end - - local tcbs = advtrains.interlocking.db.get_tcbs(rp.origin) - if not tcbs then - chat(pname, "The origin TCB has become unknown during programming. Try again.") - return - end - - local terminal = get_last_route_item(rp.origin, rp.route) - rp.route.terminal = terminal - rp.route.name = fields.name - - table.insert(tcbs.routes, rp.route) - - advtrains.interlocking.clear_visu_context("prog_"..pname) - player_rte_prog[pname] = nil - chat(pname, "Successfully programmed route.") - - advtrains.interlocking.show_route_edit_form(pname, rp.origin, #tcbs.routes) - return - end - end -end) - - --- Central route programming punch callback -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 rp = player_rte_prog[pname] - if rp then - -- determine what the punched node is - if minetest.get_item_group(node.name, "at_il_track_circuit_break") >= 1 then - -- get position of the assigned tcb - local meta = minetest.get_meta(pos) - local tcbpts = meta:get_string("tcb_pos") - if tcbpts == "" then - chat(pname, "This TCB is unconfigured, you first need to assign it to a rail") - return - end - local tcbpos = minetest.string_to_pos(tcbpts) - - -- show formspec - - show_routing_form(pname, tcbpos) - - advtrains.interlocking.visualize_route(rp.origin, rp.route, "prog_"..pname, rp.tmp_lcks, pname) - - return - end - if advtrains.is_passive(pos) then - local pts = advtrains.roundfloorpts(pos) - if rp.tmp_lcks[pts] then - clear_lock(rp.tmp_lcks, pname, pts) - else - local state = advtrains.getstate(pos) - rp.tmp_lcks[pts] = state - chat(pname, pts.." is held in "..state.." position when this route is set and freed ") - end - advtrains.interlocking.visualize_route(rp.origin, rp.route, "prog_"..pname, rp.tmp_lcks, pname) - return - end - - end -end) - - ---TODO on route setting --- routes should end at signals. complete route setting by punching a signal, and command as exceptional route completion --- Create simpler way to advance a route to the next tcb/signal on simple sections without turnouts diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua deleted file mode 100644 index 71fed09..0000000 --- a/advtrains_interlocking/route_ui.lua +++ /dev/null @@ -1,152 +0,0 @@ --- route_ui.lua --- User interface for showing and editing routes - -local atil = advtrains.interlocking -local ildb = atil.db - --- TODO duplicate -local lntrans = { "A", "B" } -local function sigd_to_string(sigd) - return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] -end - - - -function atil.show_route_edit_form(pname, sigd, routeid) - - if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - - local tcbs = atil.db.get_tcbs(sigd) - if not tcbs then return end - local route = tcbs.routes[routeid] - if not route then return end - - local form = "size[9,10]label[0.5,0.2;Route overview]" - form = form.."field[0.8,1.2;5.2,1;name;Route name;"..minetest.formspec_escape(route.name).."]" - form = form.."button[5.5,0.9;1,1;setname;Set]" - - -- construct textlist for route information - local tab = {} - local function itab(t) - tab[#tab+1] = minetest.formspec_escape(string.gsub(t, ",", " ")) - end - itab("TCB "..sigd_to_string(sigd).." ("..tcbs.signal_name..") Route #"..routeid) - - -- this code is partially copy-pasted from routesetting.lua - -- we start at the tc designated by signal - local c_sigd = sigd - local i = 1 - local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp - while c_sigd and i<=#route do - c_tcbs = ildb.get_tcbs(c_sigd) - if not c_tcbs then - itab("-!- No TCBS at "..sigd_to_string(c_sigd)..". Please reconfigure route!") - break - end - c_ts_id = c_tcbs.ts_id - if not c_ts_id then - itab("-!- No track section adjacent to "..sigd_to_string(c_sigd)..". Please reconfigure route!") - break - end - c_ts = ildb.get_ts(c_ts_id) - - c_rseg = route[i] - c_lckp = {} - - itab(""..i.." Entry "..sigd_to_string(c_sigd).." -> Sec. "..(c_ts and c_ts.name or "-").." -> Exit "..(c_rseg.next and sigd_to_string(c_rseg.next) or "END")) - - if c_rseg.locks then - for pts, state in pairs(c_rseg.locks) do - - local pos = minetest.string_to_pos(pts) - itab(" Lock: "..pts.." -> "..state) - if not advtrains.is_passive(pos) then - itab("-!- No passive component at "..pts..". Please reconfigure route!") - break - end - end - end - -- advance - c_sigd = c_rseg.next - i = i + 1 - end - if c_sigd then - local e_tcbs = ildb.get_tcbs(c_sigd) - itab("Route end: "..sigd_to_string(c_sigd).." ("..(e_tcbs and e_tcbs.signal_name or "-")..")") - else - itab("Route ends on dead-end") - end - - form = form.."textlist[0.5,2;7,4;rtelog;"..table.concat(tab, ",").."]" - - form = form.."button[0.5,6;2,1;back;<<< Back to signal]" - form = form.."button[3.5,6;2,1;aspect;Signal Aspect]" - form = form.."button[5.5,6;2,1;delete;Delete Route]" - - --atdebug(route.ars) - form = form.."textarea[1,7.3;5.2,3;ars;ARS Rule List;"..atil.ars_to_text(route.ars).."]" - form = form.."button[6,7.7;1,1;savears;Save]" - - minetest.show_formspec(pname, "at_il_routeedit_"..minetest.pos_to_string(sigd.p).."_"..sigd.s.."_"..routeid, form) - -end - - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then - return - end - - local pts, connids, routeids = string.match(formname, "^at_il_routeedit_([^_]+)_(%d)_(%d+)$") - local pos, connid, routeid - if pts then - pos = minetest.string_to_pos(pts) - connid = tonumber(connids) - routeid = tonumber(routeids) - if not connid or connid<1 or connid>2 then return end - if not routeid then return end - end - if pos and connid and routeid and not fields.quit then - local sigd = {p=pos, s=connid} - local tcbs = ildb.get_tcbs(sigd) - if not tcbs then return end - local route = tcbs.routes[routeid] - if not route then return end - - if fields.setname and fields.name then - route.name = fields.name - end - - if fields.aspect then - local suppasp = advtrains.interlocking.signal_get_supported_aspects(tcbs.signal) - - local callback = function(pname, asp) - route.aspect = asp - advtrains.interlocking.show_route_edit_form(pname, sigd, routeid) - end - - advtrains.interlocking.show_signal_aspect_selector(pname, suppasp, route.name, callback, route.aspect) - return - end - if fields.delete then - -- if something set the route in the meantime, make sure this doesn't break. - atil.route.update_route(sigd, tcbs, nil, true) - table.remove(tcbs.routes, routeid) - advtrains.interlocking.show_signalling_form(sigd, pname) - end - - if fields.ars and fields.savears then - route.ars = atil.text_to_ars(fields.ars) - --atdebug(route.ars) - end - - if fields.back then - advtrains.interlocking.show_signalling_form(sigd, pname) - end - - end -end) diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua deleted file mode 100644 index 575b053..0000000 --- a/advtrains_interlocking/routesetting.lua +++ /dev/null @@ -1,357 +0,0 @@ --- Setting and clearing routes - --- TODO duplicate -local lntrans = { "A", "B" } -local function sigd_to_string(sigd) - return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] -end - -local asp_generic_free = { - main = { - free = true, - speed = -1, - }, - shunt = { - free = false, - }, - dst = { - free = true, - speed = -1, - }, - info = {} -} - -local ildb = advtrains.interlocking.db -local ilrs = {} - -local sigd_equal = advtrains.interlocking.sigd_equal - --- table containing locked points --- also manual locks (maintenance a.s.o.) are recorded here --- [pts] = { --- [n] = { [by = <ts_id>], rsn = <human-readable text>, [origin = <sigd>] } --- } -ilrs.rte_locks = {} -ilrs.rte_callbacks = { - ts = {}, - lck = {} -} - - --- main route setting. First checks if everything can be set as designated, --- then (if "try" is not set) actually sets it --- returns: --- true - route can be/was successfully set --- false, message, cbts, cblk - something went wrong, what is contained in the message. --- cbts: the ts id of the conflicting ts, cblk: the pts of the conflicting component -function ilrs.set_route(signal, route, try) - if not try then - local tsuc, trsn, cbts, cblk = ilrs.set_route(signal, route, true) - if not tsuc then - return false, trsn, cbts, cblk - end - end - - - -- we start at the tc designated by signal - local c_sigd = signal - local first = true - local i = 1 - local rtename = route.name - local signalname = ildb.get_tcbs(signal).signal_name - local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp - while c_sigd and i<=#route do - c_tcbs = ildb.get_tcbs(c_sigd) - if not c_tcbs then - if not try then atwarn("Did not find TCBS",c_sigd,"while setting route",rtename,"of",signal) end - return false, "No TCB found at "..sigd_to_string(c_sigd)..". Please reconfigure route!" - end - c_ts_id = c_tcbs.ts_id - if not c_ts_id then - if not try then atwarn("Encountered End-Of-Interlocking while setting route",rtename,"of",signal) end - return false, "No track section adjacent to "..sigd_to_string(c_sigd)..". Please reconfigure route!" - end - c_ts = ildb.get_ts(c_ts_id) - c_rseg = route[i] - c_lckp = {} - - if c_ts.route then - if not try then atwarn("Encountered ts lock during a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end - return false, "Section '"..c_ts.name.."' already has route set from "..sigd_to_string(c_ts.route.origin)..":\n"..c_ts.route.rsn, c_ts_id, nil - end - if c_ts.trains and #c_ts.trains>0 then - if not try then atwarn("Encountered ts occupied during a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end - return false, "Section '"..c_ts.name.."' is occupied!", c_ts_id, nil - end - - for pts, state in pairs(c_rseg.locks) do - local confl = ilrs.has_route_lock(pts, state) - - local pos = minetest.string_to_pos(pts) - if advtrains.is_passive(pos) then - local cstate = advtrains.getstate(pos) - if cstate ~= state then - local confl = ilrs.has_route_lock(pts) - if confl then - if not try then atwarn("Encountered route lock while a real run of routesetting routine, at position",pts,"while setting route",rtename,"of",signal) end - return false, "Lock conflict at "..pts..", Held locked by:\n"..confl, nil, pts - elseif not try then - advtrains.setstate(pos, state) - end - end - if not try then - ilrs.add_route_lock(pts, c_ts_id, "Route '"..rtename.."' from signal '"..signalname.."'", signal) - c_lckp[#c_lckp+1] = pts - end - else - if not try then atwarn("Encountered route lock misconfiguration (no passive component) while a real run of routesetting routine, at position",pts,"while setting route",rtename,"of",signal) end - return false, "No passive component at "..pts..". Please reconfigure route!" - end - end - -- reserve ts and write locks - if not try then - local nvar = c_rseg.next - if not route[i+1] then - -- We shouldn't use the "next" value of the final route segment, because this can lead to accidental route-cancelling of already set routes from another signal. - nvar = nil - end - c_ts.route = { - origin = signal, - entry = c_sigd, - rsn = "Route '"..rtename.."' from signal '"..signalname.."', segment #"..i, - first = first, - } - c_ts.route_post = { - locks = c_lckp, - next = nvar, - } - if c_tcbs.signal then - c_tcbs.route_committed = true - c_tcbs.aspect = route.aspect or asp_generic_free - c_tcbs.route_origin = signal - advtrains.interlocking.update_signal_aspect(c_tcbs) - end - end - -- advance - first = nil - c_sigd = c_rseg.next - i = i + 1 - end - - return true -end - --- Checks whether there is a route lock that prohibits setting the component --- to the wanted state. returns string with reasons on conflict -function ilrs.has_route_lock(pts) - -- look this up - local e = ilrs.rte_locks[pts] - if not e then return nil - elseif #e==0 then - ilrs.rte_locks[pts] = nil - return nil - end - local txts = {} - for _, ent in ipairs(e) do - txts[#txts+1] = ent.rsn - end - return table.concat(txts, "\n") -end - --- adds route lock for position -function ilrs.add_route_lock(pts, ts, rsn, origin) - ilrs.free_route_locks_indiv(pts, ts, true) - local elm = {by=ts, rsn=rsn, origin=origin} - if not ilrs.rte_locks[pts] then - ilrs.rte_locks[pts] = { elm } - else - table.insert(ilrs.rte_locks[pts], elm) - end -end - --- adds route lock for position -function ilrs.add_manual_route_lock(pts, rsn) - local elm = {rsn=rsn} - if not ilrs.rte_locks[pts] then - ilrs.rte_locks[pts] = { elm } - else - table.insert(ilrs.rte_locks[pts], elm) - end -end - --- frees route locking for all points (components) that were set by this ts -function ilrs.free_route_locks(ts, lcks, nocallbacks) - for _,pts in pairs(lcks) do - ilrs.free_route_locks_indiv(pts, ts, nocallbacks) - end -end - -function ilrs.free_route_locks_indiv(pts, ts, nocallbacks) - local e = ilrs.rte_locks[pts] - if not e then return nil - elseif #e==0 then - ilrs.rte_locks[pts] = nil - return nil - end - local i = 1 - while i <= #e do - if e[i].by == ts then - --atdebug("free_route_locks_indiv",pts,"clearing entry",e[i].by,e[i].rsn) - table.remove(e,i) - else - i = i + 1 - end - end - -- This must be delayed, because this code is executed in-between a train step - -- TODO use luaautomation timers? - if not nocallbacks then - minetest.after(0, ilrs.update_waiting, "lck", pts) - minetest.after(0.5, advtrains.set_fallback_state, minetest.string_to_pos(pts)) - end -end --- frees all route locks, even manual ones set with the tool, at a specific position -function ilrs.remove_route_locks(pts, nocallbacks) - ilrs.rte_locks[pts] = nil - -- This must be delayed, because this code is executed in-between a train step - -- TODO use luaautomation timers? - if not nocallbacks then - minetest.after(0, ilrs.update_waiting, "lck", pts) - end -end - - --- starting from the designated sigd, clears all subsequent route and route_post --- information from the track sections. --- note that this does not clear the routesetting status from the entry signal, --- only from the ts's -function ilrs.cancel_route_from(sigd) - -- we start at the tc designated by signal - local c_sigd = sigd - local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp - while c_sigd do - --atdebug("cancel_route_from: at sigd",c_sigd) - c_tcbs = ildb.get_tcbs(c_sigd) - if not c_tcbs then - atwarn("Failed to cancel route, no TCBS at",c_sigd) - return false - end - - --atdebug("cancelling",c_ts.route.rsn) - -- clear signal aspect and routesetting state - c_tcbs.route_committed = nil - c_tcbs.aspect = nil - c_tcbs.routeset = nil - c_tcbs.route_auto = nil - c_tcbs.route_origin = nil - - advtrains.interlocking.update_signal_aspect(c_tcbs) - - c_ts_id = c_tcbs.ts_id - if not c_tcbs then - atwarn("Failed to cancel route, end of interlocking at",c_sigd) - return false - end - c_ts = ildb.get_ts(c_ts_id) - - if not c_ts - or not c_ts.route - or not sigd_equal(c_ts.route.entry, c_sigd) then - --atdebug("cancel_route_from: abort (eoi/no route):") - return false - end - - c_ts.route = nil - - if c_ts.route_post then - advtrains.interlocking.route.free_route_locks(c_ts_id, c_ts.route_post.locks) - c_sigd = c_ts.route_post.next - else - c_sigd = nil - end - c_ts.route_post = nil - minetest.after(0, advtrains.interlocking.route.update_waiting, "ts", c_ts_id) - end - --atdebug("cancel_route_from: done (no final sigd)") - return true -end - --- TCBS Routesetting helper: generic update function for --- route setting --- Call this function to set and cancel routes! --- sigd, tcbs: self-explanatory --- newrte: If a new route should be set, the route index of it (in tcbs.routes). nil otherwise --- cancel: true in combination with newrte=nil causes cancellation of the current route. -function ilrs.update_route(sigd, tcbs, newrte, cancel) - --atdebug("Update_Route for",sigd,tcbs.signal_name) - local has_changed_aspect = false - if tcbs.route_origin and not sigd_equal(tcbs.route_origin, sigd) then - --atdebug("Signal not in control, held by",tcbs.signal_name) - return - end - if (newrte and tcbs.routeset and tcbs.routeset ~= newrte) or cancel then - if tcbs.route_committed then - --atdebug("Cancelling:",tcbs.routeset) - advtrains.interlocking.route.cancel_route_from(sigd) - end - tcbs.route_committed = nil - tcbs.aspect = nil - has_changed_aspect = true - tcbs.routeset = nil - tcbs.route_auto = nil - tcbs.route_rsn = nil - end - if newrte or tcbs.routeset then - if tcbs.route_committed then - return - end - if newrte then tcbs.routeset = newrte end - --atdebug("Setting:",tcbs.routeset) - local succ, rsn, cbts, cblk = ilrs.set_route(sigd, tcbs.routes[tcbs.routeset]) - if not succ then - tcbs.route_rsn = rsn - --atdebug("Routesetting failed:",rsn) - -- add cbts or cblk to callback table - if cbts then - --atdebug("cbts =",cbts) - if not ilrs.rte_callbacks.ts[cbts] then ilrs.rte_callbacks.ts[cbts]={} end - advtrains.insert_once(ilrs.rte_callbacks.ts[cbts], sigd, sigd_equal) - end - if cblk then - --atdebug("cblk =",cblk) - if not ilrs.rte_callbacks.lck[cblk] then ilrs.rte_callbacks.lck[cblk]={} end - advtrains.insert_once(ilrs.rte_callbacks.lck[cblk], sigd, sigd_equal) - end - else - --atdebug("Committed Route:",tcbs.routeset) - has_changed_aspect = true - end - end - if has_changed_aspect then - -- FIX: prevent an minetest.after() loop caused by update_signal_aspect dispatching path invalidation, which in turn calls ARS again - advtrains.interlocking.update_signal_aspect(tcbs) - end - advtrains.interlocking.update_player_forms(sigd) -end - --- Try to re-set routes that conflicted with this point --- sys can be one of "ts" and "lck" --- key is then ts_id or pts respectively -function ilrs.update_waiting(sys, key) - --atdebug("update_waiting:",sys,".",key) - local t = ilrs.rte_callbacks[sys][key] - ilrs.rte_callbacks[sys][key] = nil - if t then - for _,sigd in ipairs(t) do - --atdebug("Updating", sigd) - -- While these are run, the table we cleared before may be populated again, which is in our interest. - -- (that's the reason we needed to copy it) - local tcbs = ildb.get_tcbs(sigd) - if tcbs then - ilrs.update_route(sigd, tcbs) - end - end - end -end - -advtrains.interlocking.route = ilrs - diff --git a/advtrains_interlocking/settingtypes.txt b/advtrains_interlocking/settingtypes.txt deleted file mode 100644 index f1c22b0..0000000 --- a/advtrains_interlocking/settingtypes.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Stop trains forcibly in front of signal when about to run over an LZB 0 restriction, instead of setting emergency halt for manual resolving -# This prevents the need to manually restart trains that overran red signals, but is unrealistic. -# This is a workaround to circumvent system breakages due to bugs in LZB braking curves -at_il_force_lzb_halt (Force LZB Halt) bool true diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua deleted file mode 100644 index 9729195..0000000 --- a/advtrains_interlocking/signal_api.lua +++ /dev/null @@ -1,546 +0,0 @@ --- Signal API implementation - - ---[[ -Signal aspect table: -asp = { - main = { - free = <boolean>, - speed = <int km/h>, - }, - shunt = { - free = <boolean>, - -- Whether train may proceed as shunt move, on sight - -- main aspect takes precedence over this - proceed_as_main = <boolean>, - -- If an approaching train is a shunt move and "main.free" is set, - -- the train may proceed as a train move under the "main" aspect - -- If this is not set, shunt moves are NOT allowed to switch to - -- a train move, and must stop even if "main.free" is set. - -- This is intended to be used for "Halt for shunt moves" signs. - } - dst = { - free = <boolean>, - speed = <int km/h>, - } - info = { - call_on = <boolean>, -- Call-on route, expect train in track ahead (not implemented yet) - dead_end = <boolean>, -- Route ends on a dead end (e.g. bumper) (not implemented yet) - w_speed = <integer>, - -- "Warning speed restriction". Supposed for short-term speed - -- restrictions which always override any other restrictions - -- imposed by "speed" fields, until lifted by a value of -1 - -- (Example: german Langsamfahrstellen-Signale) - } -} --- For "speed" and "w_speed" fields, a value of -1 means that the --- restriction is lifted. If they are omitted, the value imposed at --- the last aspect received remains valid. --- The "dst" subtable can be completely omitted when no explicit dst --- aspect should be signalled to the train. In this case, the last --- signalled dst aspect remains valid. - -== How signals actually work in here == -Each signal (in the advtrains universe) is some node that has at least the -following things: -- An "influence point" that is set somewhere on a rail -- An aspect which trains that pass the "influence point" have to obey - -There can be static and dynamic signals. Static signals are, roughly -spoken, signs, while dynamic signals are "real" signals which can display -different things. - -The node definition of a signal node should contain those fields: -groups = { - advtrains_signal = 2, - save_in_at_nodedb = 1, -} -advtrains = { - set_aspect = function(pos, node, asp) - -- This function gets called whenever the signal should display - -- a new or changed signal aspect. It is not required that - -- the signal actually displays the exact same aspect, since - -- some signals can not do this by design. - -- Example: pure shunt signals can not display a "main" aspect - -- and have no effect on train moves, so they will only ever - -- honor the shunt.free field for their aspect. - -- In turn, it is not guaranteed that the aspect will fulfill the - -- criteria put down in supported_aspects. - -- If set_aspect is present, supported_aspects should also be declared. - - -- The aspect passed in here can always be queried using the - -- advtrains.interlocking.signal_get_supposed_aspect(pos) function. - -- It is always DANGER when the signal is not used as route signal. - - -- For static signals, this function should be completely omitted - -- If this function is omitted, it won't be possible to use - -- route setting on this signal. - end, - supported_aspects = { - -- A table which tells which different types of aspects this signal - -- is able to display. It is used to construct the "aspect editing" - -- formspec for route programming (and others) It should always be - -- present alongside with set_aspect. If this is not specified but - -- set_aspect is, the user will be allowed to select any aspect. - -- Any of the fields marked with <boolean/nil> support 3 types of values: - nil: if this signal can switch between free/blocked - false: always shows "blocked", unchangable - true: always shows "free", unchangable - -- Any of the "speed" fields should contain a list of possible values - -- to be set as restriction. If omitted, this signal should never - -- set the corresponding "speed" field in the aspect, which means - -- that the previous speed limit stays valid - -- If your signal can only display a single speed (may it be -1), - -- always enclose that single value into a list. (such as {-1}) - main = { - free = <boolean/nil>, - speed = {<speed1>, ..., <speedn>} or nil, - }, - dst = { - free = <boolean/nil>, - speed = {<speed1>, ..., <speedn>} or nil, - }, - shunt = { - free = <boolean/nil>, - }, - info = { - call_on = <boolean/nil>, - dead_end = <boolean/nil>, - w_speed = {<speed1>, ..., <speedn>} or nil, - } - - }, - get_aspect = function(pos, node) - -- This function gets called by the train safety system. It - should return the aspect that this signal actually displays, - not preferably the input of set_aspect. - -- For regular, full-featured light signals, they will probably - honor all entries in the original aspect, however, e.g. - simple shunt signals always return main.free=true regardless of - the set_aspect input because they can not signal "Halt" to - train moves. - -- advtrains.interlocking.DANGER contains a default "all-danger" aspect. - -- If your signal does not cover certain sub-tables of the aspect, - the following reasonable defaults are automatically assumed: - main = { - free = true, - } - dst = { - free = true, - } - shunt = { - free = false, - proceed_as_main = false, - } - end, -} -on_rightclick = advtrains.interlocking.signal_rc_handler -can_dig = advtrains.interlocking.signal_can_dig -after_dig_node = advtrains.interlocking.signal_after_dig - -(If you need to specify custom can_dig or after_dig_node callbacks, -please call those functions anyway!) - -Important note: If your signal should support external ways to set its -aspect (e.g. via mesecons), there are some things that need to be considered: -- advtrains.interlocking.signal_get_supposed_aspect(pos) won't respect this -- Whenever you change the signal aspect, and that aspect change -did not happen through a call to -advtrains.interlocking.signal_set_aspect(pos, asp), you are -*required* to call this function: -advtrains.interlocking.signal_on_aspect_changed(pos) -in order to notify trains about the aspect change. -This function will query get_aspect to retrieve the new aspect. - -]]-- - -local DANGER = { - main = { - free = false, - speed = 0, - }, - shunt = { - free = false, - }, - dst = { - free = false, - speed = 0, - }, - info = {} -} -advtrains.interlocking.DANGER = DANGER - -local function fillout_aspect(asp) - if not asp.main then - asp.main = { - free = true, - } - elseif type(asp.main) ~= "table" then - asp.main = { - free = asp.main~=0, - speed = asp.main, - } - end - if not asp.dst then - asp.dst = { - free = true, - } - end - if not asp.shunt then - asp.shunt = { - free = false, - proceed_as_main = false, - } - elseif type(asp.shunt) ~= "table" then - asp.shunt = { - free = asp.shunt, - proceed_as_main = asp.proceed_as_main, - } - end - if not asp.info then - asp.info = {} - end -end - -function advtrains.interlocking.update_signal_aspect(tcbs) - if tcbs.signal then - local asp = tcbs.aspect or DANGER - advtrains.interlocking.signal_set_aspect(tcbs.signal, asp) - end -end - -function advtrains.interlocking.signal_can_dig(pos) - return not advtrains.interlocking.db.get_sigd_for_signal(pos) -end - -function advtrains.interlocking.signal_after_dig(pos) - -- clear influence point - advtrains.interlocking.db.clear_ip_by_signalpos(pos) -end - -function advtrains.interlocking.signal_set_aspect(pos, asp) - fillout_aspect(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 - ndef.advtrains.set_aspect(pos, node, asp) - advtrains.interlocking.signal_on_aspect_changed(pos) - end -end - --- should be called when aspect has changed on this signal. -function advtrains.interlocking.signal_on_aspect_changed(pos) - local ipts, iconn = advtrains.interlocking.db.get_ip_by_signalpos(pos) - if not ipts then return end - local ipos = minetest.string_to_pos(ipts) - - local tns = advtrains.occ.get_trains_over(ipos) - for id, sidx in pairs(tns) do --- local train = advtrains.trains[id] - --if train.index <= sidx then - minetest.after(0, advtrains.invalidate_path, id) - --end - end -end - -function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack, pointed_thing) - local pname = player:get_player_name() - local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos) - if sigd then - advtrains.interlocking.show_signalling_form(sigd, pname) - else - local ndef = minetest.registered_nodes[node.name] - if ndef.advtrains and ndef.advtrains.set_aspect then - -- permit to set aspect manually - minetest.show_formspec(pname, "at_il_sigasp_"..minetest.pos_to_string(pos), "field[aspect;Set Aspect ('A' to assign IP);D0D0D]") - else - --static signal - only IP - advtrains.interlocking.show_ip_form(pos, pname) - end - 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_sigasp_(.+)$") - local pos - if pts then pos = minetest.string_to_pos(pts) end - if pos and fields.aspect then - if fields.aspect == "A" then - advtrains.interlocking.show_ip_form(pos, pname) - return - end - local mfs, msps, dfs, dsps, shs = string.match(fields.aspect, "^([FD])([-0-9]+)([FD])([-0-9]+)([FD])$") - local asp = { - main = { - free = mfs=="F", - speed = tonumber(msps), - }, - shunt = { - free = shs=="F", - }, - dst = { - free = dfs=="F", - speed = tonumber(dsps), - }, - info = { - call_on = false, -- Call-on route, expect train in track ahead - dead_end = false, -- Route ends on a dead end (e.g. bumper) - } - } - advtrains.interlocking.signal_set_aspect(pos, asp) - end -end) - --- Returns the aspect the signal at pos is supposed to show -function advtrains.interlocking.signal_get_supposed_aspect(pos) - local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos) - if sigd then - local tcbs = advtrains.interlocking.db.get_tcbs(sigd) - if tcbs.aspect then - return tcbs.aspect - end - end - 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_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) - if not asp then asp = DANGER end - fillout_aspect(asp) - return asp - end - return nil -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) - local node_ok, conns, rhe = advtrains.get_rail_info_at(ipos, advtrains.all_tracktypes) - if not node_ok then return end - local yaw = advtrains.dir_to_angle(conns[connid].c) - - -- using tcbmarker here - local obj = minetest.add_entity(vector.add(ipos, {x=0, y=0.2, z=0}), "advtrains_interlocking:tcbmarker") - if not obj then return end - obj:set_yaw(yaw) - obj:set_properties({ - textures = { "at_il_signal_ip.png" }, - }) -end - --- shows small info form for signal IP state/assignment --- 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,5]label[0.5,0.5;Signal at "..minetest.pos_to_string(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]" - 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,3.5; 5,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) - end -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then - return - end - local pts = string.match(formname, "^at_il_ipassign_([^_]+)$") - 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 - end -end) - --- inits the signal IP assignment process -function advtrains.interlocking.signal_init_ip_assign(pos, pname) - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - --remove old IP - --advtrains.interlocking.db.clear_ip_by_signalpos(pos) - minetest.chat_send_player(pname, "Configuring Signal: Please look in train's driving direction and punch rail to set influence point.") - - players_assign_ip[pname] = pos -end - -minetest.register_on_punchnode(function(pos, node, player, pointed_thing) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - return - end - -- IP assignment - local signalpos = players_assign_ip[pname] - if signalpos then - if vector.distance(pos, signalpos)<=50 then - local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - if node_ok and #conns == 2 then - - local yaw = player:get_look_horizontal() - local plconnid = advtrains.yawToClosestConn(yaw, conns) - - -- add assignment if not already present. - local pts = advtrains.roundfloorpts(pos) - if not advtrains.interlocking.db.get_ip_signal_asp(pts, plconnid) then - advtrains.interlocking.db.set_ip_signal(pts, plconnid, signalpos) - ipmarker(pos, plconnid) - minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point") - else - minetest.chat_send_player(pname, "Configuring Signal: Influence point of another signal is already present!") - end - else - minetest.chat_send_player(pname, "Configuring Signal: This is not a normal two-connection rail! Aborted.") - end - else - minetest.chat_send_player(pname, "Configuring Signal: Node is too far away. Aborted.") - end - players_assign_ip[pname] = nil - end -end) - - ---== aspect selector ==-- - -local players_aspsel = {} - ---[[ -suppasp: "supported_aspects" table -purpose: form title string -callback: func(pname, aspect) called on form submit -]] -function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_purpose, callback, p_isasp) - local suppasp = p_suppasp or { - main = {}, dst = {}, shunt = {}, info = {}, - } - local purpose = p_purpose or "" - local isasp = p_isasp and fillout_aspect(p_isasp) - - local form = "size[7,5]label[0.5,0.5;Select Signal Aspect:]" - form = form.."label[0.5,1;"..purpose.."]" - - form = form.."label[0.5,1.5;== Main Signal ==]" - if suppasp.main.free == nil then - local st = 2 - if isasp and not isasp.main.free then st=1 end - form = form.."dropdown[0.5,2;2;main_free;danger,free;"..st.."]" - end - if suppasp.main.speed then - local selid = 1 - if isasp and isasp.main.speed then - for idx, spv in ipairs(suppasp.main.speed) do - if spv == isasp.main.speed then - selid = idx - break - end - end - end - form = form.."label[2.3,1;Speed:]" - form = form.."dropdown[3,2;2;main_speed;"..table.concat(suppasp.main.speed, ",")..";"..selid.."]" - end - - form = form.."label[0.5,3;== Shunting ==]" - if suppasp.shunt.free == nil then - local st = 1 - if isasp and isasp.shunt.free then st=2 end - form = form.."dropdown[0.5,3.5;2;shunt_free;---,allowed;"..st.."]" - end - - form = form.."button_exit[0.5,4.5; 5,1;save;OK]" - - local token = advtrains.random_id() - - minetest.show_formspec(pname, "at_il_sigaspdia_"..token, form) - - minetest.after(1, function() - players_aspsel[pname] = { - suppasp = suppasp, - callback = callback, - token = token, - } - end) -end - -local function usebool(sup, val, free) - if sup == nil then - return val==free - else - return sup - end -end -local function usespeed(sup, val) - if sup then - return tonumber(val) - else - return nil - end -end - --- TODO use non-hacky way to parse outputs - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - local psl = players_aspsel[pname] - if psl then - if formname == "at_il_sigaspdia_"..psl.token then - if fields.save then - local asp = { - main = { - free = usebool(psl.suppasp.main.free, fields.main_free, "free"), - speed = usespeed(psl.suppasp.main.speed, fields.main_speed), - }, - dst = { - free = true, speed = -1, - }, - shunt = { - free = usebool(psl.suppasp.shunt.free, fields.shunt_free, "allowed"), - }, - info = {} - } - psl.callback(pname, asp) - end - else - players_aspsel[pname] = nil - end - end - -end) diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua deleted file mode 100644 index da318a7..0000000 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ /dev/null @@ -1,789 +0,0 @@ --- Track Circuit Breaks and Track Sections - Player interaction - -local players_assign_tcb = {} -local players_assign_signal = {} -local players_link_ts = {} - -local ildb = advtrains.interlocking.db -local ilrs = advtrains.interlocking.route - -local sigd_equal = advtrains.interlocking.sigd_equal - -local lntrans = { "A", "B" } - -local function sigd_to_string(sigd) - return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] -end - -minetest.register_node("advtrains_interlocking:tcb_node", { - drawtype = "mesh", - paramtype="light", - paramtype2="facedir", - walkable = false, - selection_box = { - type = "fixed", - fixed = {-1/6, -1/2, -1/6, 1/6, 1/4, 1/6}, - }, - mesh = "at_il_tcb_node.obj", - tiles = {"at_il_tcb_node.png"}, - description="Track Circuit Break", - sunlight_propagates=true, - groups = { - cracky=3, - not_blocking_trains=1, - --save_in_at_nodedb=2, - at_il_track_circuit_break = 1, - }, - after_place_node = function(pos, node, player) - local meta = minetest.get_meta(pos) - meta:set_string("infotext", "Unconfigured Track Circuit Break, right-click to assign.") - end, - on_rightclick = function(pos, node, player) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - - local meta = minetest.get_meta(pos) - local tcbpts = meta:get_string("tcb_pos") - if tcbpts ~= "" then - local tcbpos = minetest.string_to_pos(tcbpts) - local tcb = ildb.get_tcb(tcbpos) - if tcb then - advtrains.interlocking.show_tcb_form(tcbpos, pname) - else - minetest.chat_send_player(pname, "This TCB has been removed. Please dig marker.") - end - else - --unconfigured - minetest.chat_send_player(pname, "Configuring TCB: Please punch the rail you want to assign this TCB to.") - - players_assign_tcb[pname] = pos - end - end, - --on_punch = function(pos, node, player) - -- local meta = minetest.get_meta(pos) - -- local tcbpts = meta:get_string("tcb_pos") - -- if tcbpts ~= "" then - -- local tcbpos = minetest.string_to_pos(tcbpts) - -- advtrains.interlocking.show_tcb_marker(tcbpos) - -- end - --end, - can_dig = function(pos, player) - if player == nil then return false end - - local pname = player:get_player_name() - - -- Those markers can only be dug when all adjacent TS's are set - -- as EOI. - local meta = minetest.get_meta(pos) - local tcbpts = meta:get_string("tcb_pos") - if tcbpts ~= "" then - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - local tcbpos = minetest.string_to_pos(tcbpts) - local tcb = ildb.get_tcb(tcbpos) - if not tcb then return true end - for connid=1,2 do - if tcb[connid].ts_id or tcb[connid].signal then - minetest.chat_send_player(pname, "Can't remove TCB: Both sides must have no track section and no signal assigned!") - return false - end - if not ildb.may_modify_tcbs(tcb[connid]) then - minetest.chat_send_player(pname, "Can't remove TCB: Side "..connid.." forbids modification (shouldn't happen).") - return false - end - end - end - return true - end, - after_dig_node = function(pos, oldnode, oldmetadata, player) - if not oldmetadata or not oldmetadata.fields then return end - local tcbpts = oldmetadata.fields.tcb_pos - if tcbpts and tcbpts ~= "" then - local tcbpos = minetest.string_to_pos(tcbpts) - local success = ildb.remove_tcb(tcbpos) - if success and player then - minetest.chat_send_player(player:get_player_name(), "TCB has been removed.") - else - minetest.chat_send_player(player:get_player_name(), "Failed to remove TCB!") - minetest.set_node(pos, oldnode) - local meta = minetest.get_meta(pos) - meta:set_string("tcb_pos", minetest.pos_to_string(tcbpos)) - end - end - 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 - -- TCB assignment - local tcbnpos = players_assign_tcb[pname] - if tcbnpos then - if vector.distance(pos, tcbnpos)<=20 then - local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - if node_ok and #conns == 2 then - local ok = ildb.create_tcb(pos) - - if not ok then - minetest.chat_send_player(pname, "Configuring TCB: TCB already exists at this position! It has now been re-assigned.") - end - - ildb.sync_tcb_neighbors(pos, 1) - ildb.sync_tcb_neighbors(pos, 2) - - local meta = minetest.get_meta(tcbnpos) - meta:set_string("tcb_pos", minetest.pos_to_string(pos)) - meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos)) - minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB") - else - minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.") - end - else - minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.") - end - players_assign_tcb[pname] = nil - end - - -- Signal assignment - local sigd = players_assign_signal[pname] - if sigd then - if vector.distance(pos, sigd.p)<=50 then - local is_signal = minetest.get_item_group(node.name, "advtrains_signal") >= 2 - if is_signal then - local ndef = minetest.registered_nodes[node.name] - if ndef and ndef.advtrains and ndef.advtrains.set_aspect then - local tcbs = ildb.get_tcbs(sigd) - if tcbs then - tcbs.signal = pos - 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 - ildb.set_sigd_for_signal(pos, sigd) - minetest.chat_send_player(pname, "Configuring TCB: Successfully assigned signal.") - advtrains.interlocking.show_ip_form(pos, pname, true) - else - minetest.chat_send_player(pname, "Configuring TCB: Internal error, TCBS doesn't exist. Aborted.") - end - else - minetest.chat_send_player(pname, "Configuring TCB: Cannot use static signals for routesetting. Aborted.") - end - else - minetest.chat_send_player(pname, "Configuring TCB: Not a compatible signal. Aborted.") - end - else - minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.") - end - players_assign_signal[pname] = nil - end -end) - --- TCB Form - -local function mktcbformspec(tcbs, btnpref, offset, pname) - local form = "" - local ts - if tcbs.ts_id then - ts = ildb.get_ts(tcbs.ts_id) - end - if ts then - form = form.."label[0.5,"..offset..";Side "..btnpref..": "..minetest.formspec_escape(ts.name).."]" - form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_gotots;Show track section]" - if ildb.may_modify_tcbs(tcbs) then - -- Note: the security check to prohibit those actions is located in database.lua in the corresponding functions. - form = form.."button[0.5,"..(offset+1.5)..";2.5,1;"..btnpref.."_update;Update near TCBs]" - form = form.."button[3 ,"..(offset+1.5)..";2.5,1;"..btnpref.."_remove;Remove from section]" - end - else - tcbs.ts_id = nil - form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]" - form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]" - --if tcbs.section_free then - --form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setlocked;Section is free]" - --else - --form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setfree;Section is blocked]" - --end - end - if tcbs.signal then - form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]" - else - form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_asnsig;Assign a signal]" - end - return form -end - - -function advtrains.interlocking.show_tcb_form(pos, pname) - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - local tcb = ildb.get_tcb(pos) - if not tcb then return end - - local form = "size[6,9] label[0.5,0.5;Track Circuit Break Configuration]" - form = form .. mktcbformspec(tcb[1], "A", 1, pname) - form = form .. mktcbformspec(tcb[2], "B", 5, pname) - - minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form) - advtrains.interlocking.show_tcb_marker(pos) -end - ---helper: length of nil table is 0 -local function nlen(t) - if not t then return 0 end - return #t -end - - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - return - end - local pts = string.match(formname, "^at_il_tcbconfig_(.+)$") - local pos - if pts then - pos = minetest.string_to_pos(pts) - end - if pos and not fields.quit then - local tcb = ildb.get_tcb(pos) - if not tcb then return end - local f_gotots = {fields.A_gotots, fields.B_gotots} - local f_update = {fields.A_update, fields.B_update} - local f_remove = {fields.A_remove, fields.B_remove} - local f_makeil = {fields.A_makeil, fields.B_makeil} - local f_setlocked = {fields.A_setlocked, fields.B_setlocked} - local f_setfree = {fields.A_setfree, fields.B_setfree} - local f_asnsig = {fields.A_asnsig, fields.B_asnsig} - local f_sigdia = {fields.A_sigdia, fields.B_sigdia} - - for connid=1,2 do - local tcbs = tcb[connid] - if tcbs.ts_id then - if f_gotots[connid] then - advtrains.interlocking.show_ts_form(tcbs.ts_id, pname) - return - end - if f_update[connid] then - ildb.sync_tcb_neighbors(pos, connid) - end - if f_remove[connid] then - ildb.remove_from_interlocking({p=pos, s=connid}) - end - else - if f_makeil[connid] then - -- try sinc_tcb_neighbors first - ildb.sync_tcb_neighbors(pos, connid) - -- if that didn't work, create new section - if not tcbs.ts_id then - ildb.create_ts({p=pos, s=connid}) - ildb.sync_tcb_neighbors(pos, connid) - end - end - -- non-interlocked - if f_setfree[connid] then - tcbs.section_free = true - end - if f_setlocked[connid] then - tcbs.section_free = nil - end - end - if f_asnsig[connid] and not tcbs.signal then - minetest.chat_send_player(pname, "Configuring TCB: Please punch the signal to assign.") - players_assign_signal[pname] = {p=pos, s=connid} - minetest.close_formspec(pname, formname) - return - end - if f_sigdia[connid] and tcbs.signal then - advtrains.interlocking.show_signalling_form({p=pos, s=connid}, pname) - return - end - - end - advtrains.interlocking.show_tcb_form(pos, pname) - end - -end) - - - --- TS Formspec - --- textlist selection temporary storage -local ts_pselidx = {} - -function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb) - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - local ts = ildb.get_ts(ts_id) - if not ts_id then return end - - local form = "size[10,10]label[0.5,0.5;Track Section Detail - "..ts_id.."]" - form = form.."field[0.8,2;5.2,1;name;Section name;"..minetest.formspec_escape(ts.name).."]" - form = form.."button[5.5,1.7;1,1;setname;Set]" - local hint - - local strtab = {} - for idx, sigd in ipairs(ts.tc_breaks) do - strtab[#strtab+1] = minetest.formspec_escape(sigd_to_string(sigd)) - advtrains.interlocking.show_tcb_marker(sigd.p) - end - - form = form.."textlist[0.5,3;5,3;tcblist;"..table.concat(strtab, ",").."]" - - if ildb.may_modify_ts(ts) then - - if players_link_ts[pname] then - local other_id = players_link_ts[pname] - local other_ts = ildb.get_ts(other_id) - if other_ts then - if ildb.may_modify_ts(other_ts) then - form = form.."button[5.5,3;3.5,1;mklink;Join with "..minetest.formspec_escape(other_ts.name).."]" - form = form.."button[9 ,3;0.5,1;cancellink;X]" - end - end - else - form = form.."button[5.5,3;4,1;link;Join into other section]" - hint = 1 - end - form = form.."button[5.5,4;4,1;dissolve;Dissolve Section]" - form = form.."tooltip[dissolve;This will remove the track section and set all its end points to End Of Interlocking]" - if sel_tcb then - form = form.."button[5.5,5;4,1;del_tcb;Unlink selected TCB]" - hint = 2 - end - else - hint=3 - end - - if ts.route then - form = form.."label[0.5,6.1;Route is set: "..ts.route.rsn.."]" - elseif ts.route_post then - form = form.."label[0.5,6.1;Section holds "..#(ts.route_post.lcks or {}).." route locks.]" - end - -- occupying trains - if ts.trains and #ts.trains>0 then - form = form.."label[0.5,7.1;Trains on this section:]" - form = form.."textlist[0.5,7.7;3,2;trnlist;"..table.concat(ts.trains, ",").."]" - else - form = form.."label[0.5,7.1;No trains on this section.]" - end - - form = form.."button[5.5,7;4,1;reset;Reset section state]" - - if hint == 1 then - form = form.."label[0.5,0.75;Use the 'Join' button to designate rail crosses and link not listed far-away TCBs]" - elseif hint == 2 then - form = form.."label[0.5,0.75;Unlinking a TCB will set it to non-interlocked mode.]" - elseif hint == 3 then - form = form.."label[0.5,0.75;You cannot modify track sections when a route is set or a train is on the section.]" - --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 - - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - return - end - -- 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 = ildb.get_ts(ts_id) - if not ts then return end - - local sel_tcb - 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 ildb.may_modify_ts(ts) then - if players_link_ts[pname] then - if fields.cancellink then - players_link_ts[pname] = nil - elseif fields.mklink then - ildb.link_track_sections(players_link_ts[pname], ts_id) - players_link_ts[pname] = nil - end - end - - if fields.del_tcb and sel_tcb and sel_tcb > 0 and sel_tcb <= #ts.tc_breaks then - if not ildb.remove_from_interlocking(ts.tc_breaks[sel_tcb]) then - minetest.chat_send_player(pname, "Please unassign signal first!") - end - sel_tcb = nil - end - - if fields.link then - players_link_ts[pname] = ts_id - end - if fields.dissolve then - ildb.dissolve_ts(ts_id) - minetest.close_formspec(pname, formname) - return - end - end - - if fields.setname then - ts.name = fields.name - if ts.name == "" then - ts.name = "Section "..ts_id - end - end - - if fields.reset then - -- User requested resetting the section - -- Show him what this means... - local form = "size[7,5]label[0.5,0.5;Reset track section]" - form = form.."label[0.5,1;This will clear the list of trains\nand the routesetting status of this section.\nAre you sure?]" - form = form.."button_exit[0.5,2.5; 5,1;reset;Yes]" - form = form.."button_exit[0.5,3.5; 5,1;cancel;Cancel]" - minetest.show_formspec(pname, "at_il_tsreset_"..ts_id, form) - return - end - - advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb) - return - end - - ts_id = string.match(formname, "^at_il_tsreset_(.+)$") - if ts_id and fields.reset then - local ts = ildb.get_ts(ts_id) - if not ts then return end - ts.trains = {} - if ts.route_post then - advtrains.interlocking.route.free_route_locks(ts_id, ts.route_post.locks) - end - ts.route_post = nil - ts.route = nil - for _, sigd in ipairs(ts.tc_breaks) do - local tcbs = ildb.get_tcbs(sigd) - advtrains.interlocking.update_signal_aspect(tcbs) - end - minetest.chat_send_player(pname, "Reset track section "..ts_id.."!") - end -end) - --- TCB marker entities - --- table with objectRefs -local markerent = {} - -minetest.register_entity("advtrains_interlocking:tcbmarker", { - visual = "mesh", - mesh = "trackplane.b3d", - textures = {"at_il_tcb_marker.png"}, - collisionbox = {-1,-0.5,-1, 1,-0.4,1}, - visual_size = {x=10, y=10}, - on_punch = function(self) - self.object:remove() - end, - on_rightclick = function(self, player) - if self.tcbpos and player then - advtrains.interlocking.show_tcb_form(self.tcbpos, player:get_player_name()) - end - end, - get_staticdata = function() return "STATIC" end, - on_activate = function(self, sdata) if sdata=="STATIC" then self.object:remove() end end, - static_save = false, -}) - -function advtrains.interlocking.show_tcb_marker(pos) - --atdebug("showing tcb marker",pos) - local tcb = ildb.get_tcb(pos) - if not tcb then return end - local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - if not node_ok then return end - local yaw = advtrains.conn_angle_median(conns[2].c, conns[1].c) - - local itex = {} - for connid=1,2 do - local tcbs = tcb[connid] - local ts - if tcbs.ts_id then - ts = ildb.get_ts(tcbs.ts_id) - end - if ts then - itex[connid] = ts.name - else - itex[connid] = "--EOI--" - end - end - - local pts = advtrains.roundfloorpts(pos) - if markerent[pts] then - markerent[pts]:remove() - end - - local obj = minetest.add_entity(pos, "advtrains_interlocking:tcbmarker") - if not obj then return end - obj:set_yaw(yaw) - obj:set_properties({ - infotext = "A = "..itex[1].."\nB = "..itex[2] - }) - local le = obj:get_luaentity() - if le then le.tcbpos = pos end - - markerent[pts] = obj -end - --- Signalling formspec - set routes a.s.o - --- textlist selection temporary storage -local sig_pselidx = {} --- Players having a signalling form open -local p_open_sig_form = {} - -function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte) - if not minetest.check_player_privs(pname, "train_operator") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - local hasprivs = minetest.check_player_privs(pname, "interlocking") - local tcbs = ildb.get_tcbs(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,10]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;"..minetest.formspec_escape(tcbs.signal_name).."]" - form = form.."button[5.5,1.2;1,1;setname;Set]" - - if tcbs.routeset then - local rte = tcbs.routes[tcbs.routeset] - if not rte then - atwarn("Unknown route set from signal!") - tcbs.routeset = nil - return - end - form = form.."label[0.5,2.5;A route is requested from this signal:]" - form = form.."label[0.5,3.0;"..minetest.formspec_escape(rte.name).."]" - if tcbs.route_committed then - form = form.."label[0.5,3.5;Route has been set.]" - else - form = form.."label[0.5,3.5;Waiting for route to be set...]" - if tcbs.route_rsn then - form = form.."label[0.5,4;"..minetest.formspec_escape(tcbs.route_rsn).."]" - end - end - if not tcbs.route_auto then - form = form.."button[0.5,7; 5,1;auto;Enable Automatic Working]" - else - form = form.."label[0.5,7 ;Automatic Working is active.]" - form = form.."label[0.5,7.3;Route is re-set when a train passed.]" - form = form.."button[0.5,7.7; 5,1;noauto;Disable Automatic Working]" - end - - form = form.."button[0.5,6; 5,1;cancelroute;Cancel Route]" - else - if not tcbs.route_origin then - local strtab = {} - for idx, route in ipairs(tcbs.routes) do - local clr = "" - if route.ars then - clr = "#FF5555" - if route.ars.default then - clr = "#55FF55" - end - end - strtab[#strtab+1] = clr .. 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]" - if hasprivs then - form = form.."button[3.5,7;2,1;editroute;Edit]" - end - else - if tcbs.ars_disabled then - form = form.."label[0.5,6 ;NOTE: ARS is disabled.]" - form = form.."label[0.5,6.5;Routes are not automatically set.]" - end - end - 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]" - end - elseif sigd_equal(tcbs.route_origin, sigd) then - -- something has gone wrong: tcbs.routeset should have been set... - form = form.."label[0.5,2.5;Inconsistent state: route_origin is same TCBS but no route set. Try again.]" - ilrs.cancel_route_from(sigd) - else - form = form.."label[0.5,2.5;Route is set over this signal by:\n"..sigd_to_string(tcbs.route_origin).."]" - form = form.."label[0.5,4;Wait for this route to be cancelled in order to do anything here.]" - end - end - sig_pselidx[pname] = sel_rte - minetest.show_formspec(pname, "at_il_signalling_"..minetest.pos_to_string(sigd.p).."_"..sigd.s, form) - p_open_sig_form[pname] = sigd - - -- always a good idea to update the signal aspect - advtrains.interlocking.update_signal_aspect(tcbs) -end - -function advtrains.interlocking.update_player_forms(sigd) - for pname, tsigd in pairs(p_open_sig_form) do - if advtrains.interlocking.sigd_equal(sigd, tsigd) then - advtrains.interlocking.show_signalling_form(sigd, pname, nil) - end - end -end - - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "train_operator") then - return - end - local hasprivs = minetest.check_player_privs(pname, "interlocking") - - -- independent of the formspec, clear this whenever some formspec event happens - local tpsi = sig_pselidx[pname] - sig_pselidx[pname] = nil - p_open_sig_form[pname] = nil - - local pts, connids = string.match(formname, "^at_il_signalling_([^_]+)_(%d)$") - local pos, connid - if pts then - pos = minetest.string_to_pos(pts) - connid = tonumber(connids) - 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 = ildb.get_tcbs(sigd) - if not tcbs then return end - - 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 and hasprivs then - tcbs.signal_name = fields.name - end - if tcbs.routeset and fields.cancelroute then - if tcbs.routes[tcbs.routeset] and tcbs.routes[tcbs.routeset].ars then - tcbs.ars_disabled = true - end - -- if route committed, cancel route ts info - ilrs.update_route(sigd, tcbs, nil, true) - end - if not tcbs.routeset then - if fields.newroute and hasprivs 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 - ilrs.update_route(sigd, tcbs, sel_rte) - 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.editroute and hasprivs then - advtrains.interlocking.show_route_edit_form(pname, sigd, sel_rte) - --local rte = tcbs.routes[sel_rte] - --minetest.show_formspec(pname, formname.."_renroute_"..sel_rte, "field[name;Enter new route name;"..rte.name.."]") - return - end - end - end - - if fields.unassign and hasprivs then - -- unassigning the signal from the tcbs - -- only when no route is set. - -- Routes and name remain saved, in case the player wants to reassign a new signal - if not tcbs.routeset then - local signal_pos = tcbs.signal - ildb.set_sigd_for_signal(signal_pos, nil) - tcbs.signal = nil - tcbs.aspect = nil - minetest.close_formspec(pname, formname) - minetest.chat_send_player(pname, "Signal has been unassigned. Name and routes are kept for reuse.") - return - else - 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 - end - - if fields.auto then - tcbs.route_auto = true - end - if fields.noauto then - tcbs.route_auto = false - end - - advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte) - return - end - - - if not hasprivs then 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 = ildb.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) diff --git a/advtrains_interlocking/textures/advtrains_dtrack_npr_placer.png b/advtrains_interlocking/textures/advtrains_dtrack_npr_placer.png Binary files differdeleted file mode 100644 index 0d1c769..0000000 --- a/advtrains_interlocking/textures/advtrains_dtrack_npr_placer.png +++ /dev/null diff --git a/advtrains_interlocking/textures/advtrains_dtrack_shared_npr.png b/advtrains_interlocking/textures/advtrains_dtrack_shared_npr.png Binary files differdeleted file mode 100644 index 0116c27..0000000 --- a/advtrains_interlocking/textures/advtrains_dtrack_shared_npr.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_route_end.png b/advtrains_interlocking/textures/at_il_route_end.png Binary files differdeleted file mode 100644 index 1433f0c..0000000 --- a/advtrains_interlocking/textures/at_il_route_end.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_route_lock.png b/advtrains_interlocking/textures/at_il_route_lock.png Binary files differdeleted file mode 100644 index 6a5269b..0000000 --- a/advtrains_interlocking/textures/at_il_route_lock.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_route_lock_edit.png b/advtrains_interlocking/textures/at_il_route_lock_edit.png Binary files differdeleted file mode 100644 index df5f923..0000000 --- a/advtrains_interlocking/textures/at_il_route_lock_edit.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_route_set.png b/advtrains_interlocking/textures/at_il_route_set.png Binary files differdeleted file mode 100644 index 3531420..0000000 --- a/advtrains_interlocking/textures/at_il_route_set.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_route_start.png b/advtrains_interlocking/textures/at_il_route_start.png Binary files differdeleted file mode 100644 index dcb5160..0000000 --- a/advtrains_interlocking/textures/at_il_route_start.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_routep_advance.png b/advtrains_interlocking/textures/at_il_routep_advance.png Binary files differdeleted file mode 100644 index d971e85..0000000 --- a/advtrains_interlocking/textures/at_il_routep_advance.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_routep_end_here.png b/advtrains_interlocking/textures/at_il_routep_end_here.png Binary files differdeleted file mode 100644 index 9dd3088..0000000 --- a/advtrains_interlocking/textures/at_il_routep_end_here.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_routep_end_over.png b/advtrains_interlocking/textures/at_il_routep_end_over.png Binary files differdeleted file mode 100644 index e03198b..0000000 --- a/advtrains_interlocking/textures/at_il_routep_end_over.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_routep_end_over_last.png b/advtrains_interlocking/textures/at_il_routep_end_over_last.png Binary files differdeleted file mode 100644 index f4fb1aa..0000000 --- a/advtrains_interlocking/textures/at_il_routep_end_over_last.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_signal_asp_danger.png b/advtrains_interlocking/textures/at_il_signal_asp_danger.png Binary files differdeleted file mode 100644 index fca786d..0000000 --- a/advtrains_interlocking/textures/at_il_signal_asp_danger.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_signal_asp_free.png b/advtrains_interlocking/textures/at_il_signal_asp_free.png Binary files differdeleted file mode 100644 index e9d6e9c..0000000 --- a/advtrains_interlocking/textures/at_il_signal_asp_free.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_signal_asp_slow.png b/advtrains_interlocking/textures/at_il_signal_asp_slow.png Binary files differdeleted file mode 100644 index 9242bb3..0000000 --- a/advtrains_interlocking/textures/at_il_signal_asp_slow.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_signal_ip.png b/advtrains_interlocking/textures/at_il_signal_ip.png Binary files differdeleted file mode 100644 index bf1618a..0000000 --- a/advtrains_interlocking/textures/at_il_signal_ip.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_signal_off.png b/advtrains_interlocking/textures/at_il_signal_off.png Binary files differdeleted file mode 100644 index f9b1f79..0000000 --- a/advtrains_interlocking/textures/at_il_signal_off.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_tcb_marker.png b/advtrains_interlocking/textures/at_il_tcb_marker.png Binary files differdeleted file mode 100644 index 3efc38a..0000000 --- a/advtrains_interlocking/textures/at_il_tcb_marker.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_tcb_node.png b/advtrains_interlocking/textures/at_il_tcb_node.png Binary files differdeleted file mode 100644 index d5f615f..0000000 --- a/advtrains_interlocking/textures/at_il_tcb_node.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_tool.png b/advtrains_interlocking/textures/at_il_tool.png Binary files differdeleted file mode 100644 index f6ce1cc..0000000 --- a/advtrains_interlocking/textures/at_il_tool.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_turnout_cr_l.png b/advtrains_interlocking/textures/at_il_turnout_cr_l.png Binary files differdeleted file mode 100644 index fb79e3d..0000000 --- a/advtrains_interlocking/textures/at_il_turnout_cr_l.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_turnout_cr_r.png b/advtrains_interlocking/textures/at_il_turnout_cr_r.png Binary files differdeleted file mode 100644 index e04dfbd..0000000 --- a/advtrains_interlocking/textures/at_il_turnout_cr_r.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_turnout_free.png b/advtrains_interlocking/textures/at_il_turnout_free.png Binary files differdeleted file mode 100644 index 5c83193..0000000 --- a/advtrains_interlocking/textures/at_il_turnout_free.png +++ /dev/null diff --git a/advtrains_interlocking/textures/at_il_turnout_st.png b/advtrains_interlocking/textures/at_il_turnout_st.png Binary files differdeleted file mode 100644 index 50d5ad5..0000000 --- a/advtrains_interlocking/textures/at_il_turnout_st.png +++ /dev/null diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua deleted file mode 100644 index 5d38b3a..0000000 --- a/advtrains_interlocking/tool.lua +++ /dev/null @@ -1,66 +0,0 @@ --- tool.lua --- Interlocking tool - -local ilrs = advtrains.interlocking.route - -minetest.register_craftitem("advtrains_interlocking:tool",{ - description = "Interlocking tool\nright-click turnouts to inspect route locks", - groups = {cracky=1}, -- key=name, value=rating; rating=1..3. - inventory_image = "at_il_tool.png", - wield_image = "at_il_tool.png", - stack_max = 1, - on_place = function(itemstack, placer, pointed_thing) - local pname = placer:get_player_name() - if not pname then - return - end - if not minetest.check_player_privs(pname, {interlocking=true}) then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - if pointed_thing.type=="node" then - local pos=pointed_thing.under - if advtrains.is_passive(pos) then - local form = "size[7,5]label[0.5,0.5;Route lock inspector]" - local pts = minetest.pos_to_string(pos) - - local rtl = ilrs.has_route_lock(pts) - - if rtl then - form = form.."label[0.5,1;Route locks currently put:\n"..rtl.."]" - form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]" - else - form = form.."label[0.5,1;No route locks set]" - form = form.."button_exit[0.5,3.5; 5,1;emplace;Emplace manual lock]" - end - - minetest.show_formspec(pname, "at_il_rtool_"..pts, form) - else - minetest.chat_send_player(pname, "Cannot use this here.") - return - end - end - end, -}) - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - return - end - local pos - local pts = string.match(formname, "^at_il_rtool_(.+)$") - if pts then - pos = minetest.string_to_pos(pts) - end - if pos then - if advtrains.is_passive(pos) then - if fields.clear then - ilrs.remove_route_locks(pts) - end - if fields.emplace then - ilrs.add_manual_route_lock(pts, "Manual lock ("..pname..")") - end - end - end -end) diff --git a/advtrains_interlocking/train_sections.lua b/advtrains_interlocking/train_sections.lua deleted file mode 100644 index 757f36a..0000000 --- a/advtrains_interlocking/train_sections.lua +++ /dev/null @@ -1,199 +0,0 @@ --- train_related.lua --- Occupation of track sections - mainly implementation of train callbacks - ---[[ -Track section occupation is saved as follows - -In train: -train.il_sections = { - [n] = {ts_id = <...> (origin = <sigd>)} -} --- "origin" is the TCB (signal describer) the train initially entered this section - -In track section -ts.trains = { - [n] = <train_id> -} - -When any inconsistency is detected, we will assume the most restrictive setup. -It will be possible to indicate a section "free" via the GUI. -]] - -local ildb = advtrains.interlocking.db - -local sigd_equal = advtrains.interlocking.sigd_equal - -local function itexist(tbl, com) - for _,item in ipairs(tbl) do - if (item==com) then - return true - end - end - return false -end -local function itkexist(tbl, ikey, com) - for _,item in ipairs(tbl) do - if item[ikey] == com then - return true - end - end - return false -end - -local function itremove(tbl, com) - local i=1 - while i <= #tbl do - if tbl[i] == com then - table.remove(tbl, i) - else - i = i + 1 - end - end -end -local function itkremove(tbl, ikey, com) - local i=1 - while i <= #tbl do - if tbl[i][ikey] == com then - table.remove(tbl, i) - else - i = i + 1 - end - end -end - -local function setsection(tid, train, ts_id, ts, sigd) - -- train - if not train.il_sections then train.il_sections = {} end - if not itkexist(train.il_sections, "ts_id", ts_id) then - table.insert(train.il_sections, {ts_id = ts_id, origin = sigd}) - end - - -- ts - if not ts.trains then ts.trains = {} end - if not itexist(ts.trains, tid) then - table.insert(ts.trains, tid) - end - - -- routes - local tcbs = advtrains.interlocking.db.get_tcbs(sigd) - - -- route setting - clear route state - if ts.route then - --atdebug(tid,"enters",ts_id,"examining Routestate",ts.route) - if not sigd_equal(ts.route.entry, sigd) then - -- Train entered not from the route. Locate origin and cancel route! - atwarn("Train",tid,"hit route",ts.route.rsn,"!") - advtrains.interlocking.route.cancel_route_from(ts.route.origin) - atwarn("Route was cancelled.") - else - -- train entered route regularily. Reset route and signal - tcbs.route_committed = nil - tcbs.route_comitted = nil -- TODO compatibility cleanup - tcbs.aspect = nil - tcbs.route_origin = nil - advtrains.interlocking.update_signal_aspect(tcbs) - if tcbs.signal and sigd_equal(ts.route.entry, ts.route.origin) then - if tcbs.route_auto and tcbs.routeset then - --atdebug("Resetting route (",ts.route.origin,")") - advtrains.interlocking.route.update_route(ts.route.origin, tcbs) - else - tcbs.routeset = nil - end - end - end - ts.route = nil - end - if tcbs.signal then - advtrains.interlocking.route.update_route(sigd, tcbs) - end -end - -local function freesection(tid, train, ts_id, ts) - -- train - if not train.il_sections then train.il_sections = {} end - itkremove(train.il_sections, "ts_id", ts_id) - - -- ts - if not ts.trains then ts.trains = {} end - itremove(ts.trains, tid) - - if ts.route_post then - advtrains.interlocking.route.free_route_locks(ts_id, ts.route_post.locks) - if ts.route_post.next then - --this does nothing when the train went the right way, because - -- "route" info is already cleared. - advtrains.interlocking.route.cancel_route_from(ts.route_post.next) - end - ts.route_post = nil - end - -- This must be delayed, because this code is executed in-between a train step - -- TODO use luaautomation timers? - minetest.after(0, advtrains.interlocking.route.update_waiting, "ts", ts_id) -end - - --- This is regular operation --- The train is on a track and drives back and forth - --- This sets the section for both directions, to be failsafe -advtrains.tnc_register_on_enter(function(pos, id, train, index) - local tcb = ildb.get_tcb(pos) - if tcb then - for connid=1,2 do - local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) - if ts then - setsection(id, train, tcb[connid].ts_id, ts, {p=pos, s=connid}) - end - end - end -end) - - --- this time, of course, only clear the backside (cp connid) -advtrains.tnc_register_on_leave(function(pos, id, train, index) - local tcb = ildb.get_tcb(pos) - if tcb and train.path_cp[index] then - local connid = train.path_cp[index] - local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) - if ts then - freesection(id, train, tcb[connid].ts_id, ts) - end - end -end) - --- those callbacks are needed to account for created and removed trains (also regarding coupling) - -advtrains.te_register_on_create(function(id, train) - -- let's see what track sections we find here - local index = atround(train.index) - local pos = advtrains.path_get(train, index) - local ts_id, origin = ildb.get_ts_at_pos(pos) - if ts_id then - local ts = ildb.get_ts(ts_id) - if ts then - setsection(id, train, ts_id, ts, origin) - else - atwarn("ILDB corruption: TCB",origin," has invalid TS reference") - end - -- Make train a shunt move - train.is_shunt = true - elseif ts_id==nil then - atlog("Train",id,": Unable to determine whether to block a track section!") - else - --atdebug("Train",id,": Outside of interlocked area!") - end -end) - -advtrains.te_register_on_remove(function(id, train) - if train.il_sections then - for idx, item in ipairs(train.il_sections) do - - local ts = item.ts_id and ildb.get_ts(item.ts_id) - - if ts and ts.trains then - itremove(ts.trains, id) - end - end - train.il_sections = nil - end -end) diff --git a/advtrains_interlocking/tsr_rail.lua b/advtrains_interlocking/tsr_rail.lua deleted file mode 100644 index a500c8f..0000000 --- a/advtrains_interlocking/tsr_rail.lua +++ /dev/null @@ -1,56 +0,0 @@ --- tsr_rail.lua --- Point speed restriction rails --- Simple rail whose only purpose is to place a TSR on the position, as a temporary solution until the timetable system covers everything. --- This code resembles the code in lines/stoprail.lua - -local function updateform(pos) - local meta = minetest.get_meta(pos) - local pe = advtrains.encode_pos(pos) - local npr = advtrains.interlocking.npr_rails[pe] or 2 - - meta:set_string("infotext", "Point speed restriction: "..npr) - meta:set_string("formspec", "field[npr;Set point speed restriction:;"..npr.."]") -end - - -local adefunc = function(def, preset, suffix, rotation) - return { - after_place_node=function(pos) - updateform(pos) - end, - after_dig_node=function(pos) - local pe = advtrains.encode_pos(pos) - advtrains.interlocking.npr_rails[pe] = nil - end, - on_receive_fields = function(pos, formname, fields, player) - if fields.npr then - local pe = advtrains.encode_pos(pos) - advtrains.interlocking.npr_rails[pe] = tonumber(fields.npr) - updateform(pos) - end - end, - advtrains = { - on_train_approach = function(pos,train_id, train, index) - if train.path_cn[index] == 1 then - local pe = advtrains.encode_pos(pos) - local npr = advtrains.interlocking.npr_rails[pe] or 2 - advtrains.lzb_add_checkpoint(train, index, npr, nil) - end - end, - }, - } -end - - -if minetest.get_modpath("advtrains_train_track") ~= nil then - advtrains.register_tracks("default", { - nodename_prefix="advtrains_interlocking:dtrack_npr", - texture_prefix="advtrains_dtrack_npr", - models_prefix="advtrains_dtrack", - models_suffix=".b3d", - shared_texture="advtrains_dtrack_shared_npr.png", - description="Point Speed Restriction Rail", - formats={}, - get_additional_definiton = adefunc, - }, advtrains.trackpresets.t_30deg_straightonly) -end
\ No newline at end of file |