diff options
-rw-r--r-- | advtrains/track_reg_helper.lua | 8 | ||||
-rw-r--r-- | advtrains_interlocking/ars.lua | 296 | ||||
-rw-r--r-- | advtrains_interlocking/database.lua | 5 | ||||
-rw-r--r-- | advtrains_interlocking/routesetting.lua | 85 | ||||
-rw-r--r-- | advtrains_interlocking/signal_aspect_ui.lua | 4 | ||||
-rwxr-xr-x | advtrains_interlocking/tcb_ts_ui.lua | 33 | ||||
-rw-r--r-- | advtrains_line_automation/stoprail.lua | 7 | ||||
-rw-r--r-- | advtrains_luaautomation/atc_rail.lua | 7 | ||||
-rw-r--r-- | advtrains_signals_japan/init.lua | 17 |
9 files changed, 350 insertions, 112 deletions
diff --git a/advtrains/track_reg_helper.lua b/advtrains/track_reg_helper.lua index 31741d6..e2f71e8 100644 --- a/advtrains/track_reg_helper.lua +++ b/advtrains/track_reg_helper.lua @@ -484,6 +484,11 @@ local function append_statemap_suffix(state_map, nnpref, rot) return t end +function advtrains.default_suitable_substrate(upos) + return core.registered_nodes[core.get_node(upos).name] + and core.registered_nodes[core.get_node(upos).name].walkable +end + function advtrains.register_tracks(tracktype, def, preset) if not preset.v25_format then error("advtrains.register_tracks(): A track preset for pre-v2.5 is used with advtrains 2.5+. Mod probably defines own track preset instead of using it from the advtrains.ap table! Please check track mod compatibility!") @@ -509,8 +514,7 @@ function advtrains.register_tracks(tracktype, def, preset) return itemstack, false end if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then - local s = minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable - if s then + if (def.suitable_substrate and def.suitable_substrate or advtrains.default_suitable_substrate)(upos) then -- minetest.chat_send_all(nnprefix) local yaw = placer:get_look_horizontal() advtrains.trackplacer.place_track(pos, nnprefix, name, yaw) diff --git a/advtrains_interlocking/ars.lua b/advtrains_interlocking/ars.lua index eb10497..5182cd3 100644 --- a/advtrains_interlocking/ars.lua +++ b/advtrains_interlocking/ars.lua @@ -3,26 +3,89 @@ --[[ 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 + Every route has (or can have) an associated ARS table: + ars = { + [n] = { + ln = "<line>" -- either line + rc = "<routingcode>" -- or routingcode + n = true/false -- true = logical not (matches everything that does not have this line/rc) + conj = { -- and conjunction, optional. This must be true in addition to the main rule + ln=... / rc=... / n=... -- like toplevel + conj=... -- can be nested + -- note that conj cannot have prio, in inherits the prio from main rule + } + prio = <num> -- optional, a priority number. If set, enables "multi-ARS" where train can wait for multiple routes at once + -- or + c="<a comment>" + } + default = true -- Route is the default route for trains + default_prio -- optional, priority for multi-ars with default route + default_conj = {...} -- optional, conditions (conjunction) that need to be fulfilled for this to be considered default route + } - Compound ("and") conjunctions are not supported (--TODO should they?) + In case a train matches the ARS rules of multiple routes, precedence is as follows: + 1. train matches rules without priority in one or more routes -> first matching route is unconditionally set + 2. train matches rules with priority in one or more routes -> all of the matching routes are set (multi-ars) + in the order given by priority, first available route is set + 3. route is default=true, default_prio=nil and train matches default_conj (if present) -> first default route is set + 4. one or more routes are default=true, with default_prio set,and train matches default_conj (if present) + -> all of the matching routes are set (multi-ars) in the order given by priority, first available route is set For editing, those tables are transformed into lines in a text area: {ln=...} -> LN ... {rc=...} -> RC ... {c=...} -> #... {default=true} -> * - See also route_ui.lua + n -> ! (e.g. ln=..., n=true -> !LN ...) + prio -> <num> (e.g. ln=..., prio=1 -> 1 LN ...) + + conj -> goes on the next line, with an & prepended, e.g.: + {ln="S1", conj={rc="Left"}} -> + LN S1 + & RC Left + + Example combining everything: + ars = { + [1] = { + ln = "S1" + n = true + prio = 4 + conj = { + rc = "R4" + } + } + default = true + default_prio = 2 + default_conj = { + rc = "R5" + n = true + } + } -> + 4 !LN S1 + & RC R4 + 2 * + & !RC R5 + ]] local il = advtrains.interlocking + +local function conj_to_text(conj, txt) + while conj do + n = "" + if conj.n then + n = "!" + end + if conj.ln then + txt[#txt+1] = "& "..n.."LN "..conj.ln + elseif conj.rc then + txt[#txt+1] = "& "..n.."RC "..conj.rc + end + conj = conj.conj + end +end + -- 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 @@ -32,103 +95,213 @@ function il.ars_to_text(arstab) local txt = {} for i, arsent in ipairs(arstab) do + local prio = "" + if arsent.prio then + prio = arsent.prio.." " + end local n = "" if arsent.n then n = "!" end if arsent.ln then - txt[#txt+1] = n.."LN "..arsent.ln + txt[#txt+1] = prio..n.."LN "..arsent.ln elseif arsent.rc then - txt[#txt+1] = n.."RC "..arsent.rc + txt[#txt+1] = prio..n.."RC "..arsent.rc elseif arsent.c then txt[#txt+1] = "#"..arsent.c end + conj_to_text(arsent.conj, txt) end if arstab.default then - return "*\n" .. table.concat(txt, "\n") + local prio = "" + if arstab.default_prio then + prio = arstab.default_prio.." " + end + txt[#txt+1] = prio.."*" + conj_to_text(arstab.default_conj, txt) end return table.concat(txt, "\n") end +local function parse_ruleexp(line) + local excl, key, val = string.match(line, "^%s*(!?)%s*([RL][CN])%s+(.+)%s*$") + if key == "RC" then + return {rc=val, n=(excl=="!")} + elseif key == "LN" then + return {ln=val, n=(excl=="!")} + end +end + function il.text_to_ars(t) if not string.match(t, "%S+") then return nil - elseif t=="*" then - return {default=true} end local arstab = {} + local previtem for line in string.gmatch(t, "[^\r\n]+") do - if line=="*" then - arstab.default = true + -- a) comment + local ct = string.match(line, "^#(.*)$") + if ct then + arstab[#arstab+1] = {c = ct} + previtem = nil 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} + -- b) Conjunction to the previous item + local conline = string.match(line, "^%s*&(.+)$") + if conline then + local conj = parse_ruleexp(conline) + if conj and previtem==true then + -- previtem was default + arstab.default_conj = conj + previtem = conj + elseif conj and previtem then + previtem.conj = conj + previtem = conj + else + -- dont know what to do with line, put as comment + arstab[#arstab+1] = {c = "? "..line} + previtem = nil end else - local ct = string.match(line, "^#(.*)$") - if ct then arstab[#arstab+1] = {c = ct} end + -- c) Normal rule spec + local prio, ruleline = string.match(line, "^%s*([0-9]*)%s*(.+)%s*$") + if ruleline == "*" then + -- ruleline is the asterisk, this is default + arstab.default = true + arstab.default_prio = tonumber(prio) -- evals to nil if not given + previtem = true -- marks that previtem was asterisk + elseif ruleline then + -- ruleline is present, parse it + local rule = parse_ruleexp(ruleline) + if not rule then + -- dont know what to do with line, put as comment + arstab[#arstab+1] = {c = "? "..line} + previtem = nil + else + rule.prio = tonumber(prio) -- evals to nil if not given + arstab[#arstab+1] = rule + previtem = rule + end + else + -- d) nothing else works, save line as comment + arstab[#arstab+1] = {c = "? "..line} + previtem = nil + 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 + +local function match_arsent(arsent, train) + local rule_matches = false + if arsent.ln then + local line = train.line + rule_matches = line and arsent.ln == line + if arsent.n then rule_matches = not rule_matches end + elseif arsent.rc then + local routingcode = train.routingcode + rule_matches = routingcode and string.find(" "..routingcode.." ", " "..arsent.rc.." ", nil, true) + if arsent.n then rule_matches = not rule_matches end + end + if rule_matches then + -- if the entry has a conjunction, go on checking + if arsent.conj then + return match_arsent(arsent.conj, train) + else + return true end + else + return false 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 +-- Given an ARS rule table, check whether any of the clauses in it match the train. +-- Returns: match_specific, match_default +-- match_specific: One of the clauses explicitly matched (if this is non-false, match_default is not checked and always given false) +-- match_default: The default clause (*) matched (as well as any conjunctions attached to the default clause) +-- both of these can be either true (unconditional match), a number (priority for multi-ars) or false function il.ars_check_rule_match(ars, train) if not ars then - return nil + return nil, 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 + local rule_matches = match_arsent(arsent, train) + if rule_matches then + return (arsent.prio or true), 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 + if ars.default then + local def_matches = true + if ars.default_conj then + def_matches = match_arsent(ars.default_conj, train) end + if def_matches then + return false, (ars.default_prio or true) + end + end + return false,false +end + +local function sort_priority(sprio) + -- insertion sort + local order = {} + local oprio = {} + for rteid, prio in pairs(sprio) do + local inspos = 1 + while order[inspos] do + if oprio[inspos] > prio then + -- next item has higher priority number (= less urgent), insert before it + break + elseif oprio[inspos] == prio and order[inspos] > rteid then + -- next item has same priority as current and a higher routeid, insert before it + break + end + inspos = inspos + 1 end + table.insert(order, inspos, rteid) + table.insert(oprio, inspos, prio) + end + --atdebug("sort_priority",sprio,"result",order,oprio) + if #order == 1 then + return order[1] -- only one route, table doesnt make sense + end + return order +end + +local function find_rtematch(routes, train) + local sprio = {} + local default = nil + local dprio = {} + for rteid, route in ipairs(routes) do + if route.ars then + local mspec, mdefault = il.ars_check_rule_match(route.ars, train) + --atdebug("route",rteid,"ars",route.ars,"gives", mspec, mdefault) + if mspec == true then + return rteid + elseif mspec then + sprio[rteid] = mspec + end + if mdefault == true then + if not default then default = rteid end + elseif mdefault then + dprio[rteid] = mdefault + end + end + end + if next(sprio) then + return sort_priority(sprio) + elseif default then + return default + elseif next(dprio) then + return sort_priority(dprio) + else return nil + end end + function advtrains.interlocking.ars_check(signalpos, train, trig_from_dst) -- check for distant signal -- this whole check must be delayed until after the route setting has taken place, @@ -169,6 +342,7 @@ function advtrains.interlocking.ars_check(signalpos, train, trig_from_dst) local rteid = find_rtematch(tcbs.routes, train) if rteid then + --atdebug("Ars setting ",rteid) --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) diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 844d350..5e35dba 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -214,10 +214,11 @@ TCB data structure -- 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, + -- routeset may be a table (e.g. {1,2}) while the route is not committed yet, indicating a wait for multiple routes at once (Multi-ARS) + route_committed = <boolean> -- When setting/requesting a route, routeset 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 + -- is set true, clearing the signal (when this is true, routeset is never a table) 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 diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 1065cad..9901d23 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -363,7 +363,7 @@ end -- 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 +-- newrte: If a new route should be set, the route index of it (in tcbs.routes). Can also be a table (multi-ars). 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) @@ -390,37 +390,66 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) if tcbs.route_committed then return end - if newrte then tcbs.routeset = newrte end + if newrte then + if type(newrte)=="table" and not next(newrte) then + error("update_route got multi-ARS with empty table, this is not allowed") + end + tcbs.routeset = newrte + else + if type(tcbs.routeset)=="table" and not next(tcbs.routeset) then + -- just unset, don't error + atwarn(sigd, "had multi-ARS route set with empty list! Cancelled!") + tcbs.routeset = nil + return + end + end --atdebug("Setting:",tcbs.routeset) - local succ, rsn, cbts, cblk - local route = tcbs.routes[tcbs.routeset] - if route then - succ, rsn, cbts, cblk = ilrs.set_route(sigd, route) + -- check: single-ars or multi-ars? + local multi_rte + if type(tcbs.routeset) == "table" then + multi_rte = tcbs.routeset else - succ = false - rsn = attrans("Route state changed.") + multi_rte = {tcbs.routeset} end - 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) + for multi_idx, rteid in ipairs(multi_rte) do + local succ, rsn, cbts, cblk + local route = tcbs.routes[rteid] + if route then + succ, rsn, cbts, cblk = ilrs.set_route(sigd, route) + else + succ = false + rsn = attrans("Route with index @1 not found", rteid) end - else - --atdebug("Committed Route:",tcbs.routeset) - -- set_route now sets the signal aspects - --has_changed_aspect = true - -- route success. apply default_autoworking flag if requested - if route.default_autoworking then - tcbs.route_auto = true --FIX 2025-01-08: never set it to false if it was true! + if not succ then + if multi_idx==1 then + tcbs.route_rsn = rsn + else + tcbs.route_rsn = (tcbs.route_rsn or "").."\n"..rsn + end + --atdebug("Routesetting",rteid,"failed:",rsn,"(multi-idx",multi_idx,")") + -- 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:",rteid,"(multi-idx",multi_idx,")") + -- replace multi_route by single actually committed route + tcbs.routeset = rteid + -- set_route now sets the signal aspects + --has_changed_aspect = true + -- route success. apply default_autoworking flag if requested + if route.default_autoworking then + tcbs.route_auto = true --FIX 2025-01-08: never set it to false if it was true! + end + -- break out of the for loop, dont try any more routes + break end end end diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua index 98a332a..49e7d8b 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -247,7 +247,9 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) ipmarker(pos, plconnid) minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point") -- Try to find a TCB ahead and auto assign this signal there - if advtrains.interlocking.signal.get_signal_cap_level(signalpos) >= 2 then + local pc = player:get_player_control() + local no_auto_assign = pc.aux1 + if not no_auto_assign and advtrains.interlocking.signal.get_signal_cap_level(signalpos) >= 2 then try_auto_assign_to_tcb(signalpos, pos, plconnid, pname) end else diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 814a11a..0be943a 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -828,14 +828,29 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle 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 + if type(tcbs.routeset)=="table" then + local rtenames = {} + for midx,rteid in ipairs(tcbs.routeset) do + local rte = tcbs.routes[rteid] + if not rte then + atwarn("Unknown route set from signal!") + tcbs.routeset = nil + return + end + rtenames[midx] = rte.name + end + form = form.."label[0.5,2.5;Multiple routes are requested (first available is set):]" + form = form.."label[0.5,3.0;"..minetest.formspec_escape(table.concat(rtenames,", ")).."]" + else + 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).."]" 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 @@ -1000,9 +1015,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end if tcbs.routeset and fields.cancelroute then - if tcbs.routes[tcbs.routeset] and tcbs.routes[tcbs.routeset].ars then - tcbs.ars_ignore_next = true - end + tcbs.ars_ignore_next = true -- if route committed, cancel route ts info ilrs.update_route(sigd, tcbs, nil, true) end diff --git a/advtrains_line_automation/stoprail.lua b/advtrains_line_automation/stoprail.lua index 89f4a09..aa8dfdd 100644 --- a/advtrains_line_automation/stoprail.lua +++ b/advtrains_line_automation/stoprail.lua @@ -24,7 +24,8 @@ local function updatemeta(pos) end local door_dropdown = {L=1, R=2, C=3} -local door_dropdown_rev = {Right="R", Left="L", Closed="C"} +--local door_dropdown_rev = {Right="R", Left="L", Closed="C"} -- Code review : why are the value in an order different than the one in the dropdown box ? +local door_dropdown_code = {"L", "R", "C"} -- switch to numerical index of selection : for conversion of the numerical index in the opening side selection dropdown box to the internal codification local function show_stoprailform(pos, player) local pe = advtrains.encode_pos(pos) @@ -60,7 +61,7 @@ local function show_stoprailform(pos, player) form = form.."field[4.30,2.0;1.75,1;track;"..S("Track")..";"..minetest.formspec_escape(stdata.track).."]" form = form.."field[6.05,2.0;1.75,1;wait;"..S("Stop Time")..";"..stdata.wait.."]" form = form.."label[0.5,2.6;"..S("Door Side").."]" - form = form.."dropdown[0.51,3.0;2;doors;"..S("Left")..","..S("Right")..","..S("Closed")..";"..door_dropdown[stdata.doors].."]" + form = form.."dropdown[0.51,3.0;2;doors;"..S("Left")..","..S("Right")..","..S("Closed")..";"..door_dropdown[stdata.doors]..";true]" -- switch to numerical index of the selection form = form.."checkbox[3.00,2.4;reverse;"..S("Reverse train")..";"..(stdata.reverse and "true" or "false").."]" form = form.."checkbox[3.00,2.8;kick;"..S("Kick out passengers")..";"..(stdata.kick and "true" or "false").."]" form = form.."checkbox[3.00,3.2;waitsig;"..S("Wait for signal to clear")..";"..(stdata.waitsig and "true" or "false").."]" @@ -120,7 +121,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) -- dropdowns if fields.doors then - stdata.doors = door_dropdown_rev[fields.doors] or "C" + stdata.doors = door_dropdown_code[tonumber(fields.doors)] or "C" -- switch to numerical index of selection; attention : fields.doors is string typed, needed to be converted to an integer typed index in door_dropdown_code table end if fields.track then diff --git a/advtrains_luaautomation/atc_rail.lua b/advtrains_luaautomation/atc_rail.lua index dd26f51..c98f62b 100644 --- a/advtrains_luaautomation/atc_rail.lua +++ b/advtrains_luaautomation/atc_rail.lua @@ -62,7 +62,7 @@ function r.fire_event(pos, evtdata, appr_internal) local new_id = advtrains.split_train_at_index(train, index) if new_id then minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow) - return true + return new_id end return false end, @@ -73,7 +73,7 @@ function r.fire_event(pos, evtdata, appr_internal) if new_id then minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow) end - return fc or "" + return (fc or ""), new_id end, split_off_locomotive = function(cmd, len) assertt(cmd, "string") @@ -81,7 +81,8 @@ function r.fire_event(pos, evtdata, appr_internal) local new_id, fc = advtrains.split_train_at_fc(train, true, len) if new_id then minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow) - end + end + return (fc or ""), new_id end, train_length = function () if not train_id then return false end diff --git a/advtrains_signals_japan/init.lua b/advtrains_signals_japan/init.lua index 1140b6b..728a91f 100644 --- a/advtrains_signals_japan/init.lua +++ b/advtrains_signals_japan/init.lua @@ -15,6 +15,19 @@ local light_distant = light_purple local light_off = signal_face_texture do + local model_dir = core.get_modpath("advtrains_signals_japan") .. DIR_DELIM .. "models" + local function add_model(name, content) + local fn = "advtrains_signals_japan_" .. name + if core.features.dynamic_add_media_startup then + core.dynamic_add_media { + filename = fn, + filedata = content, + } + else + core.mkdir(model_dir) + core.safe_file_write(model_dir .. DIR_DELIM .. fn, content) + end + end local model_path_prefix = table.concat({minetest.get_modpath("advtrains_signals_japan"), "models", "advtrains_signals_japan_"}, DIR_DELIM) local function vertex(x, y, z) @@ -122,7 +135,7 @@ do pole_vertices = table.concat(pole_vertices, "\n") pole_objdef = table.concat(pole_objdef, "\n") pole_uv = table.concat(pole_uv, "\n") - minetest.safe_file_write(model_path_prefix .. "pole.obj", table.concat({pole_vertices, pole_uv, pole_objdef}, "\n")) + add_model("pole.obj", table.concat({pole_vertices, pole_uv, pole_objdef}, "\n")) -- generate signals for lightcount = 5, 6 do @@ -231,7 +244,7 @@ do face_vertices = table.concat(face_vertices, "\n") face_uv = table.concat(face_uv, "\n") face_objdef = table.concat(face_objdef, "\n") - minetest.safe_file_write(model_path_prefix .. lightcount .. "_" .. rotname .. ".obj", table.concat({ + add_model(lightcount .. "_" .. rotname .. ".obj", table.concat({ pole_vertices, face_vertices, table.concat(light_vertices, "\n"), |