mirror of
https://github.com/metrostroi-repo/MetrostroiAddon.git
synced 2026-05-02 00:42:29 +00:00
258 lines
9.0 KiB
Lua
258 lines
9.0 KiB
Lua
--------------------------------------------------------------------------------
|
|
-- Schedule generator
|
|
--------------------------------------------------------------------------------
|
|
-- List of all unique routes that can be used in schedule generation
|
|
-- { station, platform }
|
|
Metrostroi.ScheduleRoutes = Metrostroi.ScheduleRoutes or {}
|
|
Metrostroi.SchedulesInitialized = false
|
|
|
|
-- List of all time intervals in which schedules must be generated
|
|
-- { start_time, end_time, route_name, train_interval,
|
|
Metrostroi.ScheduleConfiguration = Metrostroi.ScheduleConfiguration or {}
|
|
|
|
-- List of station names
|
|
Metrostroi.StationNames = Metrostroi.StationNames or {}
|
|
Metrostroi.StationTitles = Metrostroi.StationTitles or {}
|
|
Metrostroi.StationNamesConfiguration = Metrostroi.StationNamesConfiguration or {}
|
|
|
|
-- AI train behavior configuration
|
|
Metrostroi.AIConfiguration = Metrostroi.AIConfiguration or {}
|
|
|
|
-- Current server time
|
|
function Metrostroi.ServerTime()
|
|
return (os.time() % 86400)
|
|
end
|
|
|
|
-- Departure time of last train from first station
|
|
Metrostroi.DepartureTime = Metrostroi.DepartureTime or {}
|
|
-- Schedule counter
|
|
Metrostroi.ScheduleID = Metrostroi.ScheduleID or 0
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
local function timeToSec(str)
|
|
local x = string.find(str,":")
|
|
if not x then return tonumber(sec) or 0 end
|
|
|
|
local min = tonumber(string.sub(str,1,x-1)) or 0
|
|
local sec = tonumber(string.sub(str,x+1)) or 0
|
|
return min*60+sec,min,sec
|
|
end
|
|
|
|
local function prepareRouteData(routeData,name)
|
|
-- Prepare general route information
|
|
routeData.Duration = 0
|
|
routeData.Name = name
|
|
|
|
-- Fix up every station
|
|
for i,stationID in ipairs(routeData) do
|
|
routeData[i].ID = i
|
|
routeData[i].TimeOffset = routeData.Duration
|
|
if routeData[i+1] then
|
|
if not Metrostroi.Stations[routeData[i][1]] then print(Format("No station %d",routeData[i][1])) return end
|
|
if not Metrostroi.Stations[routeData[i][1]][routeData[i][2]] then print(Format("No platform %d for station %d",routeData[i][2],routeData[i][1])) return end
|
|
if not Metrostroi.Stations[routeData[i+1][1]] then print(Format("No station %d",routeData[i+1][1])) return end
|
|
if not Metrostroi.Stations[routeData[i+1][1]][routeData[i][2]] then print(Format("No platform %d for station %d",routeData[i+1][2],routeData[i+1][1])) return end
|
|
|
|
-- Get nodes
|
|
local start_node = Metrostroi.Stations[routeData[i ][1]][routeData[i ][2]].node_end
|
|
local end_node = Metrostroi.Stations[routeData[i+1][1]][routeData[i+1][2]].node_end
|
|
if false and start_node.path ~= end_node.path then
|
|
print(Format("Platform %d for station %d: path %d; Platform %d for station %d: path %d",
|
|
routeData[i ][2],routeData[i ][1],start_node.path.id,
|
|
routeData[i+1][2],routeData[i+1][1],end_node.path.id))
|
|
return
|
|
end
|
|
|
|
-- Calculate travel time between two nodes
|
|
local travelTime,travelDistance = Metrostroi.GetTravelTime(start_node,end_node)
|
|
-- Add time for startup and slowdown
|
|
travelTime = travelTime + 25
|
|
|
|
-- Remember stats
|
|
routeData.Duration = routeData.Duration + travelTime
|
|
routeData[i].TravelTime = travelTime
|
|
routeData[i].TravelDistance = travelDistance
|
|
|
|
-- Print debug information
|
|
print(Format("\t\t[%03d-%d]->[%03d-%d] %02d:%02d min %4.0f m %4.1f km/h",
|
|
routeData[i][1],routeData[i][2],
|
|
routeData[i+1][1],routeData[i+1][2],
|
|
math.floor(travelTime/60),math.floor(travelTime)%60,travelDistance,(travelDistance/travelTime)*3.6))
|
|
else
|
|
routeData.LastID = i
|
|
routeData.LastStation = routeData[i][1]
|
|
end
|
|
end
|
|
|
|
-- Add a quick lookup
|
|
routeData.Lookup = {}
|
|
for i,_ in ipairs(routeData) do
|
|
routeData.Lookup[routeData[i][1]] = routeData[i]
|
|
end
|
|
end
|
|
|
|
function Metrostroi.InitializeSchedules()
|
|
if Metrostroi.SchedulesInitialized then return end
|
|
Metrostroi.SchedulesInitialized = true
|
|
|
|
-- Fix up all routes
|
|
print("Metrostroi: Preparing routes...")
|
|
for routeName,routeData in pairs(Metrostroi.ScheduleRoutes) do
|
|
print(Format("\tTravel distances for preset route '%s':",routeName))
|
|
prepareRouteData(routeData,routeName)
|
|
print(Format("\t\tTotal duration: %02d:%02d min",math.floor(routeData.Duration/60),math.floor(routeData.Duration)%60))
|
|
end
|
|
end
|
|
|
|
function Metrostroi.GenerateSchedule(routeID,starts,ends)
|
|
Metrostroi.InitializeSchedules()
|
|
if not Metrostroi.ScheduleRoutes[routeID] then print("Error generating schedule, line or path not found") return end
|
|
local st = 1
|
|
local fst = not starts
|
|
local en = #Metrostroi.ScheduleRoutes[routeID]
|
|
local fen = not ends
|
|
for k,v in ipairs(Metrostroi.ScheduleRoutes[routeID]) do
|
|
if v[1] == starts and not fst then
|
|
st = k
|
|
fst = true
|
|
end
|
|
if v[1] == ends and not fen then
|
|
en = k
|
|
fen = true
|
|
end
|
|
if fst and fen then break end
|
|
end
|
|
if not fst then print("Metrostroi: Warning! Station "..starts.." not found") st = 1 end
|
|
if not fen then print("Metrostroi: Warning! Station "..ends.." not found") en = #Metrostroi.ScheduleRoutes[routeID] end
|
|
|
|
-- Time padding (extra time before schedule starts, wait time between trains)
|
|
local paddingTime = timeToSec("1:30")
|
|
-- Current server time
|
|
local serverTime = Metrostroi.ServerTime()/60
|
|
-- hack
|
|
if routeID == "Line1_Platform2" then
|
|
paddingTime = timeToSec("3:00")
|
|
end
|
|
|
|
-- Determine schedule configuration
|
|
local interval
|
|
for _,config in pairs(Metrostroi.ScheduleConfiguration) do
|
|
local t_start = timeToSec(config[1])
|
|
local t_end = timeToSec(config[2])
|
|
if (config[3] == routeID) and (t_start <= serverTime) and (t_end > serverTime) then
|
|
interval = timeToSec(config[4])
|
|
end
|
|
end
|
|
|
|
-- If no interval, then no schedules available
|
|
if not interval then return end
|
|
|
|
-- If no schedules started
|
|
if not Metrostroi.DepartureTime[routeID] then
|
|
Metrostroi.DepartureTime[routeID] = serverTime + paddingTime/60
|
|
else
|
|
-- If schedules started, depart with interval
|
|
Metrostroi.DepartureTime[routeID] = math.max(Metrostroi.DepartureTime[routeID] + interval/60,serverTime + paddingTime/60)
|
|
end
|
|
|
|
-- Create new schedule
|
|
Metrostroi.ScheduleID = Metrostroi.ScheduleID + 1
|
|
local schedule = {
|
|
ScheduleID = Metrostroi.ScheduleID,
|
|
Interval = interval,
|
|
Duration = Metrostroi.ScheduleRoutes[routeID].Duration,
|
|
}
|
|
|
|
-- Fill out all stations
|
|
local currentTime = Metrostroi.DepartureTime[routeID]
|
|
for id,stationData in ipairs(Metrostroi.ScheduleRoutes[routeID]) do
|
|
if st > id or id > en then continue end
|
|
-- Calculate stop time
|
|
local stopTime = 15
|
|
-- if not stationData.TravelTime then stopTime = 0 end
|
|
|
|
-- Add entry
|
|
schedule[#schedule+1] = {
|
|
stationData[1], -- Station
|
|
stationData[2], -- Platform
|
|
currentTime+stopTime/60, -- Departure time
|
|
currentTime, -- Arrival time
|
|
}
|
|
|
|
schedule[#schedule].arrivalTimeStr =
|
|
Format("%02d:%02d:%02d",
|
|
math.floor(schedule[#schedule][3]/60),
|
|
math.floor(schedule[#schedule][3])%60,
|
|
math.floor(schedule[#schedule][3]*60)%60)
|
|
|
|
-- Add travel time
|
|
if stationData.TravelTime then
|
|
currentTime = currentTime + (stationData.TravelTime + stopTime)/60
|
|
end
|
|
end
|
|
|
|
-- Fill out start and end
|
|
schedule.StartStation = schedule[1][1]
|
|
schedule.EndStation = schedule[#schedule][1]
|
|
schedule.StartTime = schedule[1][2]
|
|
schedule.EndTime = schedule[#schedule][2]
|
|
|
|
-- Print result
|
|
print(Format("--- %03d --- From %03d to %03d --------------------",
|
|
schedule.ScheduleID,schedule.StartStation,schedule.EndStation))
|
|
for i,d in ipairs(schedule) do
|
|
print(Format("\t%03d %s",d[1],d.arrivalTimeStr))
|
|
end
|
|
return schedule
|
|
end
|
|
|
|
function Metrostroi.LoadSchedulesData(data)
|
|
Metrostroi.ScheduleRoutes = data.Routes or {}
|
|
Metrostroi.ScheduleConfiguration = data.Configuration or {}
|
|
Metrostroi.StationNames = data.StationNames or {}
|
|
Metrostroi.StationTitles = data.StationTitles or {}
|
|
Metrostroi.AIConfiguration = data.AIConfiguration or {}
|
|
Metrostroi.StationNamesConfiguration = data.StationNamesConfiguration or {}
|
|
Metrostroi.SchedulesInitialized = false
|
|
timer.Simple(3.0,function()
|
|
Metrostroi.InitializeSchedules()
|
|
Metrostroi.DepartureTime = {}
|
|
Metrostroi.ScheduleID = 0
|
|
for k,v in pairs(Metrostroi.TrainClasses) do
|
|
if v == "gmod_subway_base" then continue end
|
|
local ents = ents.FindByClass(v)
|
|
for k2,v2 in pairs(ents) do
|
|
if v2.Schedule then v2.Schedule = nil end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
concommand.Add("metrostroi_resetschedules", function(ply, _, args)
|
|
if not ply:IsAdmin() then return end
|
|
Metrostroi.DepartureTime = {}
|
|
Metrostroi.ScheduleID = 0
|
|
for k,v in pairs(Metrostroi.TrainClasses) do
|
|
if v == "gmod_subway_base" then continue end
|
|
local ents = ents.FindByClass(v)
|
|
for k2,v2 in pairs(ents) do
|
|
if v2.Schedule then v2.Schedule = nil end
|
|
end
|
|
end
|
|
end)
|
|
concommand.Add("metrostroi_schedule1", function(ply, _, args)
|
|
Metrostroi.GenerateSchedule("Line1_Platform1")
|
|
end)
|
|
|
|
concommand.Add("metrostroi_schedule2", function(ply, _, args)
|
|
Metrostroi.GenerateSchedule("Line1_Platform2")
|
|
end)
|
|
concommand.Add("metrostroi_print_scheduleinfo", function(ply, _, args)
|
|
for routeName,routeData in pairs(Metrostroi.ScheduleRoutes) do
|
|
print(Format("\tTravel distances for preset route '%s':",routeName))
|
|
prepareRouteData(routeData,routeName)
|
|
print(Format("\t\tTotal duration: %02d:%02d min",math.floor(routeData.Duration/60),math.floor(routeData.Duration)%60))
|
|
end
|
|
end)
|