161 lines
4.7 KiB
C#
161 lines
4.7 KiB
C#
using NeoModLoader.services;
|
|
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>
|
|
/// Samples focus / spectator / power-bar state and logs only on changes or faults.
|
|
/// Tail Player.log for <c>[IdleSpectator][STATE]</c> and <c>[IdleSpectator][BAD]</c>.
|
|
/// </summary>
|
|
public static class StateProbe
|
|
{
|
|
private static bool _prevIdle;
|
|
private static bool _prevFocus;
|
|
private static bool _prevVanillaSpec;
|
|
private static bool _prevBar;
|
|
private static string _prevUnit = "";
|
|
private static float _focusLostSince = -1f;
|
|
private static float _lastHeartbeatAt = -999f;
|
|
private static float _lastBadPowerBarAt = -999f;
|
|
private static float _lastBadGapAt = -999f;
|
|
private static bool _hadFocusWhileIdle;
|
|
private const float HeartbeatSeconds = 10f;
|
|
private const float FocusGapBadSeconds = 0.35f;
|
|
|
|
public static int BadEventCount { get; private set; }
|
|
public static int StateEventCount { get; private set; }
|
|
|
|
public static void ResetCounters()
|
|
{
|
|
BadEventCount = 0;
|
|
StateEventCount = 0;
|
|
_lastBadPowerBarAt = -999f;
|
|
_lastBadGapAt = -999f;
|
|
_focusLostSince = -1f;
|
|
_lastHeartbeatAt = -999f;
|
|
_hadFocusWhileIdle = false;
|
|
}
|
|
|
|
public static void Update()
|
|
{
|
|
if (!ModSettings.DebugStateProbe || !Config.game_loaded || World.world == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool idle = SpectatorMode.Active;
|
|
bool focus = MoveCamera.hasFocusUnit();
|
|
bool vanillaSpec = MoveCamera.inSpectatorMode();
|
|
bool bar = CanvasMain.isBottomBarShowing();
|
|
string unit = FocusUnitLabel();
|
|
|
|
bool changed = idle != _prevIdle
|
|
|| focus != _prevFocus
|
|
|| vanillaSpec != _prevVanillaSpec
|
|
|| bar != _prevBar
|
|
|| unit != _prevUnit;
|
|
|
|
if (changed)
|
|
{
|
|
StateEventCount++;
|
|
LogService.LogInfo(
|
|
$"[IdleSpectator][STATE] idle={idle} focus={focus} vanillaSpec={vanillaSpec} powerBar={bar} unit={unit}");
|
|
}
|
|
|
|
if (idle && focus)
|
|
{
|
|
_hadFocusWhileIdle = true;
|
|
}
|
|
|
|
if (idle && _prevFocus && !focus)
|
|
{
|
|
BadEventCount++;
|
|
LogService.LogInfo("[IdleSpectator][BAD] focus dropped while Idle Spectator is on");
|
|
_focusLostSince = Time.unscaledTime;
|
|
}
|
|
|
|
// Only treat prolonged no-focus as bad after we have successfully acquired a target.
|
|
// Fresh maps often have a short empty window before the first unit exists.
|
|
if (idle && !focus && _hadFocusWhileIdle)
|
|
{
|
|
if (_focusLostSince < 0f)
|
|
{
|
|
_focusLostSince = Time.unscaledTime;
|
|
}
|
|
else if (Time.unscaledTime - _focusLostSince >= FocusGapBadSeconds
|
|
&& Time.unscaledTime - _lastBadGapAt >= 1f)
|
|
{
|
|
_lastBadGapAt = Time.unscaledTime;
|
|
BadEventCount++;
|
|
LogService.LogInfo(
|
|
$"[IdleSpectator][BAD] no focus unit for {Time.unscaledTime - _focusLostSince:0.00}s while idle on (power bar risk)");
|
|
}
|
|
}
|
|
else if (focus)
|
|
{
|
|
_focusLostSince = -1f;
|
|
}
|
|
|
|
if (idle && bar && Time.unscaledTime - _lastBadPowerBarAt >= 1f)
|
|
{
|
|
_lastBadPowerBarAt = Time.unscaledTime;
|
|
BadEventCount++;
|
|
LogService.LogInfo("[IdleSpectator][BAD] power bar showing while Idle Spectator is on");
|
|
}
|
|
|
|
if (idle && Time.unscaledTime - _lastHeartbeatAt >= HeartbeatSeconds)
|
|
{
|
|
_lastHeartbeatAt = Time.unscaledTime;
|
|
StateEventCount++;
|
|
LogService.LogInfo(
|
|
$"[IdleSpectator][STATE] heartbeat idle={idle} focus={focus} vanillaSpec={vanillaSpec} powerBar={bar} unit={unit}");
|
|
}
|
|
|
|
_prevIdle = idle;
|
|
_prevFocus = focus;
|
|
_prevVanillaSpec = vanillaSpec;
|
|
_prevBar = bar;
|
|
_prevUnit = unit;
|
|
}
|
|
|
|
public static void Reset()
|
|
{
|
|
_prevIdle = false;
|
|
_prevFocus = false;
|
|
_prevVanillaSpec = false;
|
|
_prevBar = false;
|
|
_prevUnit = "";
|
|
_focusLostSince = -1f;
|
|
_lastHeartbeatAt = -999f;
|
|
ResetCounters();
|
|
}
|
|
|
|
private static string FocusUnitLabel()
|
|
{
|
|
if (!MoveCamera.hasFocusUnit())
|
|
{
|
|
return "-";
|
|
}
|
|
|
|
Actor unit = MoveCamera._focus_unit;
|
|
if (unit == null)
|
|
{
|
|
return "?";
|
|
}
|
|
|
|
try
|
|
{
|
|
string name = unit.getName();
|
|
if (!string.IsNullOrEmpty(name))
|
|
{
|
|
return name;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return unit.asset != null ? unit.asset.id : "unit";
|
|
}
|
|
}
|