[Refactor] Fix GameController

This commit is contained in:
2026-03-04 06:48:21 +07:00
parent 244f635062
commit 3031d2e4c2
24 changed files with 708 additions and 303 deletions
+5
View File
@@ -14,6 +14,7 @@ using YachtDice.Player;
using YachtDice.Scoring; using YachtDice.Scoring;
using YachtDice.Shop; using YachtDice.Shop;
using YachtDice.UI; using YachtDice.UI;
using YachtDice.UI.Presentation;
namespace YachtDice.DI namespace YachtDice.DI
{ {
@@ -61,6 +62,10 @@ namespace YachtDice.DI
// Shop // Shop
builder.Register<ShopModel>(Lifetime.Singleton); builder.Register<ShopModel>(Lifetime.Singleton);
// Presentation services
builder.Register<IGameSaveService, GameSaveService>(Lifetime.Singleton);
builder.Register<IScoreSummaryService, ScoreSummaryService>(Lifetime.Singleton);
// Scene MonoBehaviour components // Scene MonoBehaviour components
builder.RegisterComponent(scoringSystem); builder.RegisterComponent(scoringSystem);
builder.RegisterComponent(currencyBank); builder.RegisterComponent(currencyBank);
+1
View File
@@ -16,6 +16,7 @@ namespace YachtDice.Game
public int CurrentRoll { get; private set; } public int CurrentRoll { get; private set; }
public int CurrentTurn { get; private set; } public int CurrentTurn { get; private set; }
public int MaxRollsPerTurn => maxRollsPerTurn;
public bool CanRoll => CurrentRoll < maxRollsPerTurn && !_diceManager.IsAnyRolling; public bool CanRoll => CurrentRoll < maxRollsPerTurn && !_diceManager.IsAnyRolling;
public bool CanScore => CurrentRoll > 0 && !_diceManager.IsAnyRolling; public bool CanScore => CurrentRoll > 0 && !_diceManager.IsAnyRolling;
+2 -2
View File
@@ -87,10 +87,10 @@ namespace YachtDice.UI
} }
} }
public void ResetForNewGame() public void ResetForNewGame(int maxRolls = 3)
{ {
ResetForNewTurn(); ResetForNewTurn();
SetRollButtonState(true, 0, 3); SetRollButtonState(true, 0, maxRolls);
} }
private void OnDestroy() private void OnDestroy()
+46 -300
View File
@@ -1,45 +1,39 @@
using System.Collections.Generic; using System;
using UnityEngine; using UnityEngine;
using VContainer; using VContainer;
using YachtDice.Categories; using YachtDice.Categories;
using YachtDice.Dice;
using YachtDice.Game;
using YachtDice.Scoring;
using YachtDice.Economy; using YachtDice.Economy;
using YachtDice.Shop; using YachtDice.Game;
using YachtDice.Inventory; using YachtDice.Inventory;
using YachtDice.Persistence;
using YachtDice.Player; using YachtDice.Player;
using YachtDice.Modifiers.Definition; using YachtDice.Scoring;
using YachtDice.Modifiers.Runtime; using YachtDice.Shop;
using YachtDice.UI.Presentation;
namespace YachtDice.UI namespace YachtDice.UI
{ {
public class GameController : MonoBehaviour public class GameController : MonoBehaviour
{ {
[Header("Views")] [Header("MVP Views")]
[SerializeField] private ScoreCardView scoreCardView; [SerializeField] private ScoreCardView scoreCardView;
[SerializeField] private DicePanelView dicePanelView; [SerializeField] private DicePanelView dicePanelView;
[SerializeField] private GameInfoView gameInfoView; [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 GameManager _gameManager;
private ScoringSystem _scoringSystem; private ScoringSystem _scoringSystem;
private DiceManager _diceManager; private DiceManager _diceManager;
private CurrencyBank _currencyBank; private CurrencyBank _currencyBank;
private ShopController _shopController; private ShopController _shopController;
private InventoryController _inventoryController; private InventoryController _inventoryController;
private ModifierRegistry _modifierRegistry;
private CategoryCatalog _categoryCatalog; private CategoryCatalog _categoryCatalog;
private ModifierCatalog _modifierCatalog;
private DiceCatalog _diceCatalog;
private ShopModel _shopModel;
private PlayerModel _playerModel; private PlayerModel _playerModel;
private IGameSaveService _saveService;
private IScoreSummaryService _scoreSummaryService;
private DicePanelPresenter _dicePanelPresenter;
private ScoreCardPresenter _scoreCardPresenter;
private GameInfoPresenter _gameInfoPresenter;
private GameFlowPresenter _gameFlowPresenter;
[Inject] [Inject]
public void Construct( public void Construct(
@@ -49,12 +43,10 @@ namespace YachtDice.UI
CurrencyBank currencyBank, CurrencyBank currencyBank,
ShopController shopController, ShopController shopController,
InventoryController inventoryController, InventoryController inventoryController,
ModifierRegistry modifierRegistry,
CategoryCatalog categoryCatalog, CategoryCatalog categoryCatalog,
ModifierCatalog modifierCatalog, PlayerModel playerModel,
DiceCatalog diceCatalog, IGameSaveService saveService,
ShopModel shopModel, IScoreSummaryService scoreSummaryService)
PlayerModel playerModel)
{ {
this._gameManager = gameManager; this._gameManager = gameManager;
this._scoringSystem = scoringSystem; this._scoringSystem = scoringSystem;
@@ -62,297 +54,51 @@ namespace YachtDice.UI
this._currencyBank = currencyBank; this._currencyBank = currencyBank;
this._shopController = shopController; this._shopController = shopController;
this._inventoryController = inventoryController; this._inventoryController = inventoryController;
this._modifierRegistry = modifierRegistry;
this._categoryCatalog = categoryCatalog; this._categoryCatalog = categoryCatalog;
this._modifierCatalog = modifierCatalog;
this._diceCatalog = diceCatalog;
this._shopModel = shopModel;
this._playerModel = playerModel; this._playerModel = playerModel;
this._saveService = saveService;
this._scoreSummaryService = scoreSummaryService;
} }
// ── Lifecycle ──────────────────────────────────────────────
private void Start() private void Start()
{ {
// Model → Controller _dicePanelPresenter = new DicePanelPresenter(dicePanelView, _gameManager, _diceManager);
_gameManager.OnTurnStarted += HandleTurnStarted; _scoreCardPresenter = new ScoreCardPresenter(scoreCardView, _categoryCatalog, _scoringSystem, _diceManager);
_gameManager.OnRollComplete += HandleRollComplete; _gameInfoPresenter = new GameInfoPresenter(gameInfoView);
_gameManager.OnScored += HandleScored;
_gameManager.OnGameOver += HandleGameOver;
_diceManager.OnDiceSettled += HandleDiceSettled;
// View → Controller _gameFlowPresenter = new GameFlowPresenter(
scoreCardView.OnCategorySelected += HandleCategorySelected; _gameManager,
dicePanelView.OnRollClicked += HandleRollClicked; _scoringSystem,
dicePanelView.OnDiceToggled += HandleDiceToggled; _diceManager,
gameInfoView.OnNewGameClicked += HandleNewGameClicked; _currencyBank,
gameInfoView.OnShopClicked += HandleShopClicked; _shopController,
gameInfoView.OnInventoryClicked += HandleInventoryClicked; _inventoryController,
_categoryCatalog,
_playerModel,
_saveService,
_scoreSummaryService,
_dicePanelPresenter,
_scoreCardPresenter,
_gameInfoPresenter);
// Currency & Player state _dicePanelPresenter.Initialize();
_currencyBank.OnBalanceChanged += HandleCurrencyChanged; _scoreCardPresenter.Initialize();
_playerModel.OnChanged += HandlePlayerChangedForSave; _gameInfoPresenter.Initialize();
_gameFlowPresenter.Initialize();
// Initialize
scoreCardView.Initialize(_categoryCatalog);
LoadSaveData();
gameInfoView.SetCurrencyText(_currencyBank.Balance);
// Start the game after all subscriptions are in place
_gameManager.StartNewGame();
} }
private void OnDestroy() private void OnDestroy()
{ {
_gameManager.OnTurnStarted -= HandleTurnStarted; DisposeIfNeeded(_gameFlowPresenter);
_gameManager.OnRollComplete -= HandleRollComplete; DisposeIfNeeded(_gameInfoPresenter);
_gameManager.OnScored -= HandleScored; DisposeIfNeeded(_scoreCardPresenter);
_gameManager.OnGameOver -= HandleGameOver; DisposeIfNeeded(_dicePanelPresenter);
_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 static void DisposeIfNeeded(IDisposable disposable)
private void LoadSaveData()
{ {
SaveData save = SaveSystem.Load(); if (disposable != null)
disposable.Dispose();
if (save.currency > 0)
_currencyBank.SetBalance(save.currency);
if (_modifierCatalog != null && save.ownedModifiers.Count > 0)
{
var entries = new List<ModifierSaveEntry>();
var permanentIds = new HashSet<string>();
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<string>(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<CategoryDefinition, int>();
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;
} }
} }
} }
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b0077025ca5d884e9709f0d8a3c77e0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -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<int> 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);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f201ff4903d62894bb6245d78d5fa3f9
@@ -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();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 88b64c71e3be57d4c84a553cc2bd3bf3
@@ -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();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6bc663d9adf9e9e4a921541dd9cf307d
@@ -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<ModifierSaveEntry> savedModifiers)
{
var validEntries = new List<ModifierSaveEntry>();
var permanentIds = new HashSet<string>();
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<string> ownedDiceIds)
{
_playerModel.Dice.LoadSaveData(ownedDiceIds, _diceCatalog);
var dicePermanentIds = new HashSet<string>(ownedDiceIds);
var existingPermanentIds = _shopModel.GetPurchasedPermanentIds();
foreach (var id in dicePermanentIds)
existingPermanentIds.Add(id);
_shopModel.LoadPurchasedPermanentIds(existingPermanentIds);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2bed20289c2dd584ca7c6d6803c5f5ae
@@ -0,0 +1,8 @@
namespace YachtDice.UI.Presentation
{
public interface IGameSaveService
{
void Load();
void Save();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 80cd4768a27c8c842b94b00255c9ea18
@@ -0,0 +1,7 @@
namespace YachtDice.UI.Presentation
{
public interface IScoreSummaryService
{
ScoreSummary Calculate();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cfa9b05828486bc419ec4ea51ef6ad28
@@ -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<CategoryDefinition> 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<CategoryDefinition, int>();
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);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5c6cef29e168ca744b536c5f71a5c110
@@ -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;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: da4516c0d195c1e4cb0405efa3e731fd
@@ -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;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fadb6232a88156d4a8f4373758fd160f
+1 -1
View File
@@ -23,7 +23,7 @@ namespace YachtDice.UI
/// <summary> /// <summary>
/// Инициализирует скоркарту из каталога категорий. /// Инициализирует скоркарту из каталога категорий.
/// Вызывается из GameController после DI. /// Вызывается из презентационного слоя после DI.
/// </summary> /// </summary>
public void Initialize(CategoryCatalog categoryCatalog) public void Initialize(CategoryCatalog categoryCatalog)
{ {