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 --[[ 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" then for i = 1, #t, 1 do if t[i][1]==l then atc_set_lzb_tsr(2) 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"))) 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 atc_set_text_outside(outtext) end S.trains[atc_id] = { v = 2, l = (get_line() or ""), s = stn, t = (trk or "N/A"), } schedule_in(interval, {intext = nxtdisp}) break end end if (not stop) then atc_send((ret and "BBWR" or "").."S"..(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"..(((not ret) == (not atc_arrow)) and "R" or "").."A1S"..(depspeed or "M")) 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", crsmacker = "Smacker's Station", crwm = "CR West Mountains", elchateau = "Erstaziland-Chateau d'Erstazi", elgp = "Erstaziland-Greener Pastures", elsf = "Erstaziland-Salt Factory", evo = "EVO", grsc = "Grassy Scarp", mushroom = "Mushroom Land", neverbuild = "Neverbuild", nvbcentral = "Neverbuild Central", nvbold = "Old Terminus", nvboutskirts = "Neverbuild Outskirts", ["NRG-museum"] = "Museum", ["NRG-south"] = "New Roses Gardens South", ["NRG-townhall"] = "Town Hall", 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{[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","N"}, {"elgp","N"}, {"elsf","E"}, {"elchateau","S"} }, [2] = { {"elchateau","S"}, {"elsf","W"}, {"elgp","S"}, {"oc","S"}, {"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"} }, }, ["NRG1"] = { name = "1", ring = "counterclockwise", rtt = 120, interval = 120, [1] = { {"NRG-townhall","E",0}, {"NRG-south","N",40}, {"NRG-museum","S",75} }, } } 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 = {} for i = 3, 5, 2 do t[#t+1] = {string.format("crs_%d_%d",v[1],i), "E"} end for i = 5, 3, -2 do u[#u+1] = {string.format("crs_%d_%d",v[1],i), "W"} end F.lines["CRT3"..k] = { name = "3"..k, ring = false, [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