using System.Collections.Generic; using UnityEngine; using VContainer; using YachtDice.Categories; using YachtDice.Dice; using YachtDice.Game; using YachtDice.Scoring; using YachtDice.Economy; using YachtDice.Shop; using YachtDice.Inventory; using YachtDice.Persistence; using YachtDice.Modifiers.Definition; using YachtDice.Modifiers.Runtime; namespace YachtDice.UI { public class GameController : MonoBehaviour { [Header("Model")] [SerializeField] private GameManager gameManager; [SerializeField] private ScoringSystem scoringSystem; [SerializeField] private DiceManager diceManager; [Header("Views")] [SerializeField] private ScoreCardView scoreCardView; [SerializeField] private DicePanelView dicePanelView; [SerializeField] private GameInfoView gameInfoView; [Header("Economy & Modifiers")] [SerializeField] private CurrencyBank currencyBank; [SerializeField] private ShopController shopController; [SerializeField] private InventoryController inventoryController; [Header("Settings")] [SerializeField] private int maxRollsPerTurn = 3; [SerializeField] private int maxActiveModifierSlots = 5; private const int UpperBonusThreshold = 63; private const int UpperBonusValue = 35; private ModifierRegistry modifierRegistry; private CategoryCatalogSO categoryCatalog; private InventoryModel inventoryModel; private ShopModel shopModel; [Inject] public void Construct(ModifierRegistry modifierRegistry, CategoryCatalogSO categoryCatalog) { this.modifierRegistry = modifierRegistry; this.categoryCatalog = categoryCatalog; } // ── Lifecycle ────────────────────────────────────────────── private void Awake() { // Model → Controller gameManager.OnTurnStarted += HandleTurnStarted; gameManager.OnRollComplete += HandleRollComplete; gameManager.OnScored += HandleScored; gameManager.OnGameOver += HandleGameOver; diceManager.OnDieSettled += HandleDieSettled; // View → Controller scoreCardView.OnCategorySelected += HandleCategorySelected; dicePanelView.OnRollClicked += HandleRollClicked; dicePanelView.OnDiceToggled += HandleDiceToggled; gameInfoView.OnNewGameClicked += HandleNewGameClicked; gameInfoView.OnShopClicked += HandleShopClicked; gameInfoView.OnInventoryClicked += HandleInventoryClicked; // Currency if (currencyBank != null) currencyBank.OnBalanceChanged += HandleCurrencyChanged; } private void Start() { // Инициализируем скоркарту из каталога категорий scoreCardView.Initialize(categoryCatalog); InitializeModifierSystems(); } private void OnDestroy() { gameManager.OnTurnStarted -= HandleTurnStarted; gameManager.OnRollComplete -= HandleRollComplete; gameManager.OnScored -= HandleScored; gameManager.OnGameOver -= HandleGameOver; diceManager.OnDieSettled -= HandleDieSettled; scoreCardView.OnCategorySelected -= HandleCategorySelected; dicePanelView.OnRollClicked -= HandleRollClicked; dicePanelView.OnDiceToggled -= HandleDiceToggled; gameInfoView.OnNewGameClicked -= HandleNewGameClicked; gameInfoView.OnShopClicked -= HandleShopClicked; gameInfoView.OnInventoryClicked -= HandleInventoryClicked; if (currencyBank != null) currencyBank.OnBalanceChanged -= HandleCurrencyChanged; if (modifierRegistry != null) modifierRegistry.OnChanged -= HandleInventoryChangedForSave; } // ── Modifier System Init ───────────────────────────────── private void InitializeModifierSystems() { if (modifierRegistry == null) modifierRegistry = new ModifierRegistry(maxActiveModifierSlots); else modifierRegistry.SetMaxActiveSlots(maxActiveModifierSlots); modifierRegistry.OnChanged += HandleInventoryChangedForSave; inventoryModel = new InventoryModel(modifierRegistry); shopModel = new ShopModel(currencyBank, inventoryModel); ModifierCatalogSO catalog = shopController != null ? shopController.Catalog : null; LoadSaveData(catalog); if (inventoryController != null) inventoryController.Initialize(inventoryModel); if (shopController != null) shopController.Initialize(shopModel); if (currencyBank != null) gameInfoView.SetCurrencyText(currencyBank.Balance); } private void LoadSaveData(ModifierCatalogSO catalog) { SaveData save = SaveSystem.Load(); if (currencyBank != null && save.Currency > 0) currencyBank.SetBalance(save.Currency); if (catalog != null && save.OwnedModifiers.Count > 0) { var entries = new List(); var permanentIds = new HashSet(); for (int i = 0; i < save.OwnedModifiers.Count; i++) { var oldEntry = save.OwnedModifiers[i]; var def = catalog.FindById(oldEntry.ModifierId); if (def == null) { Debug.LogWarning($"Modifier '{oldEntry.ModifierId}' not found in catalog, skipping."); continue; } entries.Add(new ModifierSaveEntry { ModifierId = oldEntry.ModifierId, IsActive = oldEntry.IsActive, RemainingUses = oldEntry.RemainingUses, Stacks = oldEntry.Stacks, CustomState = oldEntry.CustomState, }); if (!def.HasLimitedUses) permanentIds.Add(def.Id); } modifierRegistry.LoadSaveData(entries, catalog); shopModel.LoadPurchasedPermanentIds(permanentIds); } } private void PerformSave() { var save = new SaveData { Currency = currencyBank != null ? currencyBank.Balance : 0 }; var entries = modifierRegistry.GetSaveData(); for (int i = 0; i < entries.Count; i++) { save.OwnedModifiers.Add(entries[i]); } SaveSystem.Save(save); } // ── Model Event Handlers ────────────────────────────────── private void HandleTurnStarted(int turn) { int totalCategoryCount = categoryCatalog.Count; gameInfoView.SetTurnText(turn, totalCategoryCount); dicePanelView.ResetForNewTurn(); dicePanelView.SetRollButtonState(true, 0, maxRollsPerTurn); scoreCardView.ClearAllPreviews(); } private void HandleRollComplete(int rollNumber) { bool canRollAgain = gameManager.CanRoll; dicePanelView.SetRollButtonState(canRollAgain, rollNumber, maxRollsPerTurn); dicePanelView.SetDiceInteractable(true); int[] values = diceManager.GetCurrentValues(); dicePanelView.SetAllDiceValues(values); UpdatePreviewScores(); } private void HandleDieSettled(int index, int value) { dicePanelView.SetDieValue(index, value); } private void HandleScored(CategoryDefinitionSO category, int finalScore) { scoreCardView.SetCategoryScored(category, finalScore); UpdateTotalDisplay(); PerformSave(); } private void HandleGameOver(int totalScore) { dicePanelView.SetRollButtonState(false, maxRollsPerTurn, maxRollsPerTurn); dicePanelView.SetDiceInteractable(false); scoreCardView.SetAllInteractable(false); int displayTotal = CalculateDisplayTotal(); gameInfoView.ShowGameOver(displayTotal); PerformSave(); } // ── View Event Handlers ─────────────────────────────────── private void HandleRollClicked() { if (!gameManager.CanRoll) return; dicePanelView.SetRollButtonState(false, gameManager.CurrentRoll, maxRollsPerTurn); dicePanelView.SetDiceInteractable(false); scoreCardView.SetAllInteractable(false); gameManager.Roll(); } private void HandleDiceToggled(int index) { if (gameManager.CurrentRoll == 0) return; if (diceManager.IsAnyRolling) return; gameManager.ToggleDiceLock(index); bool isLocked = diceManager.IsLocked(index); dicePanelView.SetDieLocked(index, isLocked); } private void HandleCategorySelected(CategoryDefinitionSO category) { if (!gameManager.CanScore) return; if (scoringSystem.IsCategoryUsed(category)) return; gameManager.ScoreInCategory(category); } private void HandleNewGameClicked() { gameInfoView.HideGameOver(); scoreCardView.ResetAll(); dicePanelView.ResetForNewGame(); gameManager.StartNewGame(); } private void HandleShopClicked() { if (shopController != null) { var shopView = shopController.GetComponentInChildren(true); if (shopView != null) { if (shopView.IsVisible) shopView.Hide(); else shopView.Show(); } } } private void HandleInventoryClicked() { if (inventoryController != null) { var inventoryView = inventoryController.GetComponentInChildren(true); if (inventoryView != null) { if (inventoryView.IsVisible) inventoryView.Hide(); else inventoryView.Show(); } } } private void HandleCurrencyChanged(int newBalance) { gameInfoView.SetCurrencyText(newBalance); PerformSave(); } private void HandleInventoryChangedForSave() { PerformSave(); } // ── Helpers ──────────────────────────────────────────────── private void UpdatePreviewScores() { var dice = diceManager.GetDice(); var previews = new Dictionary(); var allCategories = categoryCatalog.All; for (int i = 0; i < allCategories.Count; i++) { var cat = allCategories[i]; if (scoringSystem.IsCategoryUsed(cat)) continue; ScoreResult result = scoringSystem.PreviewScore(dice, cat); previews[cat] = result.FinalScore; } scoreCardView.UpdatePreviews(previews); } private void UpdateTotalDisplay() { int upperSum = CalculateUpperSum(); bool hasBonus = upperSum >= UpperBonusThreshold; int displayTotal = CalculateDisplayTotal(); scoreCardView.UpdateTotalDisplay(displayTotal, upperSum, hasBonus); } private int CalculateUpperSum() { int upperSum = 0; var allCategories = categoryCatalog.All; for (int i = 0; i < allCategories.Count; i++) { if (!allCategories[i].IsUpperSection) continue; int catScore = scoringSystem.GetCategoryScore(allCategories[i]); if (catScore >= 0) upperSum += catScore; } return upperSum; } private int CalculateDisplayTotal() { int total = scoringSystem.TotalScore; int upperSum = CalculateUpperSum(); if (upperSum >= UpperBonusThreshold) total += UpperBonusValue; return total; } } }