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;
}
}