diff options
Diffstat (limited to 'advtrains_line_automation')
-rw-r--r-- | advtrains_line_automation/init.lua | 47 | ||||
-rw-r--r-- | advtrains_line_automation/mod.conf | 7 | ||||
-rw-r--r-- | advtrains_line_automation/railwaytime.lua | 308 | ||||
-rw-r--r-- | advtrains_line_automation/readme.txt | 5 | ||||
-rw-r--r-- | advtrains_line_automation/scheduler.lua | 133 | ||||
-rw-r--r-- | advtrains_line_automation/settingtypes.txt | 6 | ||||
-rw-r--r-- | advtrains_line_automation/stoprail.lua | 225 | ||||
-rw-r--r-- | advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png | bin | 0 -> 3306 bytes | |||
-rw-r--r-- | advtrains_line_automation/textures/advtrains_dtrack_stop_placer.png | bin | 0 -> 1238 bytes |
9 files changed, 731 insertions, 0 deletions
diff --git a/advtrains_line_automation/init.lua b/advtrains_line_automation/init.lua new file mode 100644 index 0000000..7b758bc --- /dev/null +++ b/advtrains_line_automation/init.lua @@ -0,0 +1,47 @@ +-- Advtrains line automation system + +advtrains.lines = { + -- [station code] = {name=..., owner=...} + stations = {}, + + --[[ [new pos hash] = { + stn = <station code>, + track = <platform identifier>, + doors = <door side L,R,C> + wait = <least wait time> + reverse = <boolean> + signal = <position of signal that is the "exit signal" for this platform> + }]] + stops = {}, +} + + +local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM + +dofile(modpath.."railwaytime.lua") +dofile(modpath.."scheduler.lua") +dofile(modpath.."stoprail.lua") + + +function advtrains.lines.load(data) + if data then + advtrains.lines.stations = data.stations or {} + advtrains.lines.stops = data.stops or {} + advtrains.lines.rwt.set_time(data.rwt_time) + advtrains.lines.sched.load(data.scheduler_queue) + end +end + +function advtrains.lines.save() + return { + stations = advtrains.lines.stations, + stops = advtrains.lines.stops, + rwt_time = advtrains.lines.rwt.get_time(), + scheduler_queue = advtrains.lines.sched.save() + } +end + +function advtrains.lines.step(dtime) + advtrains.lines.rwt.step(dtime) + advtrains.lines.sched.run() +end diff --git a/advtrains_line_automation/mod.conf b/advtrains_line_automation/mod.conf new file mode 100644 index 0000000..e9851c8 --- /dev/null +++ b/advtrains_line_automation/mod.conf @@ -0,0 +1,7 @@ +name=advtrains_line_automation +title=Advanced Trains Line Automation +description=Tools for automatic train lines +author=orwell96 + +depends=advtrains_interlocking +optional_depends=advtrains_train_track diff --git a/advtrains_line_automation/railwaytime.lua b/advtrains_line_automation/railwaytime.lua new file mode 100644 index 0000000..258009e --- /dev/null +++ b/advtrains_line_automation/railwaytime.lua @@ -0,0 +1,308 @@ +-- railwaytime.lua +-- Advtrains uses a desynchronized time for train movement. Everything is counted relative to this time counter. +-- The advtrains-internal time is in no way synchronized to the real-life time, due to: +-- - Lag +-- - Server stops/restarts +-- However, this means that implementing a "timetable" system using the "real time" is not practical. Therefore, +-- we introduce a custom time system, the RWT(Railway Time), which has nothing to do with RLT(Real-Life Time) +-- RWT has a time cycle of 1 hour. This should be sufficient for most train lines that will ever be built in Minetest. +-- A RWT looks like this: 37;25 +-- The ; is to distinguish it from a normal RLT (which has colons e.g. 12:34:56). Left number is minutes, right number is seconds. +-- The minimum RWT is 00;00, the maximum is 59;59. +-- It is OK to leave one places out at either end, esp. when writing relative times, such as: +-- 43;3 22;0 2;30 0;10 ;10 +-- Those places are then filled with zeroes. Indeed, ";" would be valid for 00;00 . + +-- There is an "adapt mode", which was proposed by gpcf, and results in RWT automatically adapting itself to real-world time. +-- It works by shifting the minute/second after the realtime minute/second, adjusting the cycle value as needed. + +-- Using negative times is discouraged. If you need a negative time, you may insert a minus (-) ONLY in the "c" place + +--[[ +1;23;45 = { + s=45, + m=23, + c=1, -- Cycle(~hour), not displayed most time +} + +Railway times can exist in 3 forms: +- as table (see above) +- as string (like "12;34") +- as number (of seconds) + +Forms are automagically converted as needed by the converter functions to_* +To be sure a rwt is in the required form, explicitly use a converter. + +]] + +local rwt = {} + +--Time Stamp (Seconds since start of world) +local e_time = 0 +local e_has_loaded = false + +local setting_rwt_real = minetest.settings:get("advtrains_lines_rwt_realtime") +if setting_rwt_real=="" then + setting_rwt_real = "independent" +end + +local e_last_epoch -- last real-time timestamp + +-- Advance RWT to match minute/second to the current real-world time +-- only accounts for the minute/second part, leaves hour/cycle untouched +local function adapt_real_time() + local datetab = os.date("*t") + local real_sectotal = 60*datetab.min + datetab.sec + + local rwttab = rwt.now() + local rwt_sectotal = 60*rwttab.m + rwttab.s + + --calculate the difference and take it %3600 (seconds/hour) to always move forward + local secsfwd = (real_sectotal - rwt_sectotal) % 3600 + + atlog("[lines][rwt] Skipping",secsfwd,"seconds forward to sync rwt (",rwt.to_string(rwttab),") to real time (",os.date("%H:%M:%S"),")") + + e_time = e_time + secsfwd +end + +function rwt.set_time(t) + e_time = t or 0 + if setting_rwt_real == "adapt_real" then + adapt_real_time() + end + atlog("[lines][rwt] Initialized railway time: ",rwt.to_string(e_time)) + e_last_epoch = os.time() + + e_has_loaded = true +end + +function rwt.get_time() + return e_time +end + +function rwt.step(dt) + if not e_has_loaded then + rwt.set_time(0) + end + + if setting_rwt_real=="independent" then + -- Regular stepping with dtime + e_time = e_time + dt + else + -- advance with real-world time + local diff = os.time() - e_last_epoch + e_last_epoch = os.time() + + if diff>0 then + e_time = e_time + diff + end + end +end + +function rwt.now() + return rwt.to_table(e_time) +end + +function rwt.new(c, m, s) + return { + c = c or 0, + m = m or 0, + s = s or 0 + } +end +function rwt.copy(rwtime) + local rwtimet = rwt.to_table(rwtime) + return { + c = rwtimet.c or 0, + m = rwtimet.m or 0, + s = rwtimet.s or 0 + } +end + +function rwt.to_table(rwtime) + if type(rwtime) == "table" then + return rwtime + elseif type(rwtime) == "string" then + return rwt.parse(rwtime) + elseif type(rwtime) == "number" then + local res = {} + local seconds = atfloor(rwtime) + res.s = seconds % 60 + local minutes = atfloor(seconds/60) + res.m = minutes % 60 + res.c = atfloor(minutes/60) + return res + end +end + +function rwt.to_secs(rwtime, c_over) + local res = rwtime + if type(rwtime) == "string" then + res = rwt.parse(rwtime) + elseif type(rwtime) == "number" then + return rwtime + end + if type(res)=="table" then + return (c_over or res.c)*60*60 + res.m*60 + res.s + end +end + +function rwt.to_string(rwtime_p, no_cycle) + local rwtime = rwt.to_table(rwtime_p) + if rwtime.c~=0 and not no_cycle then + return string.format("%d;%02d;%02d", rwtime.c, rwtime.m, rwtime.s) + else + return string.format("%02d;%02d", rwtime.m, rwtime.s) + end +end + +--- + +local function v_n(str, cpl) + if not str then return nil end + if str == "" then + return 0 + end + local n = tonumber(str) + if not cpl and (n<0 or n>59) then + return nil + end + return n +end + +function rwt.parse(str) + --atdebug("parse",str) + --3-value form + local str_c, str_m, str_s = string.match(str, "^(%-?%d?%d?);(%d%d);(%d?%d?)$") + if str_c and str_m and str_s then + --atdebug("3v",str_c, str_m, str_s) + local c, m, s = v_n(str_c, true), v_n(str_m), v_n(str_s) + if c and m and s then + return rwt.new(c,m,s) + end + end + --2-value form + local str_m, str_s = string.match(str, "^(%d?%d?);(%d?%d?)$") + if str_m and str_s then + --atdebug("2v",str_m, str_s) + local m, s = v_n(str_m), v_n(str_s) + if m and s then + return rwt.new(0,m,s) + end + end +end + +--- + +function rwt.add(t1, t2) + local t1s = rwt.to_secs(t1) + local t2s = rwt.to_secs(t2) + return rwt.to_table(t1s + t2s) +end + +-- How many seconds FROM t1 TO t2 +function rwt.diff(t1, t2) + local t1s = rwt.to_secs(t1) + local t2s = rwt.to_secs(t2) + return t2s - t1s +end + +-- Subtract t2 from t1 (inverted argument order compared to diff()) +function rwt.sub(t1, t2) + return rwt.to_table(rwt.diff(t2, t1)) +end + +-- Adjusts t2 by thresh and then returns time from t1 to t2 +function rwt.adj_diff(t1, t2, thresh) + local newc = rwt.adjust_cycle(t2, thresh, t1) + local t1s = rwt.to_secs(t1) + local t2s = rwt.to_secs(t2, newc) + return t1s - t2s +end + + + +-- Threshold values +-- "reftime" is the time to which this is made relative and defaults to now. +rwt.CA_FUTURE = 60*60 - 1 -- Selected so that time lies at or in the future of reftime (at nearest point in time) +rwt.CA_FUTURES = 60*60 -- Same, except when times are equal, advances one full cycle +rwt.CA_PAST = 0 -- Selected so that time lies at or in the past of reftime +rwt.CA_PASTS = -1 -- Same, except when times are equal, goes back one full cycle +rwt.CA_CENTER = 30*60 -- If time is within past 30 minutes of reftime, selected as past, else selected as future. + +-- Adjusts the "cycle" value of a railway time to be in some relation to reftime. +-- Returns new cycle +function rwt.adjust_cycle(rwtime, reftime_p, thresh) + local reftime = reftime_p or rwt.now() + + local reftimes = rwt.to_secs(reftime) + + local rwtimes = rwt.to_secs(rwtime, 0) + local timeres = reftimes + thresh - rwtimes + local cycles = atfloor(timeres / (60*60)) + + return cycles +end + +function rwt.adjust(rwtime, reftime, thresh) + local cp = rwt.copy(rwtime) + cp.c = rwt.adjust_cycle(rwtime, reftime, thresh) + return cp +end + +-- Useful for departure times: returns time (in seconds) +-- until the next (adjusted FUTURE) occurence of deptime is reached +-- in this case, rwtime is used as reftime and deptime should lie in the future of rwtime +-- rwtime defaults to NOW +function rwt.get_time_until(deptime, rwtime_p) + local rwtime = rwtime_p or rwt.now() + return rwt.adj_diff(rwtime, deptime, rwt.CA_FUTURE) +end + + +-- Helper functions for handling "repeating times" (rpt) +-- Those are generic declarations for time intervals like "every 5 minutes", with an optional offset +-- ( /02;00-00;45 in timetable syntax + +-- Get the time (in seconds) until the next time this rpt occurs +function rwt.time_to_next_rpt(rwtime, rpt_interval, rpt_offset) + local rpti_s = rwt.to_secs(rpt_interval) + + return (rpti_s - rwt.time_from_last_rpt(rwtime, rpti_s, rpt_offset)) % rpti_s + -- Modulo is just there to clip a false value of rpti_s to 0 +end + + +-- Get the time (in seconds) since the last time this rpt occured +function rwt.time_from_last_rpt(rwtime, rpt_interval, rpt_offset) + local rwtime_s = rwt.to_secs(rwtime) + local rpti_s = rwt.to_secs(rpt_interval) + local rpto_s = rwt.to_secs(rpt_offset) + + return ((rwtime_s - rpto_s) % rpti_s) +end + +-- From rwtime, get the next time that is divisible by rpt_interval offset by rpt_offset +function rwt.next_rpt(rwtime, rpt_interval, rpt_offset) + local rwtime_s = rwt.to_secs(rwtime) + local rpti_s = rwt.to_secs(rpt_interval) + local time_from_last = rwt.time_from_last_rpt(rwtime_s, rpti_s, rpt_offset) + + local res_s = rwtime_s - time_from_last + rpti_s + + return rwt.to_table(res_s) +end + +-- from rwtime, get the last time that this rpt matched (which is actually just next_rpt - rpt_offset +function rwt.last_rpt(rwtime, rpt_interval, rpt_offset) + local rwtime_s = rwt.to_sec(rwtime) + local rpti_s = rwt.to_sec(rpt_interval) + local time_from_last = rwt.time_from_last_rpt(rwtime, rpt_interval, rpt_offset) + + local res_s = rwtime_s - time_from_last + + return rwt.to_table(res_s) +end + + +advtrains.lines.rwt = rwt diff --git a/advtrains_line_automation/readme.txt b/advtrains_line_automation/readme.txt new file mode 100644 index 0000000..3280ce9 --- /dev/null +++ b/advtrains_line_automation/readme.txt @@ -0,0 +1,5 @@ +== advtrains_line_automation +This mod provides an extension to the interlocking system which allows to automatically operate trains on train lines. + +This extension makes use of the table +advtrains.lines
\ No newline at end of file diff --git a/advtrains_line_automation/scheduler.lua b/advtrains_line_automation/scheduler.lua new file mode 100644 index 0000000..6025b02 --- /dev/null +++ b/advtrains_line_automation/scheduler.lua @@ -0,0 +1,133 @@ +-- scheduler.lua +-- Implementation of a Railway time schedule queue +-- In contrast to the LuaATC interrupt queue, this one can handle many different +-- event receivers. This is done by registering a callback with the scheduler + +local ln = advtrains.lines +local sched = {} + +local UNITS_THRESH = 10 +local MAX_PER_ITER = 10 + +local callbacks = {} + +-- Register a handler callback to handle scheduler items. +-- e - a handler identifier (corresponds to "handler" in enqueue() ) +-- func - a function(evtdata) to be executed when a schedule item expires +-- evtdata - arbitrary data that has been passed into enqueue() +function sched.register_callback(e, func) + callbacks[e] = func +end + +--[[ +{ + t = <railway time in seconds> + e = <handler callback> + d = <data table> + u = <unit identifier> +} +The "unit identifier" is there to prevent schedule overflows. It can be, for example, the position hash +of a node or a train ID. If the number of schedules for a unit exceeds UNITS_THRESH, further schedules are +blocked. +]]-- +local queue = {} + +local units_cnt = {} + +function sched.load(data) + if data then + for i,elem in ipairs(data) do + table.insert(queue, elem) + units_cnt[elem.u] = (units_cnt[elem.u] or 0) + 1 + end + atlog("[lines][scheduler] Loaded the schedule queue,",#data,"items.") + end +end +function sched.save() + return queue +end + +function sched.run() + local ctime = ln.rwt.get_time() + local cnt = 0 + local ucn, elem + while cnt <= MAX_PER_ITER do + elem = queue[1] + if elem and elem.t <= ctime then + table.remove(queue, 1) + if callbacks[elem.e] then + -- run it + callbacks[elem.e](elem.d) + else + atwarn("[lines][scheduler] No callback to handle schedule",elem) + end + cnt=cnt+1 + ucn = units_cnt[elem.u] + if ucn and ucn>0 then + units_cnt[elem.u] = ucn - 1 + end + else + break + end + end +end + +-- Enqueue a new scheduled item to be executed at "rwtime" +-- handler: a string identifying the handler to use (registered with sched.register_callback()) +-- evtdata: Arbitrary Lua data to be passed to the handler callback +-- unitid: An arbitrary string uniquely identifying the thing that is issuing this enqueue. +-- used to prevent expotentially growing "scheduler bombs" +-- unitlim: Custom override for UNITS_THRESH (see there) +function sched.enqueue(rwtime, handler, evtdata, unitid, unitlim) + local qtime = ln.rwt.to_secs(rwtime) + assert(type(handler)=="string") + assert(type(unitid)=="string") + assert(type(unitlim)=="number") + + local cnt=1 + local ucn, elem + + ucn = (units_cnt[unitid] or 0) + local ulim=(unitlim or UNITS_THRESH) + if ucn >= ulim then + atlog("[lines][scheduler] discarding enqueue for",handler,"(limit",ulim,") because unit",unitid,"has already",ucn,"schedules enqueued") + return false + end + + while true do + elem = queue[cnt] + if not elem or elem.t > qtime then + table.insert(queue, cnt, { + t=qtime, + e=handler, + d=evtdata, + u=unitid, + }) + units_cnt[unitid] = ucn + 1 + return true + end + cnt = cnt+1 + end +end + +-- See enqueue(). Same meaning, except that rwtime is relative to now. +function sched.enqueue_in(rwtime, handler, evtdata, unitid, unitlim) + local ctime = ln.rwt.get_time() + local rwtime_s = ln.rwt.to_secs(rwtime) + sched.enqueue(ctime + rwtime_s, handler, evtdata, unitid, unitlim) +end + +-- Discards all schedules for unit "unitid" (removes them from the queue) +function sched.discard_all(unitid) + local i = 1 + while i<=#queue do + if queue[i].u == unitid then + table.remove(queue,i) + else + i=i+1 + end + end + units_cnt[unitid] = 0 +end + +ln.sched = sched diff --git a/advtrains_line_automation/settingtypes.txt b/advtrains_line_automation/settingtypes.txt new file mode 100644 index 0000000..ea3f66e --- /dev/null +++ b/advtrains_line_automation/settingtypes.txt @@ -0,0 +1,6 @@ +# This controls how the Railway Time relates to real-world time: +# *independent - RWT counts independent of real time synchronized to ingame steps. Recommended for singleplayer and ad-hoc servers. +# *follow_real - RWT is independent of real time, but counts up in real-world time speed +# *adapt_real - RWT adapts its minute and second to real-world time. When the server is stopped and restarted, this results in a "time jump". +# Time will jump for 1 cycle (59;59) at maximum. +advtrains_lines_rwt_realtime (RWT real time adaption) enum independent independent,follow_real,adapt_real
\ No newline at end of file diff --git a/advtrains_line_automation/stoprail.lua b/advtrains_line_automation/stoprail.lua new file mode 100644 index 0000000..55a4785 --- /dev/null +++ b/advtrains_line_automation/stoprail.lua @@ -0,0 +1,225 @@ +-- stoprail.lua +-- adds "stop rail". Recognized by lzb. (part of behavior is implemented there) + + +local function to_int(n) + --- Disallow floating-point numbers + local k = tonumber(n) + if k then + return math.floor(k) + end +end + +local function updatemeta(pos) + local meta = minetest.get_meta(pos) + local pe = advtrains.encode_pos(pos) + local stdata = advtrains.lines.stops[pe] + if not stdata then + meta:set_string("infotext", "Error") + end + + meta:set_string("infotext", "Stn. "..stdata.stn.." T. "..stdata.track) +end + +local door_dropdown = {L=1, R=2, C=3} +local door_dropdown_rev = {Right="R", Left="L", Closed="C"} + +local function show_stoprailform(pos, player) + local pe = advtrains.encode_pos(pos) + local pname = player:get_player_name() + if minetest.is_protected(pos, pname) then + minetest.chat_send_player(pname, "Position is protected!") + return + end + + local stdata = advtrains.lines.stops[pe] + if not stdata then + advtrains.lines.stops[pe] = { + stn="", track="", doors="R", wait=10, ars={default=true}, ddelay=1,speed="M" + } + stdata = advtrains.lines.stops[pe] + end + + local stn = advtrains.lines.stations[stdata.stn] + local stnname = stn and stn.name or "" + if not stdata.ddelay then + stdata.ddelay = 1 + end + if not stdata.speed then + stdata.speed = "M" + end + + local form = "size[8,7]" + form = form.."style[stn,ars;font=mono]" + form = form.."field[0.8,0.8;2,1;stn;"..attrans("Station Code")..";"..minetest.formspec_escape(stdata.stn).."]" + form = form.."field[2.8,0.8;5,1;stnname;"..attrans("Station Name")..";"..minetest.formspec_escape(stnname).."]" + form = form.."field[0.80,2.0;1.75,1;ddelay;"..attrans("Door Delay")..";"..minetest.formspec_escape(stdata.ddelay).."]" + form = form.."field[2.55,2.0;1.75,1;speed;"..attrans("Dep. Speed")..";"..minetest.formspec_escape(stdata.speed).."]" + form = form.."field[4.30,2.0;1.75,1;track;"..attrans("Track")..";"..minetest.formspec_escape(stdata.track).."]" + form = form.."field[6.05,2.0;1.75,1;wait;"..attrans("Stop Time")..";"..stdata.wait.."]" + form = form.."label[0.5,2.6;"..attrans("Door Side").."]" + form = form.."dropdown[0.51,3.0;2;doors;Left,Right,Closed;"..door_dropdown[stdata.doors].."]" + form = form.."checkbox[3.00,2.7;reverse;"..attrans("Reverse train")..";"..(stdata.reverse and "true" or "false").."]" + form = form.."checkbox[3.00,3.1;kick;"..attrans("Kick out passengers")..";"..(stdata.kick and "true" or "false").."]" + form = form.."textarea[0.8,4.2;7,2;ars;Trains stopping here (ARS rules);"..advtrains.interlocking.ars_to_text(stdata.ars).."]" + form = form.."button[0.5,6;7,1;save;"..attrans("Save").."]" + + minetest.show_formspec(pname, "at_lines_stop_"..pe, form) +end +local tmp_checkboxes = {} +minetest.register_on_player_receive_fields(function(player, formname, fields) + local pname = player:get_player_name() + local pe = string.match(formname, "^at_lines_stop_(............)$") + local pos = advtrains.decode_pos(pe) + if pos then + if minetest.is_protected(pos, pname) then + minetest.chat_send_player(pname, "Position is protected!") + return + end + + local stdata = advtrains.lines.stops[pe] + if not tmp_checkboxes[pe] then + tmp_checkboxes[pe] = {} + end + if fields.kick then -- handle checkboxes due to MT's weird handling + tmp_checkboxes[pe].kick = (fields.kick == "true") + end + if fields.reverse then + tmp_checkboxes[pe].reverse = (fields.reverse == "true") + end + if fields.save then + if fields.stn and stdata.stn ~= fields.stn and fields.stn ~= "" then + local stn = advtrains.lines.stations[fields.stn] + if stn then + if (stn.owner == pname or minetest.check_player_privs(pname, "train_admin")) then + stdata.stn = fields.stn + else + minetest.chat_send_player(pname, "Station code '"..fields.stn.."' does already exist and is owned by "..stn.owner) + show_stoprailform(pos,player) + return + end + else + advtrains.lines.stations[fields.stn] = {name = fields.stnname, owner = pname} + stdata.stn = fields.stn + end + end + local stn = advtrains.lines.stations[stdata.stn] + if stn and fields.stnname and fields.stnname ~= stn.name then + if (stn.owner == pname or minetest.check_player_privs(pname, "train_admin")) then + stn.name = fields.stnname + else + minetest.chat_send_player(pname, "Not allowed to edit station name, owned by "..stn.owner) + end + end + + -- dropdowns + if fields.doors then + stdata.doors = door_dropdown_rev[fields.doors] or "C" + end + + if fields.track then + stdata.track = fields.track + end + if fields.wait then + stdata.wait = to_int(fields.wait) or 10 + end + + if fields.ars then + stdata.ars = advtrains.interlocking.text_to_ars(fields.ars) + end + + if fields.ddelay then + stdata.ddelay = to_int(fields.ddelay) or 1 + end + if fields.speed then + stdata.speed = to_int(fields.speed) or "M" + end + + for k,v in pairs(tmp_checkboxes[pe]) do --handle checkboxes + stdata[k] = v or nil + end + tmp_checkboxes[pe] = nil + --TODO: signal + updatemeta(pos) + show_stoprailform(pos, player) + end + end + +end) + + +local adefunc = function(def, preset, suffix, rotation) + return { + after_place_node=function(pos) + local pe = advtrains.encode_pos(pos) + advtrains.lines.stops[pe] = { + stn="", track="", doors="R", wait=10 + } + updatemeta(pos) + end, + after_dig_node=function(pos) + local pe = advtrains.encode_pos(pos) + advtrains.lines.stops[pe] = nil + end, + on_rightclick = function(pos, node, player) + show_stoprailform(pos, player) + end, + advtrains = { + on_train_approach = function(pos,train_id, train, index, has_entered) + if has_entered then return end -- do not stop again! + if train.path_cn[index] == 1 then + local pe = advtrains.encode_pos(pos) + local stdata = advtrains.lines.stops[pe] + if stdata and stdata.stn then + + --TODO REMOVE AFTER SOME TIME (only migration) + if not stdata.ars then + stdata.ars = {default=true} + end + if stdata.ars and (stdata.ars.default or advtrains.interlocking.ars_check_rule_match(stdata.ars, train) ) then + advtrains.lzb_add_checkpoint(train, index, 2, nil) + local stn = advtrains.lines.stations[stdata.stn] + local stnname = stn and stn.name or "Unknown Station" + train.text_inside = "Next Stop:\n"..stnname + advtrains.interlocking.ars_set_disable(train, true) + end + end + end + end, + on_train_enter = function(pos, train_id, train, index) + if train.path_cn[index] == 1 then + local pe = advtrains.encode_pos(pos) + local stdata = advtrains.lines.stops[pe] + if not stdata then + return + end + + if stdata.ars and (stdata.ars.default or advtrains.interlocking.ars_check_rule_match(stdata.ars, train) ) then + local stn = advtrains.lines.stations[stdata.stn] + local stnname = stn and stn.name or "Unknown Station" + + -- Send ATC command and set text + advtrains.atc.train_set_command(train, "B0 W O"..stdata.doors..(stdata.kick and "K" or "").." D"..stdata.wait.." OC "..(stdata.reverse and "R" or "").."D"..(stdata.ddelay or 1) .. " A1 S" ..(stdata.speed or "M"), true) + train.text_inside = stnname + if tonumber(stdata.wait) then + minetest.after(tonumber(stdata.wait), function() train.text_inside = "" end) + end + end + end + end + }, + } +end + +if minetest.get_modpath("advtrains_train_track") ~= nil then + advtrains.register_tracks("default", { + nodename_prefix="advtrains_line_automation:dtrack_stop", + texture_prefix="advtrains_dtrack_stop", + models_prefix="advtrains_dtrack", + models_suffix=".b3d", + shared_texture="advtrains_dtrack_shared_stop.png", + description="Station/Stop Rail", + formats={}, + get_additional_definiton = adefunc, + }, advtrains.trackpresets.t_30deg_straightonly) +end diff --git a/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png b/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png Binary files differnew file mode 100644 index 0000000..b6629cf --- /dev/null +++ b/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png diff --git a/advtrains_line_automation/textures/advtrains_dtrack_stop_placer.png b/advtrains_line_automation/textures/advtrains_dtrack_stop_placer.png Binary files differnew file mode 100644 index 0000000..0d1c769 --- /dev/null +++ b/advtrains_line_automation/textures/advtrains_dtrack_stop_placer.png |