using System; using System.Collections.Generic; using UnityEngine; namespace IdleSpectator; /// World-session immutable observation store. Persistence is added in Phase 5. public static class NarrativeEventStore { private static readonly Dictionary Events = new Dictionary(StringComparer.Ordinal); public static int Count => Events.Count; public static void Clear() { Events.Clear(); } public static NarrativeEventRecord Get(string id) { if (string.IsNullOrEmpty(id)) { return null; } Events.TryGetValue(id, out NarrativeEventRecord record); return record; } public static bool Add(NarrativeEventRecord record) { if (record == null || string.IsNullOrEmpty(record.Id) || Events.ContainsKey(record.Id)) { return false; } Events[record.Id] = record; NarrativePersistence.MarkDirty(); return true; } public static IEnumerable All() => Events.Values; internal static bool Import(NarrativeEventRecord record) { if (record == null || string.IsNullOrEmpty(record.Id) || Events.ContainsKey(record.Id)) { return false; } Events[record.Id] = record; return true; } /// /// Keep the event side of the runtime graph aligned with retained developments. /// Recent unprojected observations fill any remaining room so dedupe remains useful. /// internal static int Compact(HashSet retainedDevelopmentIds, int max) { if (Events.Count <= max || max <= 0) { return 0; } var retained = new HashSet(StringComparer.Ordinal); var ranked = new List(Events.Values); ranked.Sort((a, b) => { bool aLinked = a != null && retainedDevelopmentIds != null && retainedDevelopmentIds.Contains(a.DevelopmentId ?? ""); bool bLinked = b != null && retainedDevelopmentIds != null && retainedDevelopmentIds.Contains(b.DevelopmentId ?? ""); if (aLinked != bLinked) return aLinked ? -1 : 1; float aAt = a?.ObservedAt ?? float.MinValue; float bAt = b?.ObservedAt ?? float.MinValue; int observed = bAt.CompareTo(aAt); if (observed != 0) return observed; return (b?.WorldTime ?? double.MinValue).CompareTo( a?.WorldTime ?? double.MinValue); }); for (int i = 0; i < ranked.Count && retained.Count < max; i++) { NarrativeEventRecord record = ranked[i]; if (record != null && !string.IsNullOrEmpty(record.Id)) { retained.Add(record.Id); } } int before = Events.Count; var remove = new List(); foreach (string id in Events.Keys) { if (!retained.Contains(id)) remove.Add(id); } for (int i = 0; i < remove.Count; i++) Events.Remove(remove[i]); return Mathf.Max(0, before - Events.Count); } }