diff --git a/Assets/Scripts/DI/GameLifetimeScope.cs b/Assets/Scripts/DI/GameLifetimeScope.cs index 62d449f..92351ab 100644 --- a/Assets/Scripts/DI/GameLifetimeScope.cs +++ b/Assets/Scripts/DI/GameLifetimeScope.cs @@ -14,6 +14,7 @@ using YachtDice.Player; using YachtDice.Scoring; using YachtDice.Shop; using YachtDice.UI; +using YachtDice.UI.Presentation; namespace YachtDice.DI { @@ -61,6 +62,10 @@ namespace YachtDice.DI // Shop builder.Register(Lifetime.Singleton); + // Presentation services + builder.Register(Lifetime.Singleton); + builder.Register(Lifetime.Singleton); + // Scene MonoBehaviour components builder.RegisterComponent(scoringSystem); builder.RegisterComponent(currencyBank); diff --git a/Assets/Scripts/Game/GameManager.cs b/Assets/Scripts/Game/GameManager.cs index a9c6155..f5afacb 100644 --- a/Assets/Scripts/Game/GameManager.cs +++ b/Assets/Scripts/Game/GameManager.cs @@ -16,6 +16,7 @@ namespace YachtDice.Game public int CurrentRoll { get; private set; } public int CurrentTurn { get; private set; } + public int MaxRollsPerTurn => maxRollsPerTurn; public bool CanRoll => CurrentRoll < maxRollsPerTurn && !_diceManager.IsAnyRolling; public bool CanScore => CurrentRoll > 0 && !_diceManager.IsAnyRolling; diff --git a/Assets/Scripts/UI/DicePanelView.cs b/Assets/Scripts/UI/DicePanelView.cs index b19fb72..f6603e5 100644 --- a/Assets/Scripts/UI/DicePanelView.cs +++ b/Assets/Scripts/UI/DicePanelView.cs @@ -87,10 +87,10 @@ namespace YachtDice.UI } } - public void ResetForNewGame() + public void ResetForNewGame(int maxRolls = 3) { ResetForNewTurn(); - SetRollButtonState(true, 0, 3); + SetRollButtonState(true, 0, maxRolls); } private void OnDestroy() diff --git a/Assets/Scripts/UI/GameController.cs b/Assets/Scripts/UI/GameController.cs index b827e7d..cf74b01 100644 --- a/Assets/Scripts/UI/GameController.cs +++ b/Assets/Scripts/UI/GameController.cs @@ -1,45 +1,39 @@ -using System.Collections.Generic; +using System; using UnityEngine; using VContainer; using YachtDice.Categories; -using YachtDice.Dice; -using YachtDice.Game; -using YachtDice.Scoring; using YachtDice.Economy; -using YachtDice.Shop; +using YachtDice.Game; using YachtDice.Inventory; -using YachtDice.Persistence; using YachtDice.Player; -using YachtDice.Modifiers.Definition; -using YachtDice.Modifiers.Runtime; +using YachtDice.Scoring; +using YachtDice.Shop; +using YachtDice.UI.Presentation; namespace YachtDice.UI { public class GameController : MonoBehaviour { - [Header("Views")] + [Header("MVP 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; + private IGameSaveService _saveService; + private IScoreSummaryService _scoreSummaryService; + + private DicePanelPresenter _dicePanelPresenter; + private ScoreCardPresenter _scoreCardPresenter; + private GameInfoPresenter _gameInfoPresenter; + private GameFlowPresenter _gameFlowPresenter; [Inject] public void Construct( @@ -49,12 +43,10 @@ namespace YachtDice.UI CurrencyBank currencyBank, ShopController shopController, InventoryController inventoryController, - ModifierRegistry modifierRegistry, CategoryCatalog categoryCatalog, - ModifierCatalog modifierCatalog, - DiceCatalog diceCatalog, - ShopModel shopModel, - PlayerModel playerModel) + PlayerModel playerModel, + IGameSaveService saveService, + IScoreSummaryService scoreSummaryService) { this._gameManager = gameManager; this._scoringSystem = scoringSystem; @@ -62,297 +54,51 @@ namespace YachtDice.UI 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; + this._saveService = saveService; + this._scoreSummaryService = scoreSummaryService; } - // ── Lifecycle ────────────────────────────────────────────── - private void Start() { - // Model → Controller - _gameManager.OnTurnStarted += HandleTurnStarted; - _gameManager.OnRollComplete += HandleRollComplete; - _gameManager.OnScored += HandleScored; - _gameManager.OnGameOver += HandleGameOver; - _diceManager.OnDiceSettled += HandleDiceSettled; + _dicePanelPresenter = new DicePanelPresenter(dicePanelView, _gameManager, _diceManager); + _scoreCardPresenter = new ScoreCardPresenter(scoreCardView, _categoryCatalog, _scoringSystem, _diceManager); + _gameInfoPresenter = new GameInfoPresenter(gameInfoView); - // View → Controller - scoreCardView.OnCategorySelected += HandleCategorySelected; - dicePanelView.OnRollClicked += HandleRollClicked; - dicePanelView.OnDiceToggled += HandleDiceToggled; - gameInfoView.OnNewGameClicked += HandleNewGameClicked; - gameInfoView.OnShopClicked += HandleShopClicked; - gameInfoView.OnInventoryClicked += HandleInventoryClicked; + _gameFlowPresenter = new GameFlowPresenter( + _gameManager, + _scoringSystem, + _diceManager, + _currencyBank, + _shopController, + _inventoryController, + _categoryCatalog, + _playerModel, + _saveService, + _scoreSummaryService, + _dicePanelPresenter, + _scoreCardPresenter, + _gameInfoPresenter); - // 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(); + _dicePanelPresenter.Initialize(); + _scoreCardPresenter.Initialize(); + _gameInfoPresenter.Initialize(); + _gameFlowPresenter.Initialize(); } 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; + DisposeIfNeeded(_gameFlowPresenter); + DisposeIfNeeded(_gameInfoPresenter); + DisposeIfNeeded(_scoreCardPresenter); + DisposeIfNeeded(_dicePanelPresenter); } - // ── Save / Load ───────────────────────────────────────── - - private void LoadSaveData() + private static void DisposeIfNeeded(IDisposable disposable) { - 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; + if (disposable != null) + disposable.Dispose(); } } } diff --git a/Assets/Scripts/UI/Presentation.meta b/Assets/Scripts/UI/Presentation.meta new file mode 100644 index 0000000..66e8896 --- /dev/null +++ b/Assets/Scripts/UI/Presentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7b0077025ca5d884e9709f0d8a3c77e0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Presentation/DicePanelPresenter.cs b/Assets/Scripts/UI/Presentation/DicePanelPresenter.cs new file mode 100644 index 0000000..c35232d --- /dev/null +++ b/Assets/Scripts/UI/Presentation/DicePanelPresenter.cs @@ -0,0 +1,87 @@ +using System; +using YachtDice.Game; + +namespace YachtDice.UI.Presentation +{ + public sealed class DicePanelPresenter : IDisposable + { + private readonly DicePanelView _view; + private readonly GameManager _gameManager; + private readonly DiceManager _diceManager; + + public event Action RollClicked; + public event Action DiceToggled; + + public DicePanelPresenter(DicePanelView view, GameManager gameManager, DiceManager diceManager) + { + _view = view; + _gameManager = gameManager; + _diceManager = diceManager; + } + + public void Initialize() + { + _view.OnRollClicked += HandleRollClicked; + _view.OnDiceToggled += HandleDiceToggled; + _diceManager.OnDiceSettled += HandleDiceSettled; + } + + public void Dispose() + { + _view.OnRollClicked -= HandleRollClicked; + _view.OnDiceToggled -= HandleDiceToggled; + _diceManager.OnDiceSettled -= HandleDiceSettled; + } + + public void ResetForNewTurn() + { + _view.ResetForNewTurn(); + _view.SetRollButtonState(true, 0, _gameManager.MaxRollsPerTurn); + } + + public void PrepareForRoll() + { + _view.SetRollButtonState(false, _gameManager.CurrentRoll, _gameManager.MaxRollsPerTurn); + _view.SetDiceInteractable(false); + } + + public void HandleRollComplete(int rollNumber) + { + var canRollAgain = _gameManager.CanRoll; + _view.SetRollButtonState(canRollAgain, rollNumber, _gameManager.MaxRollsPerTurn); + _view.SetDiceInteractable(true); + _view.SetAllDiceValues(_diceManager.GetCurrentValues()); + } + + public void HandleGameOver() + { + _view.SetRollButtonState(false, _gameManager.MaxRollsPerTurn, _gameManager.MaxRollsPerTurn); + _view.SetDiceInteractable(false); + } + + public void ResetForNewGame() + { + _view.ResetForNewGame(_gameManager.MaxRollsPerTurn); + } + + public void SetDiceLocked(int index, bool isLocked) + { + _view.SetDiceLocked(index, isLocked); + } + + private void HandleRollClicked() + { + RollClicked?.Invoke(); + } + + private void HandleDiceToggled(int index) + { + DiceToggled?.Invoke(index); + } + + private void HandleDiceSettled(int index, int value) + { + _view.SetDiceValue(index, value); + } + } +} diff --git a/Assets/Scripts/UI/Presentation/DicePanelPresenter.cs.meta b/Assets/Scripts/UI/Presentation/DicePanelPresenter.cs.meta new file mode 100644 index 0000000..dcd0eb0 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/DicePanelPresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f201ff4903d62894bb6245d78d5fa3f9 \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/GameFlowPresenter.cs b/Assets/Scripts/UI/Presentation/GameFlowPresenter.cs new file mode 100644 index 0000000..456f623 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/GameFlowPresenter.cs @@ -0,0 +1,189 @@ +using System; +using YachtDice.Categories; +using YachtDice.Economy; +using YachtDice.Game; +using YachtDice.Inventory; +using YachtDice.Player; +using YachtDice.Scoring; +using YachtDice.Shop; + +namespace YachtDice.UI.Presentation +{ + public sealed class GameFlowPresenter : IDisposable + { + private readonly GameManager _gameManager; + private readonly ScoringSystem _scoringSystem; + private readonly DiceManager _diceManager; + private readonly CurrencyBank _currencyBank; + private readonly ShopController _shopController; + private readonly InventoryController _inventoryController; + private readonly CategoryCatalog _categoryCatalog; + private readonly PlayerModel _playerModel; + private readonly IGameSaveService _saveService; + private readonly IScoreSummaryService _scoreSummaryService; + private readonly DicePanelPresenter _dicePanelPresenter; + private readonly ScoreCardPresenter _scoreCardPresenter; + private readonly GameInfoPresenter _gameInfoPresenter; + + public GameFlowPresenter( + GameManager gameManager, + ScoringSystem scoringSystem, + DiceManager diceManager, + CurrencyBank currencyBank, + ShopController shopController, + InventoryController inventoryController, + CategoryCatalog categoryCatalog, + PlayerModel playerModel, + IGameSaveService saveService, + IScoreSummaryService scoreSummaryService, + DicePanelPresenter dicePanelPresenter, + ScoreCardPresenter scoreCardPresenter, + GameInfoPresenter gameInfoPresenter) + { + _gameManager = gameManager; + _scoringSystem = scoringSystem; + _diceManager = diceManager; + _currencyBank = currencyBank; + _shopController = shopController; + _inventoryController = inventoryController; + _categoryCatalog = categoryCatalog; + _playerModel = playerModel; + _saveService = saveService; + _scoreSummaryService = scoreSummaryService; + _dicePanelPresenter = dicePanelPresenter; + _scoreCardPresenter = scoreCardPresenter; + _gameInfoPresenter = gameInfoPresenter; + } + + public void Initialize() + { + _gameManager.OnTurnStarted += HandleTurnStarted; + _gameManager.OnRollComplete += HandleRollComplete; + _gameManager.OnScored += HandleScored; + _gameManager.OnGameOver += HandleGameOver; + + _dicePanelPresenter.RollClicked += HandleRollClicked; + _dicePanelPresenter.DiceToggled += HandleDiceToggled; + _scoreCardPresenter.CategorySelected += HandleCategorySelected; + _gameInfoPresenter.NewGameClicked += HandleNewGameClicked; + _gameInfoPresenter.ShopClicked += HandleShopClicked; + _gameInfoPresenter.InventoryClicked += HandleInventoryClicked; + + _currencyBank.OnBalanceChanged += HandleCurrencyChanged; + _playerModel.OnChanged += HandlePlayerChangedForSave; + + _saveService.Load(); + _gameInfoPresenter.SetCurrencyText(_currencyBank.Balance); + _gameManager.StartNewGame(); + } + + public void Dispose() + { + _gameManager.OnTurnStarted -= HandleTurnStarted; + _gameManager.OnRollComplete -= HandleRollComplete; + _gameManager.OnScored -= HandleScored; + _gameManager.OnGameOver -= HandleGameOver; + + _dicePanelPresenter.RollClicked -= HandleRollClicked; + _dicePanelPresenter.DiceToggled -= HandleDiceToggled; + _scoreCardPresenter.CategorySelected -= HandleCategorySelected; + _gameInfoPresenter.NewGameClicked -= HandleNewGameClicked; + _gameInfoPresenter.ShopClicked -= HandleShopClicked; + _gameInfoPresenter.InventoryClicked -= HandleInventoryClicked; + + _currencyBank.OnBalanceChanged -= HandleCurrencyChanged; + + if (_playerModel != null) + _playerModel.OnChanged -= HandlePlayerChangedForSave; + } + + private void HandleTurnStarted(int turn) + { + _gameInfoPresenter.SetTurnText(turn, _categoryCatalog.Count); + _dicePanelPresenter.ResetForNewTurn(); + _scoreCardPresenter.ClearAllPreviews(); + } + + private void HandleRollComplete(int rollNumber) + { + _dicePanelPresenter.HandleRollComplete(rollNumber); + _scoreCardPresenter.UpdatePreviewScores(); + } + + private void HandleScored(CategoryDefinition category, int finalScore) + { + _scoreCardPresenter.SetCategoryScored(category, finalScore); + _scoreCardPresenter.UpdateTotalDisplay(_scoreSummaryService.Calculate()); + _saveService.Save(); + } + + private void HandleGameOver(int totalScore) + { + _dicePanelPresenter.HandleGameOver(); + _scoreCardPresenter.SetAllInteractable(false); + _gameInfoPresenter.ShowGameOver(_scoreSummaryService.Calculate().DisplayTotal); + _saveService.Save(); + } + + private void HandleRollClicked() + { + if (!_gameManager.CanRoll) + return; + + _dicePanelPresenter.PrepareForRoll(); + _scoreCardPresenter.SetAllInteractable(false); + _gameManager.Roll(); + } + + private void HandleDiceToggled(int index) + { + if (_gameManager.CurrentRoll == 0) + return; + + if (_diceManager.IsAnyRolling) + return; + + _gameManager.ToggleDiceLock(index); + _dicePanelPresenter.SetDiceLocked(index, _diceManager.IsLocked(index)); + } + + private void HandleCategorySelected(CategoryDefinition category) + { + if (!_gameManager.CanScore) + return; + + if (_scoringSystem.IsCategoryUsed(category)) + return; + + _gameManager.ScoreInCategory(category); + } + + private void HandleNewGameClicked() + { + _gameInfoPresenter.HideGameOver(); + _scoreCardPresenter.ResetAll(); + _dicePanelPresenter.ResetForNewGame(); + _gameManager.StartNewGame(); + } + + private void HandleShopClicked() + { + _shopController.ToggleVisibility(); + } + + private void HandleInventoryClicked() + { + _inventoryController.ToggleVisibility(); + } + + private void HandleCurrencyChanged(int newBalance) + { + _gameInfoPresenter.SetCurrencyText(newBalance); + } + + private void HandlePlayerChangedForSave() + { + _saveService.Save(); + } + } +} diff --git a/Assets/Scripts/UI/Presentation/GameFlowPresenter.cs.meta b/Assets/Scripts/UI/Presentation/GameFlowPresenter.cs.meta new file mode 100644 index 0000000..0eae463 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/GameFlowPresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 88b64c71e3be57d4c84a553cc2bd3bf3 \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/GameInfoPresenter.cs b/Assets/Scripts/UI/Presentation/GameInfoPresenter.cs new file mode 100644 index 0000000..5bb4ba7 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/GameInfoPresenter.cs @@ -0,0 +1,67 @@ +using System; + +namespace YachtDice.UI.Presentation +{ + public sealed class GameInfoPresenter : IDisposable + { + private readonly GameInfoView _view; + + public event Action NewGameClicked; + public event Action ShopClicked; + public event Action InventoryClicked; + + public GameInfoPresenter(GameInfoView view) + { + _view = view; + } + + public void Initialize() + { + _view.OnNewGameClicked += HandleNewGameClicked; + _view.OnShopClicked += HandleShopClicked; + _view.OnInventoryClicked += HandleInventoryClicked; + } + + public void Dispose() + { + _view.OnNewGameClicked -= HandleNewGameClicked; + _view.OnShopClicked -= HandleShopClicked; + _view.OnInventoryClicked -= HandleInventoryClicked; + } + + public void SetTurnText(int turn, int maxTurns) + { + _view.SetTurnText(turn, maxTurns); + } + + public void SetCurrencyText(int amount) + { + _view.SetCurrencyText(amount); + } + + public void ShowGameOver(int finalScore) + { + _view.ShowGameOver(finalScore); + } + + public void HideGameOver() + { + _view.HideGameOver(); + } + + private void HandleNewGameClicked() + { + NewGameClicked?.Invoke(); + } + + private void HandleShopClicked() + { + ShopClicked?.Invoke(); + } + + private void HandleInventoryClicked() + { + InventoryClicked?.Invoke(); + } + } +} diff --git a/Assets/Scripts/UI/Presentation/GameInfoPresenter.cs.meta b/Assets/Scripts/UI/Presentation/GameInfoPresenter.cs.meta new file mode 100644 index 0000000..ae164ac --- /dev/null +++ b/Assets/Scripts/UI/Presentation/GameInfoPresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6bc663d9adf9e9e4a921541dd9cf307d \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/GameSaveService.cs b/Assets/Scripts/UI/Presentation/GameSaveService.cs new file mode 100644 index 0000000..aac4448 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/GameSaveService.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using UnityEngine; +using YachtDice.Dice; +using YachtDice.Economy; +using YachtDice.Modifiers.Definition; +using YachtDice.Modifiers.Runtime; +using YachtDice.Persistence; +using YachtDice.Player; +using YachtDice.Shop; + +namespace YachtDice.UI.Presentation +{ + public sealed class GameSaveService : IGameSaveService + { + private readonly CurrencyBank _currencyBank; + private readonly ModifierRegistry _modifierRegistry; + private readonly ModifierCatalog _modifierCatalog; + private readonly DiceCatalog _diceCatalog; + private readonly ShopModel _shopModel; + private readonly PlayerModel _playerModel; + + public GameSaveService( + CurrencyBank currencyBank, + ModifierRegistry modifierRegistry, + ModifierCatalog modifierCatalog, + DiceCatalog diceCatalog, + ShopModel shopModel, + PlayerModel playerModel) + { + _currencyBank = currencyBank; + _modifierRegistry = modifierRegistry; + _modifierCatalog = modifierCatalog; + _diceCatalog = diceCatalog; + _shopModel = shopModel; + _playerModel = playerModel; + } + + public void Load() + { + var save = SaveSystem.Load(); + + if (save.currency > 0) + _currencyBank.SetBalance(save.currency); + + if (_modifierCatalog != null && save.ownedModifiers != null && save.ownedModifiers.Count > 0) + LoadModifiers(save.ownedModifiers); + + if (_diceCatalog != null && save.ownedDiceIds != null && save.ownedDiceIds.Count > 0) + LoadDice(save.ownedDiceIds); + } + + public void Save() + { + var save = new SaveData + { + currency = _currencyBank.Balance, + ownedDiceIds = _playerModel.Dice.GetSaveData(), + }; + + var entries = _modifierRegistry.GetSaveData(); + for (var i = 0; i < entries.Count; i++) + save.ownedModifiers.Add(entries[i]); + + SaveSystem.Save(save); + } + + private void LoadModifiers(List savedModifiers) + { + var validEntries = new List(); + var permanentIds = new HashSet(); + + for (var i = 0; i < savedModifiers.Count; i++) + { + var entry = savedModifiers[i]; + var definition = _modifierCatalog.FindById(entry.modifierId); + + if (definition == null) + { + Debug.LogWarning($"Modifier '{entry.modifierId}' not found in catalog, skipping."); + continue; + } + + validEntries.Add(new ModifierSaveEntry + { + modifierId = entry.modifierId, + isActive = entry.isActive, + remainingUses = entry.remainingUses, + stacks = entry.stacks, + customState = entry.customState, + }); + + if (!definition.HasLimitedUses) + permanentIds.Add(definition.Id); + } + + _modifierRegistry.LoadSaveData(validEntries, _modifierCatalog); + _shopModel.LoadPurchasedPermanentIds(permanentIds); + } + + private void LoadDice(List ownedDiceIds) + { + _playerModel.Dice.LoadSaveData(ownedDiceIds, _diceCatalog); + + var dicePermanentIds = new HashSet(ownedDiceIds); + var existingPermanentIds = _shopModel.GetPurchasedPermanentIds(); + foreach (var id in dicePermanentIds) + existingPermanentIds.Add(id); + + _shopModel.LoadPurchasedPermanentIds(existingPermanentIds); + } + } +} diff --git a/Assets/Scripts/UI/Presentation/GameSaveService.cs.meta b/Assets/Scripts/UI/Presentation/GameSaveService.cs.meta new file mode 100644 index 0000000..d59f7fc --- /dev/null +++ b/Assets/Scripts/UI/Presentation/GameSaveService.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2bed20289c2dd584ca7c6d6803c5f5ae \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/IGameSaveService.cs b/Assets/Scripts/UI/Presentation/IGameSaveService.cs new file mode 100644 index 0000000..f7b0775 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/IGameSaveService.cs @@ -0,0 +1,8 @@ +namespace YachtDice.UI.Presentation +{ + public interface IGameSaveService + { + void Load(); + void Save(); + } +} diff --git a/Assets/Scripts/UI/Presentation/IGameSaveService.cs.meta b/Assets/Scripts/UI/Presentation/IGameSaveService.cs.meta new file mode 100644 index 0000000..1acce06 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/IGameSaveService.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 80cd4768a27c8c842b94b00255c9ea18 \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/IScoreSummaryService.cs b/Assets/Scripts/UI/Presentation/IScoreSummaryService.cs new file mode 100644 index 0000000..7e87d7a --- /dev/null +++ b/Assets/Scripts/UI/Presentation/IScoreSummaryService.cs @@ -0,0 +1,7 @@ +namespace YachtDice.UI.Presentation +{ + public interface IScoreSummaryService + { + ScoreSummary Calculate(); + } +} diff --git a/Assets/Scripts/UI/Presentation/IScoreSummaryService.cs.meta b/Assets/Scripts/UI/Presentation/IScoreSummaryService.cs.meta new file mode 100644 index 0000000..28121cf --- /dev/null +++ b/Assets/Scripts/UI/Presentation/IScoreSummaryService.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cfa9b05828486bc419ec4ea51ef6ad28 \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/ScoreCardPresenter.cs b/Assets/Scripts/UI/Presentation/ScoreCardPresenter.cs new file mode 100644 index 0000000..f6bc4d7 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/ScoreCardPresenter.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using YachtDice.Categories; +using YachtDice.Game; +using YachtDice.Scoring; + +namespace YachtDice.UI.Presentation +{ + public sealed class ScoreCardPresenter : IDisposable + { + private readonly ScoreCardView _view; + private readonly CategoryCatalog _categoryCatalog; + private readonly ScoringSystem _scoringSystem; + private readonly DiceManager _diceManager; + + public event Action CategorySelected; + + public ScoreCardPresenter( + ScoreCardView view, + CategoryCatalog categoryCatalog, + ScoringSystem scoringSystem, + DiceManager diceManager) + { + _view = view; + _categoryCatalog = categoryCatalog; + _scoringSystem = scoringSystem; + _diceManager = diceManager; + } + + public void Initialize() + { + _view.Initialize(_categoryCatalog); + _view.OnCategorySelected += HandleCategorySelected; + } + + public void Dispose() + { + _view.OnCategorySelected -= HandleCategorySelected; + } + + public void ClearAllPreviews() + { + _view.ClearAllPreviews(); + } + + public void UpdatePreviewScores() + { + var dice = _diceManager.GetDice(); + var previews = new Dictionary(); + var allCategories = _categoryCatalog.All; + + for (var i = 0; i < allCategories.Count; i++) + { + var category = allCategories[i]; + if (_scoringSystem.IsCategoryUsed(category)) + continue; + + var result = _scoringSystem.PreviewScore(dice, category); + previews[category] = result.FinalScore; + } + + _view.UpdatePreviews(previews); + } + + public void SetCategoryScored(CategoryDefinition category, int finalScore) + { + _view.SetCategoryScored(category, finalScore); + } + + public void SetAllInteractable(bool interactable) + { + _view.SetAllInteractable(interactable); + } + + public void UpdateTotalDisplay(ScoreSummary summary) + { + _view.UpdateTotalDisplay(summary.DisplayTotal, summary.UpperSum, summary.HasUpperBonus); + } + + public void ResetAll() + { + _view.ResetAll(); + } + + private void HandleCategorySelected(CategoryDefinition category) + { + CategorySelected?.Invoke(category); + } + } +} diff --git a/Assets/Scripts/UI/Presentation/ScoreCardPresenter.cs.meta b/Assets/Scripts/UI/Presentation/ScoreCardPresenter.cs.meta new file mode 100644 index 0000000..616a5c5 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/ScoreCardPresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5c6cef29e168ca744b536c5f71a5c110 \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/ScoreSummary.cs b/Assets/Scripts/UI/Presentation/ScoreSummary.cs new file mode 100644 index 0000000..5f75dad --- /dev/null +++ b/Assets/Scripts/UI/Presentation/ScoreSummary.cs @@ -0,0 +1,16 @@ +namespace YachtDice.UI.Presentation +{ + public readonly struct ScoreSummary + { + public int DisplayTotal { get; } + public int UpperSum { get; } + public bool HasUpperBonus { get; } + + public ScoreSummary(int displayTotal, int upperSum, bool hasUpperBonus) + { + DisplayTotal = displayTotal; + UpperSum = upperSum; + HasUpperBonus = hasUpperBonus; + } + } +} diff --git a/Assets/Scripts/UI/Presentation/ScoreSummary.cs.meta b/Assets/Scripts/UI/Presentation/ScoreSummary.cs.meta new file mode 100644 index 0000000..b0a4e9a --- /dev/null +++ b/Assets/Scripts/UI/Presentation/ScoreSummary.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: da4516c0d195c1e4cb0405efa3e731fd \ No newline at end of file diff --git a/Assets/Scripts/UI/Presentation/ScoreSummaryService.cs b/Assets/Scripts/UI/Presentation/ScoreSummaryService.cs new file mode 100644 index 0000000..0f69736 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/ScoreSummaryService.cs @@ -0,0 +1,51 @@ +using YachtDice.Categories; +using YachtDice.Scoring; + +namespace YachtDice.UI.Presentation +{ + public sealed class ScoreSummaryService : IScoreSummaryService + { + private const int UpperBonusThreshold = 63; + private const int UpperBonusValue = 35; + + private readonly ScoringSystem _scoringSystem; + private readonly CategoryCatalog _categoryCatalog; + + public ScoreSummaryService(ScoringSystem scoringSystem, CategoryCatalog categoryCatalog) + { + _scoringSystem = scoringSystem; + _categoryCatalog = categoryCatalog; + } + + public ScoreSummary Calculate() + { + var upperSum = CalculateUpperSum(); + var hasUpperBonus = upperSum >= UpperBonusThreshold; + + var total = _scoringSystem.TotalScore; + if (hasUpperBonus) + total += UpperBonusValue; + + return new ScoreSummary(total, upperSum, hasUpperBonus); + } + + private int CalculateUpperSum() + { + var upperSum = 0; + var allCategories = _categoryCatalog.All; + + for (var i = 0; i < allCategories.Count; i++) + { + var category = allCategories[i]; + if (!category.IsUpperSection) + continue; + + var categoryScore = _scoringSystem.GetCategoryScore(category); + if (categoryScore >= 0) + upperSum += categoryScore; + } + + return upperSum; + } + } +} diff --git a/Assets/Scripts/UI/Presentation/ScoreSummaryService.cs.meta b/Assets/Scripts/UI/Presentation/ScoreSummaryService.cs.meta new file mode 100644 index 0000000..4bd04b2 --- /dev/null +++ b/Assets/Scripts/UI/Presentation/ScoreSummaryService.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fadb6232a88156d4a8f4373758fd160f \ No newline at end of file diff --git a/Assets/Scripts/UI/ScoreCardView.cs b/Assets/Scripts/UI/ScoreCardView.cs index 1cd18a9..6503276 100644 --- a/Assets/Scripts/UI/ScoreCardView.cs +++ b/Assets/Scripts/UI/ScoreCardView.cs @@ -23,7 +23,7 @@ namespace YachtDice.UI /// /// Инициализирует скоркарту из каталога категорий. - /// Вызывается из GameController после DI. + /// Вызывается из презентационного слоя после DI. /// public void Initialize(CategoryCatalog categoryCatalog) {