Add detective board
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unheimlich.Core;
|
||||
using Unheimlich.Data.DetectiveBoard;
|
||||
|
||||
namespace Unheimlich.Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class DetectiveBoardManager : Singleton<DetectiveBoardManager>
|
||||
{
|
||||
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<string, ClueState> _clueStates = new Dictionary<string, ClueState>();
|
||||
private List<Connection> _connections = new List<Connection>();
|
||||
|
||||
public DetectiveBoardSettings Settings => _settings;
|
||||
public IReadOnlyDictionary<string, ClueState> ClueStates => _clueStates;
|
||||
public IReadOnlyList<Connection> Connections => _connections;
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a clue on the detective board. Can be triggered by any in-game event.
|
||||
/// </summary>
|
||||
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
|
||||
};
|
||||
|
||||
Debug.Log($"[DetectiveBoardManager] Unlocked clue: {clueData.Title}");
|
||||
OnClueUnlocked?.Invoke(clueData);
|
||||
}
|
||||
else if (!_clueStates[clueData.ClueID].IsUnlocked)
|
||||
{
|
||||
_clueStates[clueData.ClueID].IsUnlocked = true;
|
||||
Debug.Log($"[DetectiveBoardManager] Unlocked clue: {clueData.Title}");
|
||||
OnClueUnlocked?.Invoke(clueData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a clue by its ID string.
|
||||
/// </summary>
|
||||
public void UnlockClueByID(string clueID)
|
||||
{
|
||||
if (_clueStates.TryGetValue(clueID, out ClueState state))
|
||||
{
|
||||
if (!state.IsUnlocked)
|
||||
{
|
||||
state.IsUnlocked = true;
|
||||
Debug.Log($"[DetectiveBoardManager] Unlocked clue: {state.Data.Title}");
|
||||
OnClueUnlocked?.Invoke(state.Data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[DetectiveBoardManager] Clue ID '{clueID}' not found!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a clue in the system without unlocking it.
|
||||
/// </summary>
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a connection between two clues.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a clue as eliminated (incorrect hypothesis).
|
||||
/// </summary>
|
||||
public void EliminateClue(string clueID)
|
||||
{
|
||||
if (_clueStates.TryGetValue(clueID, out ClueState state))
|
||||
{
|
||||
state.IsEliminated = true;
|
||||
OnClueEliminated?.Invoke(clueID);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a connection as eliminated.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a clue's position on the board.
|
||||
/// </summary>
|
||||
public void UpdateCluePosition(string clueID, Vector2 newPosition)
|
||||
{
|
||||
if (_clueStates.TryGetValue(clueID, out ClueState state))
|
||||
{
|
||||
state.BoardPosition = newPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public event System.Action<ClueData> OnClueUnlocked;
|
||||
public event System.Action<string, string> OnConnectionCreated;
|
||||
public event System.Action<string> OnClueEliminated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b187cdccaa498441a752e8f49ff4290
|
||||
@@ -107,8 +107,8 @@ namespace Unheimlich.Managers
|
||||
}
|
||||
}
|
||||
|
||||
if (PlayerInputManager.Instance != null &&
|
||||
PlayerInputManager.Instance.InteractAction.action.WasPressedThisFrame())
|
||||
var interactAction = PlayerInputManager.GetInteractAction();
|
||||
if (interactAction != null && interactAction.WasPressedThisFrame())
|
||||
{
|
||||
EndDialogue();
|
||||
}
|
||||
|
||||
@@ -26,18 +26,19 @@ namespace Unheimlich.Managers
|
||||
[Header("Input Values (Read only)")]
|
||||
public Vector2 MoveInput;
|
||||
public Vector2 LookInput;
|
||||
public InputActionReference JumpAction;
|
||||
public InputActionReference InteractAction;
|
||||
public InputActionReference DiapasonAction;
|
||||
|
||||
public static bool IsInputEnabled { get; private set; } = true;
|
||||
public static bool IsLookingEnabled { get; private set; } = true;
|
||||
public static InputContext CurrentContext { get; private set; } = InputContext.Normal;
|
||||
|
||||
private InputActionMap _playerActionMap;
|
||||
private InputAction _moveAction;
|
||||
private InputAction _lookAction;
|
||||
private InputAction _jumpAction;
|
||||
private InputAction _interactAction;
|
||||
private InputAction _diapasonAction;
|
||||
private InputAction _fireAction;
|
||||
private InputAction _exitAction;
|
||||
|
||||
private PlayerMovement _playerMovement;
|
||||
private PlayerLook _playerLook;
|
||||
@@ -51,14 +52,13 @@ namespace Unheimlich.Managers
|
||||
playerInput.actions.Enable();
|
||||
|
||||
_playerActionMap = playerInput.actions.FindActionMap("Player");
|
||||
_moveAction = _playerActionMap.FindAction("Move");
|
||||
_lookAction = _playerActionMap.FindAction("Look");
|
||||
_jumpAction = _playerActionMap.FindAction("Jump");
|
||||
_interactAction = _playerActionMap.FindAction("Interact");
|
||||
_diapasonAction = _playerActionMap.FindAction("Diapason");
|
||||
|
||||
JumpAction.action.Enable();
|
||||
InteractAction.action.Enable();
|
||||
if (DiapasonAction != null)
|
||||
DiapasonAction.action.Enable();
|
||||
_fireAction = _playerActionMap.FindAction("Fire");
|
||||
_exitAction = _playerActionMap.FindAction("Exit");
|
||||
|
||||
_playerMovement = GetComponent<PlayerMovement>();
|
||||
_playerLook = GetComponent<PlayerLook>();
|
||||
@@ -101,7 +101,11 @@ namespace Unheimlich.Managers
|
||||
Instance._lookAction.Enable();
|
||||
if (Instance._diapasonAction != null)
|
||||
Instance._diapasonAction.Enable();
|
||||
Instance.SetComponentsEnabled(false, false, false);
|
||||
if (Instance._fireAction != null)
|
||||
Instance._fireAction.Enable();
|
||||
if (Instance._exitAction != null)
|
||||
Instance._exitAction.Enable();
|
||||
Instance.SetComponentsEnabled(false, false, true);
|
||||
IsInputEnabled = false;
|
||||
IsLookingEnabled = false;
|
||||
break;
|
||||
@@ -152,5 +156,24 @@ namespace Unheimlich.Managers
|
||||
public void OnDiapason(InputAction.CallbackContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnFire(InputAction.CallbackContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnExit(InputAction.CallbackContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides public access to input actions for other systems.
|
||||
/// </summary>
|
||||
public static InputAction GetMoveAction() => Instance?._moveAction;
|
||||
public static InputAction GetLookAction() => Instance?._lookAction;
|
||||
public static InputAction GetJumpAction() => Instance?._jumpAction;
|
||||
public static InputAction GetInteractAction() => Instance?._interactAction;
|
||||
public static InputAction GetDiapasonAction() => Instance?._diapasonAction;
|
||||
public static InputAction GetFireAction() => Instance?._fireAction;
|
||||
public static InputAction GetExitAction() => Instance?._exitAction;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user