worldbox-observer-mod/IdleSpectator/WatchCaption.cs
2026-07-14 16:25:28 -05:00

197 lines
6 KiB
C#

using NeoModLoader.General;
using NeoModLoader.services;
using UnityEngine;
using UnityEngine.UI;
namespace IdleSpectator;
/// <summary>
/// Persistent compact dossier card (top-left) while Idle Spectator is active.
/// Replaces WorldTip for dossier text so AFK watching does not spam giant orange toasts.
/// </summary>
public static class WatchCaption
{
private const float PanelWidth = 260f;
private const float PanelHeight = 72f;
private static GameObject _root;
private static Text _nameText;
private static Text _reasonText;
private static Text _metaText;
private static UnitDossier _current;
private static bool _visible;
public static UnitDossier Current => _current;
public static string LastHeadline { get; private set; } = "";
public static string LastDetail { get; private set; } = "";
public static string LastCaptionText { get; private set; } = "";
public static void Clear()
{
_current = null;
LastHeadline = "";
LastDetail = "";
LastCaptionText = "";
SetVisible(false);
}
public static void SetFromInterest(InterestEvent interest)
{
Actor unit = interest != null && interest.HasFollowUnit
? interest.FollowUnit
: (MoveCamera.hasFocusUnit() ? MoveCamera._focus_unit : null);
if (unit == null || !unit.isAlive())
{
if (interest != null)
{
LastHeadline = CameraDirector.FormatWatchTip(interest);
LastDetail = "";
LastCaptionText = LastHeadline;
_current = null;
EnsureBuilt();
ApplyLines(LastHeadline, "", "");
SetVisible(ModSettings.ShowDossierCaption && SpectatorMode.Active);
}
return;
}
SetFromActor(unit, interest?.Tier, interest?.Label);
}
public static void SetFromActor(Actor actor, InterestTier? tier = null, string label = null)
{
UnitDossier dossier = UnitDossier.FromActor(actor, tier, label);
_current = dossier;
LastHeadline = dossier.Headline ?? "";
LastDetail = dossier.DetailLine ?? "";
LastCaptionText = dossier.CaptionText ?? "";
EnsureBuilt();
ApplyLines(dossier.Headline, dossier.ReasonLine, dossier.DetailLine);
SetVisible(ModSettings.ShowDossierCaption && SpectatorMode.Active);
LogService.LogInfo("[IdleSpectator][CAPTION] " + LastCaptionText.Replace("\n", " | "));
}
public static void Update()
{
if (!SpectatorMode.Active || !ModSettings.ShowDossierCaption)
{
if (_visible)
{
SetVisible(false);
}
return;
}
if (_root == null && !string.IsNullOrEmpty(LastCaptionText))
{
EnsureBuilt();
if (_current != null)
{
ApplyLines(_current.Headline, _current.ReasonLine, _current.DetailLine);
}
else
{
ApplyLines(LastHeadline, "", LastDetail);
}
SetVisible(true);
}
}
private static void ApplyLines(string name, string reason, string meta)
{
if (_nameText != null)
{
_nameText.text = string.IsNullOrEmpty(name) ? "" : name;
}
if (_reasonText != null)
{
_reasonText.text = string.IsNullOrEmpty(reason) ? "" : reason;
}
if (_metaText != null)
{
_metaText.text = string.IsNullOrEmpty(meta) ? "" : meta;
}
}
private static void SetVisible(bool visible)
{
EnsureBuilt();
if (_root == null)
{
return;
}
_visible = visible;
_root.SetActive(visible);
}
private static void EnsureBuilt()
{
if (_root != null)
{
return;
}
Canvas canvas = HudCanvas.Resolve();
if (canvas == null)
{
return;
}
_root = new GameObject("IdleSpectatorDossierHud", typeof(RectTransform), typeof(Image), typeof(CanvasGroup));
_root.transform.SetParent(canvas.transform, false);
RectTransform rt = _root.GetComponent<RectTransform>();
rt.anchorMin = new Vector2(0f, 1f);
rt.anchorMax = new Vector2(0f, 1f);
rt.pivot = new Vector2(0f, 1f);
rt.sizeDelta = new Vector2(PanelWidth, PanelHeight);
rt.anchoredPosition = new Vector2(12f, -12f);
Image bg = _root.GetComponent<Image>();
bg.color = new Color(0.05f, 0.06f, 0.08f, 0.72f);
bg.raycastTarget = false;
CanvasGroup group = _root.GetComponent<CanvasGroup>();
group.blocksRaycasts = false;
group.interactable = false;
_nameText = HudCanvas.MakeText(_root.transform, "Name", "", 12);
PlaceLine(_nameText.GetComponent<RectTransform>(), -6f, 22f);
_nameText.fontStyle = FontStyle.Bold;
_nameText.color = new Color(0.95f, 0.93f, 0.88f, 1f);
_nameText.alignment = TextAnchor.MiddleLeft;
_reasonText = HudCanvas.MakeText(_root.transform, "Reason", "", 10);
PlaceLine(_reasonText.GetComponent<RectTransform>(), -28f, 18f);
_reasonText.color = new Color(0.95f, 0.72f, 0.38f, 1f);
_reasonText.alignment = TextAnchor.MiddleLeft;
_metaText = HudCanvas.MakeText(_root.transform, "Meta", "", 9);
PlaceLine(_metaText.GetComponent<RectTransform>(), -46f, 18f);
_metaText.color = new Color(0.72f, 0.75f, 0.78f, 1f);
_metaText.alignment = TextAnchor.MiddleLeft;
_root.SetActive(false);
_visible = false;
LogService.LogInfo("[IdleSpectator] Dossier HUD ready (top-left card)");
}
private static void PlaceLine(RectTransform lineRt, float yFromTop, float height)
{
lineRt.anchorMin = new Vector2(0f, 1f);
lineRt.anchorMax = new Vector2(1f, 1f);
lineRt.pivot = new Vector2(0.5f, 1f);
lineRt.sizeDelta = new Vector2(-16f, height);
lineRt.anchoredPosition = new Vector2(0f, yFromTop);
}
}