using System; using System.Collections.Generic; using System.Linq; using System.IO; using UnityEngine; using UnityEditor; using UnityEngine.SceneManagement; public enum SectorColour { Green, Purple, White, Yellow, } [Serializable] public struct SectorTime { public int Sector; public double Seconds; public override string ToString() => $"{Seconds:0.000}"; } [Serializable] public struct Lap { public string Map; public int Number; public List Sectors; // 0:1:39 public override string ToString() { return $"{TimeSpan.FromSeconds(TotalTime()):m\\:ss\\.fff}"; } public double TotalTime() { return Sectors.Sum(s => s.Seconds); } } [Serializable] public class LapJSON { public string Map; public List Laps = new(); } public class LapStorage { public List Laps = new(); } static class LapStorageService { // static Dictionary lapsByMap = new Dictionary(); static Dictionary lapsByMap = new(); static string lapStoragePath = Path.Combine(Application.persistentDataPath, "lapStorage.json"); static LapStorageService() { LapStorage lapStorage = new LapStorage(); try { string json = File.ReadAllText(lapStoragePath); JsonUtility.FromJsonOverwrite(json, lapStorage); } catch { Debug.Log("No existing lapStorage.json"); } lapsByMap = lapStorage.Laps.ToDictionary((lapJson) => lapJson.Map); } public static LapJSON GetPlayerLaps(string mapName) { LapJSON currentLaps; bool found = lapsByMap.TryGetValue(mapName, out currentLaps); if (!found) return new LapJSON { Map = mapName, Laps = new List() }; return currentLaps; } static void Persist() { LapStorage lapStorage = new LapStorage { Laps = lapsByMap.Values.ToList() }; File.WriteAllText(lapStoragePath, JsonUtility.ToJson(lapStorage)); } // laps.count public static int NumberOfCompletions() { return 0; } // finds your PB in all completions public static int Rank() { return 1; } public static void PersistLap(Lap lap) { LapJSON lapJson; bool found = lapsByMap.TryGetValue(lap.Map, out lapJson); if (!found) lapJson = new LapJSON { Map = lap.Map, Laps = new List() }; // Add lap and persist lapJson.Laps.Add(lap); lapsByMap[lap.Map] = lapJson; Persist(); } } public class Timing: MonoBehaviour { [SerializeField] public List lapTimes; [SerializeField] public List sectorTimes; public bool timerEnabled; public double sectorTime; public double lapTime; double sectorStart; double lapStart; public Sector[] sectors; public LapStorage lapStorage; public string mapName; void Awake() { lapTimes = new(); sectorTimes = new List(); sectorStart = 0.0; sectorTime = 0.0; sectors = GetComponentsInChildren().OrderBy(s => s.sector).ToArray(); // set map name Scene scene = SceneManager.GetActiveScene(); mapName = scene.name; Debug.Log("Track Sector Count - " + sectors.Length); } void Update() { if (timerEnabled) { sectorTime = Time.timeAsDouble - sectorStart; lapTime = Time.timeAsDouble - lapStart; } } public void StartTimer() { if (sectors.Length == 0) { Debug.LogWarning("No Sectors found in timing"); return; } Debug.Log("Started Timer"); timerEnabled = true; lapStart = Time.timeAsDouble; sectorStart = Time.timeAsDouble; } public void StopTimer() { sectorTimes = new List(); timerEnabled = false; sectorTime = 0; lapTime = 0; Debug.Log("Timer Stopped"); } public void NextSector(int sector) { if (!timerEnabled) return; // they turned around or somethin? if (sectorTimes.Any(s => s.Sector == sector)) return; // this basically makes sure the timer doesn't do anything weird // due to respawns, driving in the wrong direction etc // you have to hit the sectors in numbered order // --------------- // make sure sectors are only accessible by being hit by following // the track correctly! if (sector != sectorTimes.Count + 1) return; double sTime = Time.timeAsDouble - sectorStart; SectorTime s = new SectorTime { Sector = sector, Seconds = sTime }; sectorTimes.Add(s); sectorStart = Time.timeAsDouble; Debug.Log($"{sectorTimes.Count} / {sectors.Length}"); if (sectorTimes.Count == sectors.Length) { EndLap(); NextLap(); } } void NextLap() { Debug.Log("NextLap" + lapTimes.Count); sectorTimes = new List(); StartTimer(); } public void EndLap() { // when live reloading this happens if (sectorTimes == null) Awake(); Debug.Log($"Ending lap Sectors={sectorTimes.Count} Name={mapName} Lap={lapTimes.Count}"); Lap l = new Lap { Map = mapName, Number = lapTimes.Count, Sectors = sectorTimes }; Debug.Log($"EndLap {l}"); lapTimes.Add(l); LapStorageService.PersistLap(l); Debug.Log($"Laps Stored {LapStorageService.GetPlayerLaps(mapName)}"); } public SectorColour GetSectorColour(SectorTime s) { if (lapTimes.Count == 0) return SectorColour.Green; var fastestLap = lapTimes.MinBy(l => l.TotalTime()); var fastestSector = fastestLap.Sectors.Single(fls => fls.Sector == s.Sector); return (s.Seconds < fastestSector.Seconds) switch { true => SectorColour.Green, false => SectorColour.Yellow, }; } }