1
0
mirror of https://github.com/metrostroi-repo/MetrostroiAddon.git synced 2026-05-02 00:42:29 +00:00
Files
MetrostroiAddon/lua/metrostroi/sv_schedule.lua
g_brzhezinskiy 1d05caf866 init
2021-01-02 12:51:45 +03:00

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)