using System; using System.Collections.Generic; using System.Text; using NeoModLoader.services; namespace IdleSpectator; /// /// Ring buffer of why a beat stayed Layer B or failed to take / keep Layer A camera. /// public static class InterestDropLog { public const int Capacity = 48; private static readonly string[] Buffer = new string[Capacity]; private static int _write; private static int _count; private static float _lastPlayerLogAt = -999f; private const float PlayerLogInterval = 8f; public static int Count => _count; public static void Clear() { _write = 0; _count = 0; for (int i = 0; i < Capacity; i++) { Buffer[i] = null; } } public static void Record(string reason, string detail = "") { if (string.IsNullOrEmpty(reason)) { return; } string line = string.IsNullOrEmpty(detail) ? reason : (reason + ": " + detail); Buffer[_write] = line; _write = (_write + 1) % Capacity; if (_count < Capacity) { _count++; } float now = UnityEngine.Time.unscaledTime; if (now - _lastPlayerLogAt >= PlayerLogInterval) { _lastPlayerLogAt = now; try { LogService.LogInfo("[IdleSpectator][DROP] " + line); } catch { // ignore log failures } } } /// Newest-first snapshot for harness / diagnostics. public static List Snapshot(int max = 12) { var list = new List(Math.Min(max, _count)); if (_count == 0 || max <= 0) { return list; } int n = Math.Min(max, _count); for (int i = 0; i < n; i++) { int idx = (_write - 1 - i + Capacity) % Capacity; if (!string.IsNullOrEmpty(Buffer[idx])) { list.Add(Buffer[idx]); } } return list; } public static string FormatRecent(int max = 8) { List lines = Snapshot(max); if (lines.Count == 0) { return ""; } var sb = new StringBuilder(); for (int i = 0; i < lines.Count; i++) { if (i > 0) { sb.Append(" | "); } sb.Append(lines[i]); } return sb.ToString(); } }