if S.trains == nil then S.trains = {} end if not S.odsd then S.odsd = {} end if not S.cpoints then S.cpoints = {} end S.pd = {} S.pdt = {} __approach_callback_mode = 1 F.odj = {} -- F.dst - Get summer time function F.dst() if os.date().isdst then return "CEST" else return "CET" end end function F.pccheck(pos, state) if is_passive(pos) then return true end return (getstate(pos) == state) end -- F.error - Send error with error code function F.error(errorcode) error(F.errlist[errorcode]) end -- Level crossing handling if not S.crossings then S.crossings = {} end local crossing_light_count = { NRG_1 = 2, NRG_2 = 4, NRG_3 = 3, NRG_4 = 2, NRG_5 = 4, } local function update_light(loc, k, v) local t = S.crossings[loc] if not t then t = {} S.crossings[loc] = t end t[k] = v local val = "on" if next(t) == nil then val = "off" end local pfx = loc .. "_" local count = crossing_light_count[loc] if not count then return end for i = 1, count do setstate(pfx .. i, val) end end function F.x_on(t) if event.type ~= "train" then return end if not atc_arrow then return end for loc, k in pairs(t) do update_light(loc, k, true) end end function F.x_off(t) if event.type ~= "train" then return end if not atc_arrow then return end for loc, k in pairs(t) do update_light(loc, k, nil) end end --[[ F.stnbasic - Basis for all station functions: (By default, nil is defined as false or "no changes") stn = Station Code side = Door opening side optime = Length of time before train departs reverse = Reverse train before departing acc = Departure Speed out = Change outside text reventry = Allow trains to pass from the reverse direction predepart = Function to execute before departure postdepart = Function to execute after departure next = Next stop ]] function F.stnbasic(stn, side, optime, reverse, acc, out, reventry, predepart, postdepart, next,track) if not event then return end if event.type == "train" then if atc_arrow then if (get_rc() or ""):match ("FREIGHT") then atc_send (reverse and "RSM" or "SM") return end local cmdstr="" local t_sched = ((optime or 15) + 1) if out then atc_set_text_outside (out) end local intext = "Unknown Station" if stn then intext = F.stnlist[stn] or stn end atc_set_text_inside (intext) cmdstr = "BBWO" .. (side or "R") if type (predepart) == "function" then predepart () end S.trains[atc_id] = { v = 1, l = (get_line() or ""), s = stn, t = (trk or "N/A"), } cmdstr = cmdstr .."D" .. math.floor((optime or 15)) .. "OCD1" if reverse then cmdstr = cmdstr .. "R" end cmdstr = cmdstr .. "S" .. (acc or "M") atc_send (cmdstr) interrupt (t_sched,"") end elseif event.type == "int" then if atc_id then local intext = "" if next then intext = "Next stop: " .. (F.stnlist[next] or next) end if type (postdepart) == "function" then postdepart () end atc_set_text_inside (intext) end end end -- F.hst - preset function for small stations (Haltestelle) function F.hst(cur, nxt, side, spd, out, trk) F.stnbasic(cur, side, 10, false, spd, out, true, nil, nil, nxt, trk) end -- F.bhf - preset function for large stations (Bahnhof) function F.bhf(cur, nxt, side, spd, out, trk) F.stnbasic(cur, side, 15, false, spd, out, true, nil, nil, nxt, trk) end -- F.kbhf - preset function for termini function F.kbhf(cur, nxt, side, spd, out, trk) F.stnbasic(cur, side, 15, true, spd, out, true, nil, nil, nxt, trk) end -- F.timing - station with timing function F.timing(d_off, d_int, cur, nxt, side, spd, out, trk, term, pre, post) local timenow = os.time() local timesincelast = (timenow+d_off) % d_int local wait = d_int - timesincelast F.stnbasic(cur, side, wait, term, spd, out, true, pre, post, nxt, trk) end -- F.brk(direction) - sends ATC B2 command function F.brk(dir) if event.type == "train" then if atc_arrow == dir then atc_send("B2S2") end end end -- Second version of the station function function F.stn2gen(stn, trk, door, ret, chout, depspeed) __approach_callback_mode = 1 if not stn then return end if not trk then return end if not door then return end if door~="L" and door~="R" and door~="C" then return end if not atc_arrow then return end if not F.stndet[stn] then return end if not F.stndet[stn][trk] then return end local t = F.stndet[stn][trk] local stop = false if #t==0 then return end local l = get_line() if (not l) or l==" " then return end local rc = get_rc() or "" if rc:match("FREIGHT") and atc_id then atc_send (ret and "BBWRSM" or "SM") return end if event.type == "approach" and not event.has_entered then for i = 1, #t, 1 do if t[i][1]==l then atc_set_ars_disable(true) atc_set_lzb_tsr(2) local intext = ("Arriving at: %s (Track: %s)"):format(F.stnlist[stn] or stn, trk) atc_set_text_inside(intext) S.trains[atc_id] = { v = 2, l = get_line() or "", s = stn, t = trk or "N/A", ts = os.time(), mode = "APP", } return end end return elseif event.type == "train" then for i = 1, #t, 1 do if t[i][1]==l then stop = true local timenow = os.time() local interval = F.lines[l].interval local offset = t[i][7] local arroff if interval and offset then arroff = timenow%(F.lines[l].rtt or interval) interval = (offset-timenow)%interval interval = math.max(5, math.min(45, interval)) else interval = 15 end --atc_send(string.format("A0B0WO%sD%dOCD1%sA1S%s", door, interval, (ret and "R" or ""), tostring(depspeed or "M"))) atc_send(("B0WO%s"):format(door)) local nxt = t[i][2] local intext_nxt = "" local nxtdisp = "" if F.stnlist[nxt] then intext_nxt = F.stnlist[nxt] and ("Next station: %s (Track: %s)"):format(F.stnlist[nxt], t[i][3] or "?") or "" nxtdisp = intext_nxt end local intext = ("%s\n%s\nDebug: %d %d"):format(F.stnlist[stn] or stn, intext_nxt, arroff or timenow, interval) local outtext = F.lines[l].name or l if t[i][4] then outtext = outtext..": "..(F.stnlist[t[i][4]] or t[i][4]) end t[i][5] = t[i][5] or (timenow-(F.depint[l] or 300)) -- note that the "average" time is weighted t[i][6] = t[i][6] and (t[i][6]+timenow-t[i][5]+30)/2 or (timenow-t[i][5]) t[i][5] = timenow atc_set_text_inside(intext) if chout then if type(chout) == "string" then atc_set_text_outside(chout) else atc_set_text_outside(outtext) end end S.trains[atc_id] = { v = 2, l = (get_line() or ""), s = stn, t = (trk or "N/A"), ts = os.time(), mode = "ARR", } schedule_in(interval, {intext = nxtdisp}) break end end if (not stop) then atc_send((ret and "BBWR" or "").."A1S"..(depspeed or "M")) end elseif event.type == "schedule" then if atc_id then local msg = event.msg or {} if msg.intext then atc_set_text_inside(msg.intext) end atc_send("OCD1"..((ret and atc_arrow) and "B0WR" or "").."A1S"..(depspeed or "M")) S.trains[atc_id] = { v = 2, l = get_line() or "", s = stn, t = trk or "N/A", ts = os.time(), mode = "DEP", } end end end function F.tram2gen(stn,trk,ret) return F.stn2gen(stn,trk,"C",ret,true,8) end function F.disp2gen(stn) if not stn then return end if not F.stndet[stn] then return end local s = F.stndet[stn] local sn = F.stnlist[stn] or stn local d={{ {sn.."\nTrack Line"}, {(string.sub(sn, 28, 54) or "").." \nDestination"}, {" \nEstimated Arrival"} }} local c=2 local t={} for i,_ in pairs(s) do t[#t+1]=i end table.sort(t) for i = 1,#t,1 do local trkname = t[i] local trk = s[trkname] for j = 1,#trk,1 do local det = trk[j] local r = (c%4==0) and (c/4+1) or ((c-c%4)/4+1) if not d[r] then d[r]={{},{},{}} end d[r][1][#d[r][1]+1] = string.format("%-8s %s", tostring(trkname), F.lines[det[1]].name) d[r][2][#d[r][2]+1] = (F.lines[det[1]].ring and "Ring Line" or (F.stnlist[det[4]] or det[4] or "")) c=c+1 local earr = (det[5] or os.time())+(det[6] or F.depint[det[1]] or 600) d[r][3][#d[r][3]+1] = os.date("%Y-%m-%d %H:%M %Z", earr) end end for i = 1, #d, 1 do for j = 1, #d[i], 1 do digiline_send("d_"..tostring(i).."_"..tostring(j), table.concat(d[i][j],"\n")) end end end --function F.disp2gen() end function F.pdisp(stn, trk, dn) if not (stn and trk and dn) then return end if not F.stndet[stn] or not F.stndet[stn][trk] then return end if not S.pd[stn] then S.pd[stn] = { [trk] = {[dn] = 0} } end if not S.pd[stn][trk] then S.pd[stn][trk] = {[dn] = 0} end if not S.pd[stn][trk][dn] then S.pd[stn][trk][dn] = 0 end if not S.pdt[stn] then S.pdt[stn] = {} end if not S.pdt[stn][trk] then local t = {} for i = 1, #F.stndet[stn][trk], 1 do local v = F.stndet[stn][trk][i] t[#t+1] = ("Track: %s\n%s\nTo: %s\nNext: %s"):format( trk, F.lines[v[1]].name, F.stnlist[v[4] or "N/A"] or v[4] or "N/A", F.stnlist[v[2] or "N/A"] or v[2] or "N/A") end S.pdt[stn][trk] = t end local n = S.pd[stn][trk][dn] + 1 local t = S.pdt[stn][trk] digiline_send("track"..trk,t[n]) if n >= #t then n = 0 end S.pd[stn][trk][dn] = n end function F.dirsign(stn, trk, ln, arrow) if event.type == "init" or event.type == "punch" then if not (stn and trk and ln and arrow) then return end local t = F.stndet[stn] if not t then return end t = t[trk] if not t then return end for i = 1, #t, 1 do if t[i][1] == ln then digiline_send("lcd",("%s T. %s | %s | %s"):format(arrow, trk, F.lines[ln].name, F.stnlist[t[i][4]])) break end end end end function F.eval(expr, rettrue, retfalse) if expr then return rettrue else return retfalse end end function F.checkpoint(name,arrow,opp) if event.train then S.trains[atc_id] = "C "..name.." " if atc_arrow then S.trains[atc_id] = S.trains[atc_id]..arrow else S.trains[atc_id] = S.trains[atc_id]..opp end end end function F.atc (cmd, intext, outtext) if event.type == "train" then if atc_arrow then if cmd then atc_send (cmd) end if intext then atc_set_text_inside (intext) end if outtext then atc_set_text_outside (outtext) end end end end F.stnlist = { ["N/A"] = "N/A", Bts = "Berton St.", cg = "Colored Grasses", clockwise = "Clockwise", counterclockwise = "Counterclockwise", cras = "ARSE7's Shop", crbfost = "Station East", crbfsm = "Station St. Central", crbfso = "Station St. East", crbfsw = "Station St. West", crch = "Crossroads City Hall", crchs = "City Hall South", crmtrail = "Mountain Railway Terminus", crshelter1 = "Shelter I", crsfterm = "South Forest St. Terminal", crsmacker = "Smacker's Station", crwm = "West Mountains", elchateau = "Chateau d'Erstazi", elgp = "Greener Pastures", elsf = "Salt Factory", evo = "EVO", grsc = "Grassy Scarp", krasnograd = "Krasnograd-FTNT", mushroom = "Mushroom Land", neverbuild = "Neverbuild", nvbcentral = "Neverbuild Central", nvbold = "Old Terminus", nvboutskirts = "Neverbuild Outskirts", ["NRG-bplatz"] = "Berliner Platz", ["NRG-CW"] = "Dörfle", ["NRG-harbor"] = "Hafengebiet", ["NRG-krstr"] = "Krasnograder Straße", ["NRG-museum"] = "Museum", ["NRG-pek"] = "Pekinger Straße", ["NRG-south"] = "Südbahnhof", ["NRG-townhall"] = "Rathaus", ["NRG-townhall2"] = "Hintere Rathausstraße", ["NRG-yard"] = "Betriebshof", oc = "Ocean City", occh = "City Hall", occrt = "CRT Office", ocmushroom = "Mushroom Market", ocoutskirts = "Ocean City Outskirts", phsc = "Southern Crossing", phwest = "Personhood West", scc = "Silver Coast Central", scn = "Silver Coast North", scs = "Silver Coast South", thecube = "The Cube", } for k, v in pairs{[1] = "Station",} do for _, i in pairs{6,} do F.stnlist[string.format("crc_%d_%d", k, i)] = string.format("%d %s St.", i, v) end end for k, v in pairs{[5] = "Fifth", [6] = "Sixth", [7] = "Seventh", [8] = "Eighth",} do for l, w in pairs{[1] = "First", [3] = "Third", [5] = "Fifth",} do F.stnlist[string.format("crs_%d_%d", k, l)] = string.format("%s St./%s Alley", v, w) end end F.lines = { ["AB"] = { name = "ATL-B Commuter", short = "ATL-B/C", ring = false, interval = 240, [1] = { {"crch","2"}, --[[ {"crwm","3"},]] {"scs","N1"}, {"scc","N1"}, {"scn","N1"}, {"cg","1"}, {"thecube","4"}, --[[{"phsc","N"},]] {"grsc", "4"}, {"phwest","1"} }, [2] = { {"phwest","1"}, {"grsc", "3"}, --[[{"phsc","S"},]] {"thecube","1"}, {"cg","2"}, {"scn","S1"}, {"scc","S1"}, {"scs","S1"}, --[[ {"crwm","4"}, ]] {"crch","2"} }, }, ["ABE"] = { name = "ATL-B Express", short = "ATL-B/E", ring = false, interval = 240, [1] = {{"crch","1"}, {"scc","N2"}, {"cg","1"}, {"thecube", "4"}, {"phwest", "2"}}, [2] = {{"phwest", "2"}, {"thecube", "1"}, {"cg", "2"}, {"scc", "S2"}, {"crch", "1"}}, }, ["AZ"] = { name = "ATL-Z", short = "ATL-Z", ring = false, interval = 480, [1] = { {"Bts","3"}, {"evo","N/A"}, {"scs","N1"}, {"scc","N1"}, {"scn","N1"}, {"oc","K1"}, --[[{"elgp","N"}, {"elsf","E"}, {"elchateau","S"}]] }, [2] = { --[[{"elchateau","S"}, {"elsf","W"}, {"elgp","S"},]] {"oc","K1"}, {"scn","S1"}, {"scc", "S1"}, {"scs","S1"}, {"evo","N/A"}, {"Bts","3"}, }, }, ["CRT1"] = { name = "1", ring = false, [1] = { {"crsmacker", "R3"}, --[[{"crch", "U1"},]] {"crchs", "W"}, {"cras", "N"} }, [2] = { {"cras", "N"}, {"crchs", "E"}, --[[{"crch", "U2"},]] {"crsmacker", "R3"} }, }, ["CRT4"] = { name = "4", ring = false, rtt = 120, interval = 60, [1] = { {"crch", "11", 0}, {"crsfterm", "1N", 0}, }, [2] = { {"crsfterm", "1N", 0}, {"crch", "11", 0}, }, }, ["CRT12"] = { name = "12", ring = false, [1] = {{"crch", "M"}, {"crwm", "3"}}, [2] = {{"crwm", "3"}, {"crch", "M"}}, }, ["CRT21"] = { name = "21", ring = false, rtt = 120, interval = 120, [1] = { {"crch", "12", 15}, {"crshelter1", "T", 75}, }, [2] = { {"crshelter1", "T", 75}, {"crch", "12", 15}, }, }, ["NRG1"] = { name = "1", ring = "counterclockwise", rtt = 120, interval = 120, [1] = { {"NRG-townhall","E",0}, {"NRG-south","N",40}, {"NRG-museum","S",75} }, }, ["NRG2"] = { name = "2", ring = false, rtt = 240, interval = 240, [1] = { {"NRG-harbor", "S", 35}, {"NRG-townhall2", "W", 70}, {"NRG-museum", "S", 135}, }, [2] = { {"NRG-museum", "S", 135}, {"NRG-townhall", "E", 195}, {"NRG-bplatz", "S", 235}, {"NRG-harbor", "S", 275}, }, }, ["NRG3"] = { name = "3", ring = false, [1] = { {"NRG-yard", "W"}, {"NRG-krstr", "N"}, {"NRG-CW", "N"}, {"NRG-pek", "E"}, {"krasnograd", "W"}, }, [2] = { {"krasnograd", "W"}, {"NRG-pek", "W"}, {"NRG-CW", "S"}, {"NRG-krstr", "S"}, {"NRG-yard", "W"}, }, }, } for k, v in pairs{["B"] = {6, 5, 3, 20},} do local t = {{"crc_1_"..v[1], "E", v[4]}} for i = 5, 8 do t[#t+1] = {string.format("crs_%d_%d", i, v[2]),"S", v[4] + 45 + (i-5)*30} end for i = 8, 5, -1 do t[#t+1] = {string.format("crs_%d_%d", i, v[3]),"N", v[4] + 175 + (8-i)*30} end F.lines["CRT1"..k] = { name = "1"..k, ring = "clockwise", rtt = 320, interval = 80, [1] = t } end for k, v in pairs{["A"] = {5}, ["B"]={6}, ["C"]={7}, ["D"]={8}} do local t = {} local u = {} local phase = ((v[1]-5)%2)*15 t[1] = {"crsfterm", (v[1]-4).."S", phase} for i = 1, 5, 2 do t[#t+1] = {string.format("crs_%d_%d",v[1],i), "E", 0} end for i = 5, 1, -2 do u[#u+1] = {string.format("crs_%d_%d",v[1],i), "W", 0} end u[#u+1] = {"crsfterm", (v[1]-4).."S", phase} F.lines["CRT3"..k] = { name = "3"..k, ring = false, rtt = 30, interval = 30, [1] = t, [2] = u, } end F.stndet = {} for i, l in pairs(F.lines) do for j = 1, #l, 1 do local dir = l[j] for k = 1, #dir, 1 do local s = dir[k][1] local t = dir[k][2] local o = dir[k][3] if not F.stndet[s] then F.stndet[s] = {} end if not F.stndet[s][t] then F.stndet[s][t] = {} end local det = F.stndet[s][t] if k~=#dir or l.ring then local nxt = dir[k==#dir and 1 or k+1] det[#det+1] = {i, nxt[1], nxt[2], l.ring or dir[#dir][1], nil, nil, o} else local addent = true for m = 1, #l, 1 do addent = addent and (l[m][1]==dir[k]) end if addent then det[#det+1] = {i, nil, nil, s, nil, nil, o} end end end end end F.errlist = { ["incorrect_setup"] = "Incorrect LuaATC track setup!", ["runaway_train"] = "Runaway train found!", ["train_disappeared"] = "Train has disappeared!", ["unexpected_train"] = "Train is not expected to pass!", ["wrong_direction"] = "Train passed in wrong direction!", } F.depint = { ["AB"] = 90, ["AG"] = 120, ["CRT1"] = 30, } F.dpts = { ['CRT1'] = POS(1835,5,1000), ["CRT2"] = POS(1741,-1,1020), ['AG'] = POS(1399,4,2020), } function F.odd(ln) if event.type=='ext_int' then if atc_id then set_rc(event.message.rc) atc_send('SM') else if type(F.odj[ln])~='table' then F.odj[ln] = {} end for i = 1,(#F.odj[ln]) do if F.odj[ln][i]==event then return end end table.insert(F.odj[ln],1,event) end end if event.type=='train' then if F.odj[ln] and #F.odj[ln]~=0 then ev = table.remove(F.odj[ln]) if ev then set_rc(ev.message.rc) atc_send('BBWRSM') return else F.odj[ln] = {} end end atc_send('BBWR') end end function F.ods(rc, side, rev) if event.type=='train' then if atc_arrow then if string.find((get_rc() or ''),(rc or '')) then atc_send('BBWO' .. (side or 'R') .. 'D10OCD1' .. ((rev or string.find((get_rc() or ''), 'R' .. rc)) and 'R' or '')..'SM') elseif rev then atc_send('BBWRSM') end end end end function F.odc(ln, rc) interrupt_pos(F.dpts[ln],{['rc']=rc}) end function F.cpoint(name, int) __approach_callback_mode = 1 local last = S.cpoints[name] or 0 local next = last+int local now = os.time() if event.type == "approach" then if now < next then atc_set_lzb_tsr(2) end elseif event.type == "train" and atc_arrow then if now < next then atc_send(string.format("A0B0WD%dA1SM", next-now)) schedule_in(next-now) else S.cpoints[name] = now atc_send("A1SM") end elseif event.type == "schedule" and atc_arrow then S.cpoints[name] = now atc_send("A1SM") end end