using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using VContainer; using YachtDice.Categories; using YachtDice.Dice; using YachtDice.Economy; using YachtDice.Events; using YachtDice.Modifiers.Core; using YachtDice.Modifiers.Runtime; namespace YachtDice.Scoring { public class ScoringSystem : MonoBehaviour { public event Action OnCategoryScored; public event Action OnAllCategoriesScored; public event Action OnCategoryConfirmed; private readonly Dictionary _scorecard = new(); private readonly HashSet _usedCategories = new(); private GameEventBus _eventBus; private ModifierRegistry _modifierRegistry; private CategoryCatalog _catalog; private CurrencyBank _currencyBank; [Inject] public void Construct( GameEventBus eventBus, ModifierRegistry modifierRegistry, CategoryCatalog catalog, CurrencyBank currencyBank) { this._eventBus = eventBus; this._modifierRegistry = modifierRegistry; this._catalog = catalog; this._currencyBank = currencyBank; } public CategoryCatalog Catalog => _catalog; public bool IsCategoryUsed(CategoryDefinition category) => _usedCategories.Contains(category); public int GetCategoryScore(CategoryDefinition category) { return _scorecard.TryGetValue(category, out int score) ? score : -1; } public int TotalScore { get { int total = 0; foreach (var kvp in _scorecard) total += kvp.Value; return total; } } public int CategoriesFilledCount => _usedCategories.Count; public int TotalCategoryCount => _catalog != null ? _catalog.Count : 0; public bool IsComplete => CategoriesFilledCount >= TotalCategoryCount; public ScoreResult PreviewScore(IReadOnlyList dice, CategoryDefinition category, int currentRoll = 0, int currentTurn = 0, int playerCurrency = 0) { int baseScore = category.Calculate(dice); if (_eventBus == null || _modifierRegistry == null) return ScoreResult.Create(baseScore, dice, category); var context = ModifierContext.CreateForScoring( baseScore, dice, category, currentRoll, currentTurn, playerCurrency, _modifierRegistry.Active); _eventBus.Fire(TriggerType.OnCategoryScored, context).Forget(); return context.ToScoreResult(); } public async UniTask ScoreCategoryAsync(IReadOnlyList dice, CategoryDefinition category, int currentRoll, int currentTurn, int playerCurrency) { if (_usedCategories.Contains(category)) throw new InvalidOperationException($"Category {category.DisplayName} has already been scored."); int baseScore = category.Calculate(dice); ModifierContext context; if (_eventBus != null && _modifierRegistry != null) { context = ModifierContext.CreateForScoring( baseScore, dice, category, currentRoll, currentTurn, playerCurrency, _modifierRegistry.Active); await _eventBus.Fire(TriggerType.OnCategoryScored, context); } else { context = new ModifierContext { BaseScore = baseScore, Dice = dice, DiceValues = DiceCheckUtility.ExtractValues(dice), Category = category, }; } var result = context.ToScoreResult(); int finalScore = result.FinalScore; _scorecard[category] = finalScore; _usedCategories.Add(category); if (context.CurrencyDelta != 0 && _currencyBank != null) _currencyBank.Add(context.CurrencyDelta); OnCategoryScored?.Invoke(category, finalScore); OnCategoryConfirmed?.Invoke(category, result); if (IsComplete) OnAllCategoriesScored?.Invoke(TotalScore); return result; } public ScoreResult ScoreCategory(IReadOnlyList dice, CategoryDefinition category) { if (_usedCategories.Contains(category)) throw new InvalidOperationException($"Category {category.DisplayName} has already been scored."); var baseScore = category.Calculate(dice); ModifierContext context = null; if (_eventBus != null && _modifierRegistry != null) { context = ModifierContext.CreateForScoring( baseScore, dice, category, 0, 0, 0, _modifierRegistry.Active); _eventBus.Fire(TriggerType.OnCategoryScored, context).Forget(); } ScoreResult result; if (context != null) result = context.ToScoreResult(); else result = ScoreResult.Create(baseScore, dice, category); var finalScore = result.FinalScore; _scorecard[category] = finalScore; _usedCategories.Add(category); if (context != null && context.CurrencyDelta != 0 && _currencyBank != null) _currencyBank.Add(context.CurrencyDelta); OnCategoryScored?.Invoke(category, finalScore); OnCategoryConfirmed?.Invoke(category, result); if (IsComplete) OnAllCategoriesScored?.Invoke(TotalScore); return result; } public void ResetScorecard() { _scorecard.Clear(); _usedCategories.Clear(); } } }