using System; using System.Collections.Generic; using UnityEngine; using YachtDice.Game; using YachtDice.Scoring; using YachtDice.Economy; using YachtDice.Shop; using YachtDice.Inventory; using YachtDice.Persistence; using YachtDice.Modifiers; namespace YachtDice.UI { public sealed 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 static readonly YachtCategory[] UpperCategories = { YachtCategory.Ones, YachtCategory.Twos, YachtCategory.Threes, YachtCategory.Fours, YachtCategory.Fives, YachtCategory.Sixes }; private const int UpperBonusThreshold = 63; private const int UpperBonusValue = 35; private int totalCategoryCount; private InventoryModel inventoryModel; private ShopModel shopModel; // ── Lifecycle ────────────────────────────────────────────── private void Awake() { totalCategoryCount = Enum.GetValues(typeof(YachtCategory)).Length; // 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; 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 (inventoryModel != null) inventoryModel.OnInventoryChanged -= HandleInventoryChangedForSave; } // ── Modifier System Init ───────────────────────────────── private void InitializeModifierSystems() { inventoryModel = new InventoryModel(maxActiveModifierSlots); inventoryModel.OnInventoryChanged += HandleInventoryChangedForSave; ShopCatalog catalog = shopController != null ? shopController.Catalog : null; shopModel = new ShopModel(currencyBank, inventoryModel); LoadSaveData(catalog); if (inventoryController != null) inventoryController.Initialize(inventoryModel); if (shopController != null) shopController.Initialize(shopModel); if (currencyBank != null) gameInfoView.SetCurrencyText(currencyBank.Balance); } private void LoadSaveData(ShopCatalog catalog) { SaveData save = SaveSystem.Load(); if (currencyBank != null && save.Currency > 0) currencyBank.SetBalance(save.Currency); if (catalog != null && save.OwnedModifiers.Count > 0) { var runtimeList = new List(); var permanentIds = new HashSet(); for (int i = 0; i < save.OwnedModifiers.Count; i++) { var entry = save.OwnedModifiers[i]; ModifierData data = catalog.FindById(entry.ModifierId); if (data == null) { Debug.LogWarning($"Modifier '{entry.ModifierId}' not found in catalog, skipping."); continue; } var runtime = new ModifierRuntime { ModifierId = entry.ModifierId, IsActive = entry.IsActive, RemainingUses = entry.RemainingUses, Data = data }; runtimeList.Add(runtime); if (data.Durability == ModifierDurability.Permanent) permanentIds.Add(data.Id); } inventoryModel.LoadState(runtimeList); shopModel.LoadPurchasedPermanentIds(permanentIds); } } private void PerformSave() { var save = new SaveData { Currency = currencyBank != null ? currencyBank.Balance : 0 }; var owned = inventoryModel.GetAllForSave(); for (int i = 0; i < owned.Count; i++) { save.OwnedModifiers.Add(new ModifierSaveEntry { ModifierId = owned[i].ModifierId, IsActive = owned[i].IsActive, RemainingUses = owned[i].RemainingUses }); } SaveSystem.Save(save); } // ── Model Event Handlers ────────────────────────────────── private void HandleTurnStarted(int turn) { 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(values); } private void HandleDieSettled(int index, int value) { dicePanelView.SetDieValue(index, value); } private void HandleScored(YachtCategory 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(YachtCategory 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(int[] diceValues) { var previews = new Dictionary(); var categories = (YachtCategory[])Enum.GetValues(typeof(YachtCategory)); for (int i = 0; i < categories.Length; i++) { if (scoringSystem.IsCategoryUsed(categories[i])) continue; ScoreResult result = scoringSystem.PreviewScore(diceValues, categories[i]); previews[categories[i]] = result.FinalScore; } scoreCardView.UpdatePreviews(previews); } private void UpdateTotalDisplay() { int upperSum = 0; for (int i = 0; i < UpperCategories.Length; i++) { int catScore = scoringSystem.GetCategoryScore(UpperCategories[i]); if (catScore >= 0) upperSum += catScore; } bool hasBonus = upperSum >= UpperBonusThreshold; int displayTotal = CalculateDisplayTotal(); scoreCardView.UpdateTotalDisplay(displayTotal, upperSum, hasBonus); } private int CalculateDisplayTotal() { int total = scoringSystem.TotalScore; int upperSum = 0; for (int i = 0; i < UpperCategories.Length; i++) { int catScore = scoringSystem.GetCategoryScore(UpperCategories[i]); if (catScore >= 0) upperSum += catScore; } if (upperSum >= UpperBonusThreshold) total += UpperBonusValue; return total; } } }