using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using VContainer; using YachtDice.Categories; using YachtDice.Dice; 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; [Inject] public void Construct(GameEventBus eventBus, ModifierRegistry modifierRegistry, CategoryCatalog catalog) { this.eventBus = eventBus; this.modifierRegistry = modifierRegistry; this.catalog = catalog; } 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); 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."); int 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); 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(); } } }