using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using VContainer; 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; [Inject] public void Construct(GameEventBus eventBus, ModifierRegistry modifierRegistry) { this.eventBus = eventBus; this.modifierRegistry = modifierRegistry; } public bool IsCategoryUsed(YachtCategory category) => usedCategories.Contains(category); public int GetCategoryScore(YachtCategory 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 => Enum.GetValues(typeof(YachtCategory)).Length; public bool IsComplete => CategoriesFilledCount >= TotalCategoryCount; public ScoreResult PreviewScore(int[] diceValues, YachtCategory category, int currentRoll = 0, int currentTurn = 0, int playerCurrency = 0) { int baseScore = CategoryScorer.Calculate(diceValues, category); if (eventBus == null || modifierRegistry == null) return ScoreResult.Create(baseScore, diceValues, category); var context = ModifierContext.CreateForScoring( baseScore, diceValues, category, currentRoll, currentTurn, playerCurrency, modifierRegistry.Active); eventBus.Fire(TriggerType.OnCategoryScored, context).Forget(); return context.ToScoreResult(); } public async UniTask ScoreCategoryAsync(int[] diceValues, YachtCategory category, int currentRoll, int currentTurn, int playerCurrency) { if (usedCategories.Contains(category)) throw new InvalidOperationException($"Category {category} has already been scored."); int baseScore = CategoryScorer.Calculate(diceValues, category); ModifierContext context; if (eventBus != null && modifierRegistry != null) { context = ModifierContext.CreateForScoring( baseScore, diceValues, category, currentRoll, currentTurn, playerCurrency, modifierRegistry.Active); await eventBus.Fire(TriggerType.OnCategoryScored, context); } else { context = new ModifierContext { BaseScore = baseScore, DiceValues = diceValues, Category = category, }; } var result = context.ToScoreResult(); int finalScore = result.FinalScore; scorecard[category] = finalScore; usedCategories.Add(category); OnCategoryScored?.Invoke(category, finalScore); OnCategoryConfirmed?.Invoke(category, result); if (IsComplete) OnAllCategoriesScored?.Invoke(TotalScore); return result; } public ScoreResult ScoreCategory(int[] diceValues, YachtCategory category) { if (usedCategories.Contains(category)) throw new InvalidOperationException($"Category {category} has already been scored."); int baseScore = CategoryScorer.Calculate(diceValues, category); ModifierContext context = null; if (eventBus != null && modifierRegistry != null) { context = ModifierContext.CreateForScoring( baseScore, diceValues, 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, diceValues, category); int finalScore = result.FinalScore; scorecard[category] = finalScore; usedCategories.Add(category); OnCategoryScored?.Invoke(category, finalScore); OnCategoryConfirmed?.Invoke(category, result); if (IsComplete) OnAllCategoriesScored?.Invoke(TotalScore); return result; } public void ResetScorecard() { scorecard.Clear(); usedCategories.Clear(); } } }