First commit for private source control. Older commits available on Github.

This commit is contained in:
2026-03-26 12:52:52 +00:00
parent a04c602626
commit 2d449c4a17
2176 changed files with 408185 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
// ==============================
// HintManager.cs (fallback to nearest lower stage)
// ==============================
using System;
using System.Collections.Generic;
using System.Threading;
using BriarQueen.Framework.Coordinators.Events;
using BriarQueen.Framework.Events.Gameplay;
using BriarQueen.Framework.Events.Progression;
using BriarQueen.Framework.Managers.Hints.Data;
using BriarQueen.Framework.Managers.Levels.Data;
using Cysharp.Threading.Tasks;
using UnityEngine;
using VContainer;
namespace BriarQueen.Framework.Managers.Hints
{
public class HintManager : IDisposable, IManager
{
private readonly EventCoordinator _eventCoordinator;
private readonly Dictionary<int, BaseHint> _hints = new();
private CancellationTokenSource _activeHintCts;
private BaseLevel _currentLevel;
private CancellationTokenSource _levelCts;
public bool Initialized { get; private set; }
[Inject]
public HintManager(EventCoordinator eventCoordinator)
{
_eventCoordinator = eventCoordinator;
}
public IReadOnlyDictionary<int, BaseHint> Hints => _hints;
public void Dispose()
{
_eventCoordinator.Unsubscribe<LevelChangedEvent>(OnLevelChanged);
_eventCoordinator.Unsubscribe<RequestHintEvent>(OnHintRequested);
CancelAndDispose(ref _activeHintCts);
CancelAndDispose(ref _levelCts);
_currentLevel = null;
_hints.Clear();
}
public void Initialize()
{
_eventCoordinator.Subscribe<LevelChangedEvent>(OnLevelChanged);
_eventCoordinator.Subscribe<RequestHintEvent>(OnHintRequested);
Initialized = true;
}
private void OnLevelChanged(LevelChangedEvent evt)
{
CancelAndDispose(ref _activeHintCts);
CancelAndDispose(ref _levelCts);
_levelCts = new CancellationTokenSource();
_hints.Clear();
_currentLevel = evt?.Level;
if (_currentLevel?.Hints == null) return;
foreach (var kvp in _currentLevel.Hints) _hints[kvp.Key] = kvp.Value;
}
private void OnHintRequested(RequestHintEvent evt)
{
RequestHint().Forget();
}
/// <summary>
/// Plays the hint for the current hint stage.
/// If no hint exists at that stage, it falls back to the nearest lower stage that has a hint.
/// </summary>
public async UniTask RequestHint()
{
if (_currentLevel == null || _hints.Count == 0) return;
var stage = Mathf.Max(0, _currentLevel.CurrentLevelHintStage);
if (!TryGetHintForStageOrFallback(stage, out var resolvedStage, out var hint) || hint == null)
// Optional: later show a “No hints available” toast
return;
// Cancel any in-progress hint playback (spam-proof).
CancelAndDispose(ref _activeHintCts);
_activeHintCts = new CancellationTokenSource();
var levelToken = _levelCts?.Token ?? CancellationToken.None;
using var linked = CancellationTokenSource.CreateLinkedTokenSource(levelToken, _activeHintCts.Token);
try
{
await hint.Activate().AttachExternalCancellation(linked.Token);
}
catch (OperationCanceledException)
{
// Fine: new request or level changed.
}
catch (Exception ex)
{
Debug.LogWarning(
$"[HintManager] Hint activation threw at requested stage {stage} (resolved {resolvedStage}): {ex}");
}
}
private bool TryGetHintForStageOrFallback(int requestedStage, out int resolvedStage, out BaseHint hint)
{
// Exact stage
if (_hints.TryGetValue(requestedStage, out hint) && hint != null)
{
resolvedStage = requestedStage;
return true;
}
// ✅ Fallback: nearest lower stage that exists
// (Designers can skip stages; hint still works.)
var best = -1;
// If you prefer performance, you can cache/sort keys once per level.
foreach (var k in _hints.Keys)
if (k <= requestedStage && k > best)
best = k;
if (best >= 0 && _hints.TryGetValue(best, out hint) && hint != null)
{
resolvedStage = best;
return true;
}
resolvedStage = -1;
hint = null;
return false;
}
private static void CancelAndDispose(ref CancellationTokenSource cts)
{
if (cts == null) return;
try
{
cts.Cancel();
}
catch
{
/* ignore */
}
cts.Dispose();
cts = null;
}
}
}