using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using TMPro;
/// <summary>
/// GAME 1303 Tier 6 — A list of what the player has found out. Detective Liz's case notes,
/// Anabasis's recovered memories.
///
/// SETUP
/// 1. Empty GameObject + this script (tick Persist Across Scenes if the case spans scenes).
/// 2. Make a Clue entry per discoverable fact: an id, a title, and the text.
/// 3. Discover one from any Interactable: OnInteract -> ClueJournal.Discover, then type
/// the clue id in the string field.
/// 4. Optional UI: drag in a panel, a TMP text, and a Clue Entry Prefab (a TMP text) and
/// it will list everything found. Toggle the panel with a UI button or the "Journal"
/// input action.
/// 5. Each discovered clue also sets the flag "clue_<id>", so dialogue lines and endings
/// can react with Require Flags — that's how "ask the ghost about the locked room"
/// appears only after you found the room.
///
/// The count is what makes an investigation feel like progress: OnClueCountChanged
/// drives a "4 / 9 clues" HUD readout.
/// </summary>
public class ClueJournal : MonoBehaviour
{
public static ClueJournal Instance { get; private set; }
[System.Serializable]
public class Clue
{
public string id = "clue_1";
public string title = "A torn photograph";
[TextArea(2, 5)] public string text = "";
public Sprite icon;
[Tooltip("Which floor / chapter this belongs to, for grouping. Optional.")]
public string category = "";
}
public bool persistAcrossScenes = true;
public List<Clue> allClues = new List<Clue>();
[Header("UI (optional)")]
public GameObject panel;
public Transform entryContainer;
public TMP_Text entryPrefab;
public TMP_Text counterText;
public string counterFormat = "{0} / {1} clues";
[Header("Feedback")]
public string discoverSfx = "clue_found";
[Header("Events")]
public UnityEvent<string> OnClueDiscovered; // clue id
public UnityEvent<int, int> OnClueCountChanged; // (found, total)
public UnityEvent OnAllCluesFound;
private readonly HashSet<string> found = new HashSet<string>();
public int FoundCount => found.Count;
public int TotalCount => allClues.Count;
void Awake()
{
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this;
if (persistAcrossScenes) DontDestroyOnLoad(gameObject);
if (panel != null) panel.SetActive(false);
}
void Start() => OnClueCountChanged?.Invoke(FoundCount, TotalCount);
/// <summary>Wire an Interactable's OnInteract here and type the clue id.</summary>
public void Discover(string clueId)
{
if (string.IsNullOrEmpty(clueId) || !found.Add(clueId)) return;
ChoiceFlagManager.Instance?.SetFlag("clue_" + clueId);
if (!string.IsNullOrEmpty(discoverSfx)) AudioManager.Instance?.PlaySFX(discoverSfx);
OnClueDiscovered?.Invoke(clueId);
OnClueCountChanged?.Invoke(FoundCount, TotalCount);
if (FoundCount >= TotalCount && TotalCount > 0) OnAllCluesFound?.Invoke();
if (panel != null && panel.activeSelf) Rebuild();
}
public bool HasFound(string clueId) => found.Contains(clueId);
public Clue GetClue(string clueId) => allClues.Find(c => c.id == clueId);
public void TogglePanel()
{
if (panel == null) return;
bool show = !panel.activeSelf;
panel.SetActive(show);
if (show) Rebuild();
}
void Rebuild()
{
if (entryContainer == null || entryPrefab == null) return;
foreach (Transform child in entryContainer) Destroy(child.gameObject);
foreach (var clue in allClues)
{
if (!found.Contains(clue.id)) continue;
var entry = Instantiate(entryPrefab, entryContainer);
entry.text = $"<b>{clue.title}</b>\n{clue.text}";
}
if (counterText != null) counterText.text = string.Format(counterFormat, FoundCount, TotalCount);
}
/// <summary>New game / testing.</summary>
public void ResetJournal()
{
found.Clear();
OnClueCountChanged?.Invoke(0, TotalCount);
if (panel != null && panel.activeSelf) Rebuild();
}
}