using System.Collections.Generic; using UnityEngine; using Unheimlich.Core; using Unheimlich.Data.DetectiveBoard; namespace Unheimlich.Managers { /// /// Manages the detective board state and clue unlocking system. /// Tracks unlocked clues, connections, certainty levels, and eliminated hypotheses. /// Does NOT affect game progression - purely interpretive tool for the player. /// public class DetectiveBoardManager : Singleton { public enum CertaintyLevel { Uncertain, Possible, Confident, Certain } public class ClueState { public ClueData Data; public bool IsUnlocked; public bool IsEliminated; public Vector2 BoardPosition; } public class Connection { public string FromClueID; public string ToClueID; public CertaintyLevel Certainty; public bool IsEliminated; } [Header("References")] [SerializeField] private DetectiveBoardSettings _settings; private Dictionary _clueStates = new Dictionary(); private List _connections = new List(); public DetectiveBoardSettings Settings => _settings; public IReadOnlyDictionary ClueStates => _clueStates; public IReadOnlyList Connections => _connections; /// /// Unlocks a clue on the detective board. Can be triggered by any in-game event. /// public void UnlockClue(ClueData clueData) { if (clueData == null) { Debug.LogError("[DetectiveBoardManager] Attempted to unlock null clue!"); return; } if (!_clueStates.ContainsKey(clueData.ClueID)) { _clueStates[clueData.ClueID] = new ClueState { Data = clueData, IsUnlocked = true, IsEliminated = false, BoardPosition = clueData.InitialPosition }; OnClueUnlocked?.Invoke(clueData); } else if (!_clueStates[clueData.ClueID].IsUnlocked) { _clueStates[clueData.ClueID].IsUnlocked = true; OnClueUnlocked?.Invoke(clueData); } } /// /// Returns a list of all currently unlocked clues. /// Used by DetectiveBoardCanvas to display already-unlocked clues on initialization. /// public List GetUnlockedClues() { List unlockedClues = new List(); foreach (var state in _clueStates.Values) { if (state.IsUnlocked) { unlockedClues.Add(state.Data); } } return unlockedClues; } /// /// Unlocks a clue by its ID string. /// public void UnlockClueByID(string clueID) { if (_clueStates.TryGetValue(clueID, out ClueState state)) { if (!state.IsUnlocked) { state.IsUnlocked = true; OnClueUnlocked?.Invoke(state.Data); } } else { Debug.LogWarning($"[DetectiveBoardManager] Clue ID '{clueID}' not found!"); } } /// /// Registers a clue in the system without unlocking it. /// public void RegisterClue(ClueData clueData) { if (clueData == null || _clueStates.ContainsKey(clueData.ClueID)) return; _clueStates[clueData.ClueID] = new ClueState { Data = clueData, IsUnlocked = false, IsEliminated = false, BoardPosition = clueData.InitialPosition }; } /// /// Creates a connection between two clues. /// public void CreateConnection(string fromClueID, string toClueID, CertaintyLevel certainty = CertaintyLevel.Uncertain) { if (!_clueStates.ContainsKey(fromClueID) || !_clueStates.ContainsKey(toClueID)) { Debug.LogWarning($"[DetectiveBoardManager] Cannot create connection - one or both clues not found!"); return; } Connection existing = _connections.Find(c => (c.FromClueID == fromClueID && c.ToClueID == toClueID) || (c.FromClueID == toClueID && c.ToClueID == fromClueID)); if (existing != null) { existing.Certainty = certainty; } else { _connections.Add(new Connection { FromClueID = fromClueID, ToClueID = toClueID, Certainty = certainty, IsEliminated = false }); OnConnectionCreated?.Invoke(fromClueID, toClueID); } } /// /// Marks a clue as eliminated (incorrect hypothesis). /// public void EliminateClue(string clueID) { if (_clueStates.TryGetValue(clueID, out ClueState state)) { state.IsEliminated = true; OnClueEliminated?.Invoke(clueID); } } /// /// Marks a connection as eliminated. /// public void EliminateConnection(string fromClueID, string toClueID) { Connection connection = _connections.Find(c => (c.FromClueID == fromClueID && c.ToClueID == toClueID) || (c.FromClueID == toClueID && c.ToClueID == fromClueID)); if (connection != null) { connection.IsEliminated = true; } } /// /// Updates a clue's position on the board. /// public void UpdateCluePosition(string clueID, Vector2 newPosition) { if (_clueStates.TryGetValue(clueID, out ClueState state)) { state.BoardPosition = newPosition; } } public event System.Action OnClueUnlocked; public event System.Action OnConnectionCreated; public event System.Action OnClueEliminated; } }