461 lines
14 KiB
C#
461 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BriarQueen.Data.Identifiers;
|
|
using BriarQueen.Data.IO.Saves;
|
|
using BriarQueen.Framework.Coordinators.Events;
|
|
using BriarQueen.Framework.Events.Gameplay;
|
|
using BriarQueen.Framework.Events.Save;
|
|
using BriarQueen.Framework.Events.UI;
|
|
using BriarQueen.Framework.Managers.Audio;
|
|
using BriarQueen.Framework.Managers.IO;
|
|
using BriarQueen.Framework.Managers.Player.Data;
|
|
using BriarQueen.Framework.Managers.Player.Data.Codex;
|
|
using BriarQueen.Framework.Managers.Player.Data.Inventory;
|
|
using BriarQueen.Framework.Managers.Player.Data.Tools;
|
|
using BriarQueen.Framework.Registries;
|
|
using BriarQueen.Framework.Services.Settings;
|
|
using BriarQueen.Framework.Services.Tutorials;
|
|
using UnityEngine;
|
|
using VContainer;
|
|
|
|
namespace BriarQueen.Framework.Managers.Player
|
|
{
|
|
/// <summary>
|
|
/// Owns player state (inventory + codex + tools) and keeps it in sync with save data.
|
|
/// </summary>
|
|
public class PlayerManager : IDisposable, IManager
|
|
{
|
|
private readonly CodexRegistry _codexRegistry;
|
|
private readonly EventCoordinator _eventCoordinator;
|
|
private readonly ItemRegistry _itemRegistry;
|
|
private readonly SaveManager _saveManager;
|
|
private readonly AudioManager _audioManager;
|
|
private readonly TutorialService _tutorialService;
|
|
private readonly SettingsService _settingsService;
|
|
|
|
private Backpack _backpack;
|
|
private Codex _codex;
|
|
private Toolbelt _toolbelt;
|
|
|
|
public bool Initialized { get; private set; }
|
|
|
|
[Inject]
|
|
public PlayerManager(
|
|
SaveManager saveManager,
|
|
ItemRegistry itemRegistry,
|
|
CodexRegistry codexRegistry,
|
|
EventCoordinator eventCoordinator,
|
|
TutorialService tutorialService,
|
|
AudioManager audioManager,
|
|
SettingsService settingsService)
|
|
{
|
|
_saveManager = saveManager;
|
|
_itemRegistry = itemRegistry;
|
|
_codexRegistry = codexRegistry;
|
|
_eventCoordinator = eventCoordinator;
|
|
_tutorialService = tutorialService;
|
|
_audioManager = audioManager;
|
|
_settingsService = settingsService;
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
if (Initialized)
|
|
return;
|
|
|
|
_saveManager.OnSaveGameLoaded += LoadFromSave;
|
|
_saveManager.OnSaveRequested += UpdateSaveGame;
|
|
|
|
_backpack ??= new Backpack();
|
|
_codex ??= new Codex();
|
|
|
|
EnsureToolbelt();
|
|
|
|
if (_saveManager.IsGameLoaded && _saveManager.CurrentSave != null)
|
|
LoadFromSave(_saveManager.CurrentSave);
|
|
|
|
Initialized = true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!Initialized)
|
|
return;
|
|
|
|
_saveManager.OnSaveGameLoaded -= LoadFromSave;
|
|
_saveManager.OnSaveRequested -= UpdateSaveGame;
|
|
|
|
_toolbelt?.Dispose();
|
|
_toolbelt = null;
|
|
|
|
Initialized = false;
|
|
}
|
|
|
|
public IReadOnlyList<ItemDataSo> GetInventoryItems()
|
|
{
|
|
return _backpack?.Items ?? Array.Empty<ItemDataSo>();
|
|
}
|
|
|
|
public IReadOnlyList<ItemDataSo> GetAllCollectedItems()
|
|
{
|
|
var collectedItems = new List<ItemDataSo>();
|
|
var saveCollected = _saveManager.CurrentSave?.CollectedItems;
|
|
|
|
if (saveCollected == null)
|
|
return collectedItems;
|
|
|
|
foreach (var item in saveCollected)
|
|
{
|
|
var template = _itemRegistry.FindItemTemplateByID(item.UniqueIdentifier);
|
|
if (template != null)
|
|
collectedItems.Add(template);
|
|
}
|
|
|
|
return collectedItems;
|
|
}
|
|
|
|
public IReadOnlyList<CodexEntrySo> GetDiscoveredCodexEntries()
|
|
{
|
|
return _codex?.Entries ?? Array.Empty<CodexEntrySo>();
|
|
}
|
|
|
|
public IEnumerable<CodexEntrySo> GetDiscoveredCodexEntriesByType(CodexType codexType)
|
|
{
|
|
return _codex?.GetEntriesByType(codexType) ?? Enumerable.Empty<CodexEntrySo>();
|
|
}
|
|
|
|
public bool HasCodexEntry(string uniqueIdentifier)
|
|
{
|
|
return _codex != null && _codex.HasEntry(uniqueIdentifier);
|
|
}
|
|
|
|
public bool HasCodexEntry(CodexEntrySo entry)
|
|
{
|
|
return _codex != null && _codex.HasEntry(entry);
|
|
}
|
|
|
|
public IReadOnlyDictionary<ToolID, bool> GetTools()
|
|
{
|
|
EnsureToolbelt();
|
|
return _toolbelt.UnlockedTools;
|
|
}
|
|
|
|
public bool HasAccessToTool(ToolID id)
|
|
{
|
|
return _toolbelt != null && _toolbelt.HasAccess(id);
|
|
}
|
|
|
|
#region Inventory / Selection
|
|
|
|
public void CollectItem(string uniqueIdentifier)
|
|
{
|
|
var item = _itemRegistry.FindItemTemplateByID(uniqueIdentifier);
|
|
if (item == null)
|
|
return;
|
|
|
|
CollectItem(item);
|
|
}
|
|
|
|
public void CollectItem(ItemDataSo item)
|
|
{
|
|
if (item == null)
|
|
return;
|
|
|
|
_backpack ??= new Backpack();
|
|
_backpack.AddItem(item);
|
|
|
|
var save = _saveManager.CurrentSave;
|
|
if (save != null)
|
|
{
|
|
save.CollectedItems ??= new List<ItemSaveData>();
|
|
|
|
if (save.CollectedItems.All(x => x.UniqueIdentifier != item.UniqueID))
|
|
{
|
|
save.CollectedItems.Add(new ItemSaveData
|
|
{
|
|
UniqueIdentifier = item.UniqueID
|
|
});
|
|
}
|
|
}
|
|
|
|
_audioManager.Play(AudioNameIdentifiers.Get(SFXKey.ItemCollected));
|
|
_eventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
|
_eventCoordinator.Publish(new InventoryChangedEvent());
|
|
}
|
|
|
|
public void RemoveItem(string uniqueIdentifier)
|
|
{
|
|
var item = _backpack?.Items.FirstOrDefault(x => x.UniqueID == uniqueIdentifier);
|
|
if (item == null)
|
|
return;
|
|
|
|
RemoveItem(item);
|
|
}
|
|
|
|
public void RemoveItem(ItemDataSo item)
|
|
{
|
|
if (item == null || _backpack == null)
|
|
return;
|
|
|
|
_backpack.RemoveItem(item);
|
|
|
|
_eventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
|
_eventCoordinator.Publish(new InventoryChangedEvent());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Codex
|
|
|
|
public void UnlockCodexEntry(string uniqueIdentifier)
|
|
{
|
|
var entry = _codexRegistry.FindEntryByID(uniqueIdentifier);
|
|
if (entry == null)
|
|
return;
|
|
|
|
UnlockCodexEntry(entry);
|
|
}
|
|
|
|
public void UnlockCodexEntry(CodexEntrySo entry)
|
|
{
|
|
if (entry == null)
|
|
return;
|
|
|
|
_codex ??= new Codex();
|
|
|
|
if (_codex.HasEntry(entry))
|
|
return;
|
|
|
|
_codex.AddEntry(entry);
|
|
|
|
var save = _saveManager.CurrentSave;
|
|
if (save != null)
|
|
{
|
|
save.DiscoveredCodexEntries ??= new List<CodexSaveData>();
|
|
|
|
if (save.DiscoveredCodexEntries.All(x => x.UniqueIdentifier != entry.UniqueID))
|
|
{
|
|
save.DiscoveredCodexEntries.Add(new CodexSaveData
|
|
{
|
|
UniqueIdentifier = entry.UniqueID
|
|
});
|
|
}
|
|
}
|
|
|
|
_tutorialService.DisplayTutorial(TutorialPopupID.Codex);
|
|
|
|
_eventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
|
_eventCoordinator.Publish(new CodexChangedEvent(entry.EntryType));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Tools
|
|
|
|
public bool CanUseTool(ToolID id)
|
|
{
|
|
if(_settingsService.Game.AutoUseTools)
|
|
return HasAccessToTool(id);
|
|
|
|
return GetEquippedTool() == id;
|
|
}
|
|
|
|
public ToolID GetEquippedTool()
|
|
{
|
|
EnsureToolbelt();
|
|
return _toolbelt.CurrentTool;
|
|
}
|
|
|
|
public void UnlockTool(ToolID id)
|
|
{
|
|
EnsureToolbelt();
|
|
|
|
if (_toolbelt.HasAccess(id))
|
|
return;
|
|
|
|
_toolbelt.Unlock(id);
|
|
Debug.Log($"Tool {id} has been unlocked");
|
|
|
|
var save = _saveManager.CurrentSave;
|
|
if (save != null)
|
|
{
|
|
save.Tools ??= new Dictionary<ToolID, bool>();
|
|
save.Tools[id] = true;
|
|
}
|
|
|
|
Debug.Log($"Tool {id} access after unlock = {_toolbelt.HasAccess(id)}");
|
|
|
|
_eventCoordinator.Publish(new ToolbeltChangedEvent(id, false));
|
|
_eventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
|
}
|
|
|
|
public void LockTool(ToolID id)
|
|
{
|
|
EnsureToolbelt();
|
|
|
|
if (!_toolbelt.UnlockedTools.TryGetValue(id, out var unlocked) || !unlocked)
|
|
return;
|
|
|
|
_toolbelt.Lock(id);
|
|
|
|
var save = _saveManager.CurrentSave;
|
|
if (save != null)
|
|
{
|
|
save.Tools ??= new Dictionary<ToolID, bool>();
|
|
save.Tools[id] = false;
|
|
}
|
|
|
|
_eventCoordinator.Publish(new ToolbeltChangedEvent(id, true));
|
|
_eventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
|
}
|
|
|
|
private void LoadToolsFromSave(SaveGame save)
|
|
{
|
|
_toolbelt?.Dispose();
|
|
_toolbelt = new Toolbelt(_eventCoordinator, _tutorialService);
|
|
_toolbelt.Initialize();
|
|
|
|
if (save.Tools == null)
|
|
return;
|
|
|
|
foreach (var kvp in save.Tools)
|
|
{
|
|
if (kvp.Value)
|
|
_toolbelt.Unlock(kvp.Key);
|
|
else
|
|
_toolbelt.Lock(kvp.Key);
|
|
}
|
|
}
|
|
|
|
private void EnsureToolbelt()
|
|
{
|
|
if (_toolbelt != null)
|
|
return;
|
|
|
|
_toolbelt = new Toolbelt(_eventCoordinator, _tutorialService);
|
|
_toolbelt.Initialize();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Saving / Loading
|
|
|
|
private void LoadFromSave(SaveGame save)
|
|
{
|
|
if (save == null)
|
|
return;
|
|
|
|
LoadItemsFromSave(save);
|
|
LoadCodexFromSave(save);
|
|
LoadToolsFromSave(save);
|
|
}
|
|
|
|
private void LoadItemsFromSave(SaveGame save)
|
|
{
|
|
_backpack = new Backpack();
|
|
|
|
Debug.Log($"[PlayerManager] LoadItemsFromSave InventoryData count = {save.InventoryData?.Count ?? 0}");
|
|
|
|
if (save.InventoryData != null)
|
|
{
|
|
foreach (var item in save.InventoryData)
|
|
{
|
|
Debug.Log($"[PlayerManager] Loading InventoryData item '{item.UniqueIdentifier}'");
|
|
|
|
var itemData = _itemRegistry.FindItemTemplateByID(item.UniqueIdentifier);
|
|
if (!itemData)
|
|
{
|
|
Debug.LogWarning($"[PlayerManager] Missing item template '{item.UniqueIdentifier}', skipping.");
|
|
continue;
|
|
}
|
|
|
|
_backpack.AddItem(itemData);
|
|
}
|
|
}
|
|
|
|
Debug.Log($"[PlayerManager] Backpack count after load = {_backpack.Items?.Count ?? 0}");
|
|
|
|
_eventCoordinator.Publish(new SelectedItemChangedEvent(null));
|
|
_eventCoordinator.Publish(new InventoryChangedEvent());
|
|
}
|
|
|
|
private void LoadCodexFromSave(SaveGame save)
|
|
{
|
|
_codex = new Codex();
|
|
|
|
if (save.DiscoveredCodexEntries != null)
|
|
{
|
|
foreach (var codexEntry in save.DiscoveredCodexEntries)
|
|
{
|
|
var entryData = _codexRegistry.FindEntryByID(codexEntry.UniqueIdentifier);
|
|
if (!entryData)
|
|
{
|
|
Debug.LogWarning($"[PlayerManager] Missing codex entry '{codexEntry.UniqueIdentifier}', skipping.");
|
|
continue;
|
|
}
|
|
|
|
_codex.AddEntry(entryData);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateSaveGame(SaveGame save)
|
|
{
|
|
if (save == null)
|
|
return;
|
|
|
|
Debug.Log($"[PlayerManager] UpdateSaveGame backpack count = {_backpack?.Items?.Count ?? 0}");
|
|
|
|
save.InventoryData = new List<ItemSaveData>();
|
|
|
|
if (_backpack?.Items != null)
|
|
{
|
|
foreach (var item in _backpack.Items)
|
|
{
|
|
if (!item)
|
|
continue;
|
|
|
|
Debug.Log($"[PlayerManager] Writing InventoryData item '{item.UniqueID}'");
|
|
|
|
save.InventoryData.Add(new ItemSaveData
|
|
{
|
|
UniqueIdentifier = item.UniqueID
|
|
});
|
|
}
|
|
}
|
|
|
|
Debug.Log($"[PlayerManager] Final InventoryData count = {save.InventoryData.Count}");
|
|
|
|
save.DiscoveredCodexEntries ??= new List<CodexSaveData>();
|
|
save.DiscoveredCodexEntries.Clear();
|
|
|
|
if (_codex?.Entries != null)
|
|
{
|
|
foreach (var entry in _codex.Entries)
|
|
{
|
|
if (!entry)
|
|
continue;
|
|
|
|
save.DiscoveredCodexEntries.Add(new CodexSaveData
|
|
{
|
|
UniqueIdentifier = entry.UniqueID
|
|
});
|
|
}
|
|
}
|
|
|
|
save.Tools ??= new Dictionary<ToolID, bool>();
|
|
save.Tools.Clear();
|
|
|
|
if (_toolbelt?.UnlockedTools != null)
|
|
{
|
|
foreach (var kvp in _toolbelt.UnlockedTools)
|
|
{
|
|
save.Tools[kvp.Key] = kvp.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |