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.Player; using YachtDice.Modifiers.Definition; using YachtDice.Modifiers.Runtime; namespace YachtDice.UI { public class GameController : MonoBehaviour { [Header("Views")] [SerializeField] private ScoreCardView scoreCardView; [SerializeField] private DicePanelView dicePanelView; [SerializeField] private GameInfoView gameInfoView; [Header("Settings")] [SerializeField] private int maxRollsPerTurn = 3; private const int UpperBonusThreshold = 63; private const int UpperBonusValue = 35; private GameManager _gameManager; private ScoringSystem _scoringSystem; private DiceManager _diceManager; private CurrencyBank _currencyBank; private ShopController _shopController; private InventoryController _inventoryController; private ModifierRegistry _modifierRegistry; private CategoryCatalog _categoryCatalog; private ModifierCatalog _modifierCatalog; private DiceCatalog _diceCatalog; private ShopModel _shopModel; private PlayerModel _playerModel; [Inject] public void Construct( GameManager gameManager, ScoringSystem scoringSystem, DiceManager diceManager, CurrencyBank currencyBank, ShopController shopController, InventoryController inventoryController, ModifierRegistry modifierRegistry, CategoryCatalog categoryCatalog, ModifierCatalog modifierCatalog, DiceCatalog diceCatalog, ShopModel shopModel, PlayerModel playerModel) { this._gameManager = gameManager; this._scoringSystem = scoringSystem; this._diceManager = diceManager; this._currencyBank = currencyBank; this._shopController = shopController; this._inventoryController = inventoryController; this._modifierRegistry = modifierRegistry; this._categoryCatalog = categoryCatalog; this._modifierCatalog = modifierCatalog; this._diceCatalog = diceCatalog; this._shopModel = shopModel; this._playerModel = playerModel; } // ── Lifecycle ────────────────────────────────────────────── private void Start() { // Model → Controller _gameManager.OnTurnStarted += HandleTurnStarted; _gameManager.OnRollComplete += HandleRollComplete; _gameManager.OnScored += HandleScored; _gameManager.OnGameOver += HandleGameOver; _diceManager.OnDiceSettled += HandleDiceSettled; // View → Controller scoreCardView.OnCategorySelected += HandleCategorySelected; dicePanelView.OnRollClicked += HandleRollClicked; dicePanelView.OnDiceToggled += HandleDiceToggled; gameInfoView.OnNewGameClicked += HandleNewGameClicked; gameInfoView.OnShopClicked += HandleShopClicked; gameInfoView.OnInventoryClicked += HandleInventoryClicked; // Currency & Player state _currencyBank.OnBalanceChanged += HandleCurrencyChanged; _playerModel.OnChanged += HandlePlayerChangedForSave; // Initialize scoreCardView.Initialize(_categoryCatalog); LoadSaveData(); gameInfoView.SetCurrencyText(_currencyBank.Balance); // Start the game after all subscriptions are in place _gameManager.StartNewGame(); } private void OnDestroy() { _gameManager.OnTurnStarted -= HandleTurnStarted; _gameManager.OnRollComplete -= HandleRollComplete; _gameManager.OnScored -= HandleScored; _gameManager.OnGameOver -= HandleGameOver; _diceManager.OnDiceSettled -= HandleDiceSettled; scoreCardView.OnCategorySelected -= HandleCategorySelected; dicePanelView.OnRollClicked -= HandleRollClicked; dicePanelView.OnDiceToggled -= HandleDiceToggled; gameInfoView.OnNewGameClicked -= HandleNewGameClicked; gameInfoView.OnShopClicked -= HandleShopClicked; gameInfoView.OnInventoryClicked -= HandleInventoryClicked; _currencyBank.OnBalanceChanged -= HandleCurrencyChanged; if (_playerModel != null) _playerModel.OnChanged -= HandlePlayerChangedForSave; } // ── Save / Load ───────────────────────────────────────── private void LoadSaveData() { SaveData save = SaveSystem.Load(); if (save.currency > 0) _currencyBank.SetBalance(save.currency); if (_modifierCatalog != null && save.ownedModifiers.Count > 0) { var entries = new List(); var permanentIds = new HashSet(); foreach (var oldEntry in save.ownedModifiers) { var def = _modifierCatalog.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, _modifierCatalog); _shopModel.LoadPurchasedPermanentIds(permanentIds); } if (_diceCatalog != null && save.ownedDiceIds != null && save.ownedDiceIds.Count > 0) { _playerModel.Dice.LoadSaveData(save.ownedDiceIds, _diceCatalog); var dicePermIds = new HashSet(save.ownedDiceIds); var existingIds = _shopModel.GetPurchasedPermanentIds(); foreach (var id in dicePermIds) existingIds.Add(id); _shopModel.LoadPurchasedPermanentIds(existingIds); } } private void PerformSave() { var save = new SaveData { currency = _currencyBank.Balance, ownedDiceIds = _playerModel.Dice.GetSaveData(), }; 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 HandleDiceSettled(int index, int value) { dicePanelView.SetDiceValue(index, value); } private void HandleScored(CategoryDefinition 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.SetDiceLocked(index, isLocked); } private void HandleCategorySelected(CategoryDefinition 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() { _shopController.ToggleVisibility(); } private void HandleInventoryClicked() { _inventoryController.ToggleVisibility(); } private void HandleCurrencyChanged(int newBalance) { gameInfoView.SetCurrencyText(newBalance); } private void HandlePlayerChangedForSave() { 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; } } }