[Add] Game Loop Playable
This commit is contained in:
@@ -23,10 +23,10 @@ namespace YachtDice.Game
|
||||
public int MaxRollsPerTurn => _runLoopService != null ? _runLoopService.State.CurrentStageRollBudget : 0;
|
||||
public RunPhase CurrentPhase => _runLoopService != null ? _runLoopService.State.Phase : RunPhase.None;
|
||||
|
||||
public bool CanRoll => _runLoopService != null && _runLoopService.CanRoll() && !_diceManager.IsAnyRolling;
|
||||
public bool CanRoll => _runLoopService != null && _runLoopService.CanBeginRoll() && !_diceManager.IsAnyRolling;
|
||||
public bool CanScore => _runLoopService != null && CurrentPhase == RunPhase.CategorySelection && !_diceManager.IsAnyRolling;
|
||||
public bool IsGameOver => _runLoopService != null && _runLoopService.State.IsFailed;
|
||||
public bool IsShopOpen => CurrentPhase == RunPhase.Shop;
|
||||
public bool IsShopAvailable => _runLoopService != null && _runLoopService.State.IsShopAvailable;
|
||||
|
||||
public event Action<int> OnTurnStarted;
|
||||
public event Action<int> OnRollComplete;
|
||||
@@ -41,6 +41,7 @@ namespace YachtDice.Game
|
||||
public event Action<int> OnQuotaChanged;
|
||||
public event Action<int, int> OnCycleCompleted;
|
||||
public event Action<RunPhase> OnPhaseChanged;
|
||||
public event Action<bool> OnShopAvailabilityChanged;
|
||||
|
||||
[Inject]
|
||||
public void Construct(DiceManager diceManager, RunLoopService runLoopService, ScoringSystem scoringSystem)
|
||||
@@ -50,6 +51,7 @@ namespace YachtDice.Game
|
||||
_scoringSystem = scoringSystem;
|
||||
|
||||
_runLoopService.OnStageStarted += HandleStageStarted;
|
||||
_runLoopService.OnCategoryRecorded += HandleCategoryRecorded;
|
||||
_runLoopService.OnStageCleared += HandleStageCleared;
|
||||
_runLoopService.OnRunFailed += HandleRunFailed;
|
||||
_runLoopService.OnBetStarted += HandleBetStarted;
|
||||
@@ -59,6 +61,7 @@ namespace YachtDice.Game
|
||||
_runLoopService.OnQuotaChanged += HandleQuotaChanged;
|
||||
_runLoopService.OnCycleCompleted += HandleCycleCompleted;
|
||||
_runLoopService.OnPhaseChanged += HandlePhaseChanged;
|
||||
_runLoopService.OnShopAvailabilityChanged += HandleShopAvailabilityChanged;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
@@ -67,6 +70,7 @@ namespace YachtDice.Game
|
||||
return;
|
||||
|
||||
_runLoopService.OnStageStarted -= HandleStageStarted;
|
||||
_runLoopService.OnCategoryRecorded -= HandleCategoryRecorded;
|
||||
_runLoopService.OnStageCleared -= HandleStageCleared;
|
||||
_runLoopService.OnRunFailed -= HandleRunFailed;
|
||||
_runLoopService.OnBetStarted -= HandleBetStarted;
|
||||
@@ -76,6 +80,7 @@ namespace YachtDice.Game
|
||||
_runLoopService.OnQuotaChanged -= HandleQuotaChanged;
|
||||
_runLoopService.OnCycleCompleted -= HandleCycleCompleted;
|
||||
_runLoopService.OnPhaseChanged -= HandlePhaseChanged;
|
||||
_runLoopService.OnShopAvailabilityChanged -= HandleShopAvailabilityChanged;
|
||||
}
|
||||
|
||||
public void StartNewGame()
|
||||
@@ -86,23 +91,21 @@ namespace YachtDice.Game
|
||||
|
||||
public void CompleteShop()
|
||||
{
|
||||
if (!IsShopOpen)
|
||||
return;
|
||||
|
||||
_runLoopService.CompleteShop();
|
||||
OnShopClosed?.Invoke();
|
||||
}
|
||||
|
||||
public void Roll()
|
||||
public bool Roll()
|
||||
{
|
||||
if (!CanRoll)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!_runLoopService.TryBeginRoll())
|
||||
return;
|
||||
return false;
|
||||
|
||||
_diceManager.OnAllDiceSettled += HandleAllDiceSettled;
|
||||
_diceManager.RollUnlocked();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleAllDiceSettled()
|
||||
@@ -139,7 +142,7 @@ namespace YachtDice.Game
|
||||
|
||||
public bool CanOpenShopManually()
|
||||
{
|
||||
return IsShopOpen;
|
||||
return IsShopAvailable && !IsGameOver;
|
||||
}
|
||||
|
||||
private void HandleStageStarted(RunStageState stage)
|
||||
@@ -148,10 +151,14 @@ namespace YachtDice.Game
|
||||
OnTurnStarted?.Invoke(stage.Index + 1);
|
||||
}
|
||||
|
||||
private void HandleCategoryRecorded(CategoryDefinition category, ScoreResult result)
|
||||
{
|
||||
OnScored?.Invoke(category, result.FinalScore);
|
||||
}
|
||||
|
||||
private void HandleStageCleared(RunStageState stage, CategoryDefinition category, ScoreResult result)
|
||||
{
|
||||
_diceManager.UnlockAll();
|
||||
OnScored?.Invoke(category, result.FinalScore);
|
||||
}
|
||||
|
||||
private void HandleRunFailed(RunState state)
|
||||
@@ -193,5 +200,10 @@ namespace YachtDice.Game
|
||||
{
|
||||
OnPhaseChanged?.Invoke(phase);
|
||||
}
|
||||
|
||||
private void HandleShopAvailabilityChanged(bool isAvailable)
|
||||
{
|
||||
OnShopAvailabilityChanged?.Invoke(isAvailable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace YachtDice.Modifiers.Editor
|
||||
return;
|
||||
}
|
||||
|
||||
_inventoryController = FindObjectOfType<InventoryController>();
|
||||
_inventoryController = FindAnyObjectByType<InventoryController>();
|
||||
}
|
||||
|
||||
private void RefreshDefinitions()
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace YachtDice.Modifiers.Pipeline
|
||||
{
|
||||
string traceStr = trace.ToString();
|
||||
context.DebugLog.Add(traceStr);
|
||||
Debug.Log(traceStr);
|
||||
// Debug.Log(traceStr);
|
||||
}
|
||||
|
||||
_isExecuting = false;
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace YachtDice.Run
|
||||
public event Action<RunState> OnBetStarted;
|
||||
public event Action<RunState> OnShopOpened;
|
||||
public event Action<RunStageState> OnStageStarted;
|
||||
public event Action<CategoryDefinition, ScoreResult> OnCategoryRecorded;
|
||||
public event Action<RunStageState, CategoryDefinition, ScoreResult> OnStageCleared;
|
||||
public event Action<RunStageState> OnStageFailed;
|
||||
public event Action<int> OnStoredRollsChanged;
|
||||
@@ -49,22 +50,29 @@ namespace YachtDice.Run
|
||||
public event Action<int> OnQuotaChanged;
|
||||
public event Action<RunState> OnRunFailed;
|
||||
public event Action<RunPhase> OnPhaseChanged;
|
||||
public event Action<bool> OnShopAvailabilityChanged;
|
||||
public event Action OnCategoriesRefreshed;
|
||||
|
||||
public void StartNewRun()
|
||||
{
|
||||
_scoringSystem.ResetScorecard();
|
||||
_storedRollBank.Reset();
|
||||
if (_currencyBank != null)
|
||||
_currencyBank.SetBalance(0);
|
||||
|
||||
_state.BaseQuota = _config.StartingBaseQuota;
|
||||
_state.BetIndex = 0;
|
||||
_state.StageIndex = 0;
|
||||
_state.CurrentStageGoal = _config.GetStageTarget(_state.BaseQuota, 0);
|
||||
_state.CurrentRoll = 0;
|
||||
_state.CurrentStageRollBudget = _config.BaseRollsPerStage;
|
||||
_state.CurrentStageTarget = _config.GetStageTarget(_state.BaseQuota, 0);
|
||||
_state.CurrentStageTarget = _state.CurrentStageGoal;
|
||||
_state.StoredRolls = _storedRollBank.Value;
|
||||
_state.IsActive = true;
|
||||
_state.IsFailed = false;
|
||||
_state.IsShopUnlocked = false;
|
||||
_state.IsShopAvailable = false;
|
||||
_state.HasRolledThisStage = false;
|
||||
SetPhase(RunPhase.None);
|
||||
|
||||
OnRunStarted?.Invoke(_state);
|
||||
@@ -73,10 +81,8 @@ namespace YachtDice.Run
|
||||
|
||||
public void CompleteShop()
|
||||
{
|
||||
if (_state.Phase != RunPhase.Shop || !_state.IsActive)
|
||||
if (!_state.IsActive)
|
||||
return;
|
||||
|
||||
StartStage(0);
|
||||
}
|
||||
|
||||
public bool CanRoll()
|
||||
@@ -89,9 +95,14 @@ namespace YachtDice.Run
|
||||
|| _state.Phase == RunPhase.Rolling;
|
||||
}
|
||||
|
||||
public bool CanBeginRoll()
|
||||
{
|
||||
return CanRoll() && HasRemainingRollCapacity();
|
||||
}
|
||||
|
||||
public bool TryBeginRoll()
|
||||
{
|
||||
if (!CanRoll())
|
||||
if (!CanBeginRoll())
|
||||
return false;
|
||||
|
||||
if (_state.CurrentRoll >= _state.CurrentStageRollBudget)
|
||||
@@ -103,6 +114,13 @@ namespace YachtDice.Run
|
||||
_state.StoredRolls = _storedRollBank.Value;
|
||||
}
|
||||
|
||||
if (!_state.HasRolledThisStage)
|
||||
{
|
||||
_state.HasRolledThisStage = true;
|
||||
if (_state.IsShopAvailable)
|
||||
SetShopAvailable(false);
|
||||
}
|
||||
|
||||
_state.CurrentRoll += 1;
|
||||
SetPhase(RunPhase.Rolling);
|
||||
return true;
|
||||
@@ -130,8 +148,7 @@ namespace YachtDice.Run
|
||||
if (_state.CurrentRoll <= 0)
|
||||
return false;
|
||||
|
||||
var preview = PreviewScore(dice, category);
|
||||
return preview.FinalScore >= _state.CurrentStageTarget;
|
||||
return true;
|
||||
}
|
||||
|
||||
public ScoreResult PreviewScore(IReadOnlyList<IDice> dice, CategoryDefinition category)
|
||||
@@ -182,19 +199,36 @@ namespace YachtDice.Run
|
||||
if (remainingRolls > 0)
|
||||
_storedRollBank.Add(remainingRolls);
|
||||
|
||||
if (_config.StageClearReward > 0 && _currencyBank != null)
|
||||
_currencyBank.Add(_config.StageClearReward);
|
||||
_state.CurrentStageTarget = Math.Max(0, _state.CurrentStageTarget - result.FinalScore);
|
||||
OnCategoryRecorded?.Invoke(category, result);
|
||||
|
||||
SetPhase(RunPhase.StageResolved);
|
||||
OnStageCleared?.Invoke(clearedStage, category, result);
|
||||
|
||||
if (_state.StageIndex >= _config.StageCount - 1)
|
||||
if (_state.CurrentStageTarget <= 0)
|
||||
{
|
||||
ResolveBet();
|
||||
if (_config.StageClearReward > 0 && _currencyBank != null)
|
||||
_currencyBank.Add(_config.StageClearReward);
|
||||
|
||||
if (!_state.IsShopUnlocked)
|
||||
_state.IsShopUnlocked = true;
|
||||
|
||||
SetPhase(RunPhase.StageResolved);
|
||||
OnStageCleared?.Invoke(clearedStage, category, result);
|
||||
|
||||
if (_state.StageIndex >= _config.StageCount - 1)
|
||||
{
|
||||
ResolveBet();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartStage(_state.StageIndex + 1);
|
||||
}
|
||||
}
|
||||
else if (HasAnyAvailableCategory())
|
||||
{
|
||||
StartStageAttempt();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartStage(_state.StageIndex + 1);
|
||||
FailCurrentStage();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -220,22 +254,30 @@ namespace YachtDice.Run
|
||||
_state.StageIndex = 0;
|
||||
_state.CurrentRoll = 0;
|
||||
_state.CurrentStageRollBudget = _config.BaseRollsPerStage;
|
||||
_state.CurrentStageTarget = _config.GetStageTarget(_state.BaseQuota, 0);
|
||||
_state.CurrentStageGoal = _config.GetStageTarget(_state.BaseQuota, 0);
|
||||
_state.CurrentStageTarget = _state.CurrentStageGoal;
|
||||
|
||||
SetPhase(RunPhase.StartBet);
|
||||
OnCategoriesRefreshed?.Invoke();
|
||||
OnBetStarted?.Invoke(_state);
|
||||
|
||||
SetPhase(RunPhase.Shop);
|
||||
OnShopOpened?.Invoke(_state);
|
||||
StartStage(0);
|
||||
}
|
||||
|
||||
private void StartStage(int stageIndex)
|
||||
{
|
||||
_state.StageIndex = stageIndex;
|
||||
_state.CurrentStageGoal = _config.GetStageTarget(_state.BaseQuota, stageIndex);
|
||||
_state.CurrentStageTarget = _state.CurrentStageGoal;
|
||||
StartStageAttempt(_state.IsShopUnlocked);
|
||||
}
|
||||
|
||||
private void StartStageAttempt(bool allowShopAtStart = false)
|
||||
{
|
||||
_state.CurrentRoll = 0;
|
||||
_state.CurrentStageRollBudget = _config.BaseRollsPerStage;
|
||||
_state.CurrentStageTarget = _config.GetStageTarget(_state.BaseQuota, stageIndex);
|
||||
_state.HasRolledThisStage = false;
|
||||
|
||||
SetShopAvailable(allowShopAtStart);
|
||||
|
||||
SetPhase(RunPhase.StageStart);
|
||||
OnStageStarted?.Invoke(GetCurrentStageState());
|
||||
@@ -279,6 +321,15 @@ namespace YachtDice.Run
|
||||
OnPhaseChanged?.Invoke(phase);
|
||||
}
|
||||
|
||||
private void SetShopAvailable(bool isAvailable)
|
||||
{
|
||||
_state.IsShopAvailable = isAvailable;
|
||||
OnShopAvailabilityChanged?.Invoke(isAvailable);
|
||||
|
||||
if (isAvailable)
|
||||
OnShopOpened?.Invoke(_state);
|
||||
}
|
||||
|
||||
private void HandleStoredRollsChanged(int value)
|
||||
{
|
||||
_state.StoredRolls = value;
|
||||
@@ -289,5 +340,21 @@ namespace YachtDice.Run
|
||||
{
|
||||
OnCurrencyChanged?.Invoke(value);
|
||||
}
|
||||
|
||||
private bool HasAnyAvailableCategory()
|
||||
{
|
||||
var catalog = _scoringSystem.Catalog;
|
||||
if (catalog == null)
|
||||
return false;
|
||||
|
||||
var categories = catalog.All;
|
||||
for (var i = 0; i < categories.Count; i++)
|
||||
{
|
||||
if (!_scoringSystem.IsCategoryUsed(categories[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,16 @@ namespace YachtDice.Run
|
||||
public int BaseQuota;
|
||||
public int BetIndex;
|
||||
public int StageIndex;
|
||||
public int CurrentStageGoal;
|
||||
public int CurrentStageTarget;
|
||||
public int CurrentRoll;
|
||||
public int CurrentStageRollBudget;
|
||||
public int StoredRolls;
|
||||
public bool IsActive;
|
||||
public bool IsFailed;
|
||||
public bool IsShopUnlocked;
|
||||
public bool IsShopAvailable;
|
||||
public bool HasRolledThisStage;
|
||||
public RunPhase Phase;
|
||||
|
||||
public int StageNumber => StageIndex + 1;
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace YachtDice.Shop
|
||||
private ShopModel _model;
|
||||
|
||||
public ShopCatalog Catalog => _catalog;
|
||||
public event System.Action OnCloseRequested;
|
||||
|
||||
[Inject]
|
||||
public void Construct(ShopCatalog catalog, CurrencyBank currencyBank, ShopModel model)
|
||||
@@ -25,6 +26,7 @@ namespace YachtDice.Shop
|
||||
private void Start()
|
||||
{
|
||||
shopView.OnBuyClicked += HandleBuyClicked;
|
||||
shopView.OnCloseRequested += HandleCloseRequested;
|
||||
_currencyBank.OnBalanceChanged += HandleCurrencyChanged;
|
||||
_model.OnItemPurchased += HandleItemPurchased;
|
||||
|
||||
@@ -35,7 +37,10 @@ namespace YachtDice.Shop
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (shopView != null)
|
||||
{
|
||||
shopView.OnBuyClicked -= HandleBuyClicked;
|
||||
shopView.OnCloseRequested -= HandleCloseRequested;
|
||||
}
|
||||
|
||||
if (_currencyBank != null)
|
||||
_currencyBank.OnBalanceChanged -= HandleCurrencyChanged;
|
||||
@@ -83,5 +88,10 @@ namespace YachtDice.Shop
|
||||
{
|
||||
shopView.RefreshStates(_catalog.All, _model);
|
||||
}
|
||||
|
||||
private void HandleCloseRequested()
|
||||
{
|
||||
OnCloseRequested?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,18 @@ namespace YachtDice.Shop
|
||||
private readonly List<ShopItemView> _spawnedItems = new();
|
||||
|
||||
public event Action<IShopItem> OnBuyClicked;
|
||||
public event Action OnCloseRequested;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (closeButton != null)
|
||||
closeButton.onClick.AddListener(Hide);
|
||||
closeButton.onClick.AddListener(HandleCloseClicked);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (closeButton != null)
|
||||
closeButton.onClick.RemoveListener(Hide);
|
||||
closeButton.onClick.RemoveListener(HandleCloseClicked);
|
||||
}
|
||||
|
||||
public void Show() => gameObject.SetActive(true);
|
||||
@@ -92,5 +93,10 @@ namespace YachtDice.Shop
|
||||
if (tooltipView != null)
|
||||
tooltipView.Hide();
|
||||
}
|
||||
|
||||
private void HandleCloseClicked()
|
||||
{
|
||||
OnCloseRequested?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace YachtDice.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StartNewRun_EntersShopWithConfigQuota()
|
||||
public void StartNewRun_StartsFirstStageWithoutShop()
|
||||
{
|
||||
var categories = CreateFixedCatalog(100, 100, 100);
|
||||
var service = CreateService(categories);
|
||||
@@ -62,8 +62,10 @@ namespace YachtDice.Tests
|
||||
|
||||
Assert.AreEqual(30, service.State.BaseQuota);
|
||||
Assert.AreEqual(1, service.State.BetIndex);
|
||||
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
Assert.AreEqual(0, service.State.StoredRolls);
|
||||
Assert.AreEqual(0, _currencyBank.Balance);
|
||||
Assert.IsFalse(service.State.IsShopAvailable);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,7 +75,6 @@ namespace YachtDice.Tests
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
service.CompleteShop();
|
||||
service.TryBeginRoll();
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
|
||||
@@ -84,6 +85,41 @@ namespace YachtDice.Tests
|
||||
Assert.AreEqual(2, service.State.StoredRolls);
|
||||
Assert.AreEqual(2, service.State.StageNumber);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
Assert.IsTrue(service.State.IsShopAvailable);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CategoryCanBeScored_WithZeroResultAfterFirstRoll()
|
||||
{
|
||||
var categories = CreateFixedCatalog(0, 100, 100);
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
service.TryBeginRoll();
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
|
||||
Assert.IsTrue(service.CanScoreCategory(_emptyDice, categories.All[0]));
|
||||
Assert.IsTrue(service.TryScoreCategoryAsync(_emptyDice, categories.All[0]).GetAwaiter().GetResult());
|
||||
Assert.AreEqual(30, service.State.CurrentStageTarget);
|
||||
Assert.AreEqual(1, service.State.StageNumber);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TargetDecreasesByRecordedScore_AndStageContinues()
|
||||
{
|
||||
var categories = CreateFixedCatalog(5, 100, 100);
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
service.TryBeginRoll();
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
|
||||
Assert.IsTrue(service.TryScoreCategoryAsync(_emptyDice, categories.All[0]).GetAwaiter().GetResult());
|
||||
|
||||
Assert.AreEqual(25, service.State.CurrentStageTarget);
|
||||
Assert.AreEqual(1, service.State.StageNumber);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -93,7 +129,6 @@ namespace YachtDice.Tests
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
service.CompleteShop();
|
||||
service.TryBeginRoll();
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
service.TryScoreCategoryAsync(_emptyDice, categories.All[0]).GetAwaiter().GetResult();
|
||||
@@ -117,8 +152,6 @@ namespace YachtDice.Tests
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
service.CompleteShop();
|
||||
|
||||
ClearStageInOneRoll(service, categories.All[0]);
|
||||
ClearStageInOneRoll(service, categories.All[1]);
|
||||
ClearStageInOneRoll(service, categories.All[2]);
|
||||
@@ -126,9 +159,10 @@ namespace YachtDice.Tests
|
||||
Assert.AreEqual(135, _currencyBank.Balance);
|
||||
Assert.AreEqual(60, service.State.BaseQuota);
|
||||
Assert.AreEqual(2, service.State.BetIndex);
|
||||
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
Assert.AreEqual(6, service.State.StoredRolls);
|
||||
Assert.AreEqual(0, _scoringSystem.CategoriesFilledCount);
|
||||
Assert.IsTrue(service.State.IsShopAvailable);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -138,37 +172,56 @@ namespace YachtDice.Tests
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
service.CompleteShop();
|
||||
Assert.IsTrue(service.TryBeginRoll());
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
Assert.IsTrue(service.TryBeginRoll());
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
}
|
||||
|
||||
Assert.IsTrue(service.State.IsFailed);
|
||||
Assert.AreEqual(RunPhase.RunFailed, service.State.Phase);
|
||||
Assert.IsFalse(service.State.IsFailed);
|
||||
Assert.AreEqual(RunPhase.CategorySelection, service.State.Phase);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Shop_ReopensOnlyAtStartOfNextBet()
|
||||
public void ShopUnlocksAfterFirstClear_AndClosesAfterFirstRoll()
|
||||
{
|
||||
var categories = CreateFixedCatalog(100, 100, 100, 100);
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
|
||||
Assert.IsFalse(service.State.IsShopAvailable);
|
||||
|
||||
service.CompleteShop();
|
||||
ClearStageInOneRoll(service, categories.All[0]);
|
||||
Assert.IsTrue(service.State.IsShopAvailable);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
|
||||
ClearStageInOneRoll(service, categories.All[1]);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
Assert.IsTrue(service.TryBeginRoll());
|
||||
Assert.IsFalse(service.State.IsShopAvailable);
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
Assert.IsTrue(service.TryScoreCategoryAsync(_emptyDice, categories.All[1]).GetAwaiter().GetResult());
|
||||
|
||||
Assert.IsTrue(service.State.IsShopAvailable);
|
||||
|
||||
ClearStageInOneRoll(service, categories.All[2]);
|
||||
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
|
||||
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
|
||||
Assert.AreEqual(2, service.State.BetIndex);
|
||||
Assert.IsTrue(service.State.IsShopAvailable);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeginRoll_BecomesFalse_WhenNoRollsOrStoredRollsRemain()
|
||||
{
|
||||
var categories = CreateFixedCatalog(100, 100, 100, 100);
|
||||
var service = CreateService(categories);
|
||||
|
||||
service.StartNewRun();
|
||||
|
||||
Assert.IsTrue(service.TryBeginRoll());
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
Assert.IsTrue(service.TryBeginRoll());
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
Assert.IsTrue(service.TryBeginRoll());
|
||||
service.NotifyRollResolved(_emptyDice);
|
||||
|
||||
Assert.IsFalse(service.CanBeginRoll());
|
||||
Assert.AreEqual(RunPhase.CategorySelection, service.State.Phase);
|
||||
}
|
||||
|
||||
private RunLoopService CreateService(CategoryCatalog catalog)
|
||||
|
||||
@@ -82,6 +82,12 @@ namespace YachtDice.UI
|
||||
rollButtonText.text = $"Бросок {currentRoll + 1}/{maxRolls}";
|
||||
}
|
||||
|
||||
public void SetRollButtonPending()
|
||||
{
|
||||
rollButton.interactable = false;
|
||||
rollButtonText.text = "Бросок...";
|
||||
}
|
||||
|
||||
public void SetRollButtonInteractable(bool interactable)
|
||||
{
|
||||
rollButton.interactable = interactable;
|
||||
|
||||
@@ -7,8 +7,14 @@ namespace YachtDice.UI
|
||||
{
|
||||
public class GameInfoView : MonoBehaviour
|
||||
{
|
||||
[Header("Turn Info")]
|
||||
[Header("Run HUD")]
|
||||
[SerializeField] private TMP_Text turnText;
|
||||
[SerializeField] private TMP_Text phaseText;
|
||||
[SerializeField] private TMP_Text betText;
|
||||
[SerializeField] private TMP_Text stageText;
|
||||
[SerializeField] private TMP_Text targetText;
|
||||
[SerializeField] private TMP_Text quotaText;
|
||||
[SerializeField] private TMP_Text storedRollsText;
|
||||
|
||||
[Header("Currency")]
|
||||
[SerializeField] private TMP_Text currencyText;
|
||||
@@ -28,6 +34,9 @@ namespace YachtDice.UI
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
EnsureHudBindings();
|
||||
EnsureGameOverBindings();
|
||||
|
||||
newGameButton.onClick.AddListener(() => OnNewGameClicked?.Invoke());
|
||||
gameOverPanel.SetActive(false);
|
||||
|
||||
@@ -43,9 +52,27 @@ namespace YachtDice.UI
|
||||
}
|
||||
|
||||
public void SetRunInfoText(string text)
|
||||
{
|
||||
if (phaseText != null)
|
||||
phaseText.text = text;
|
||||
}
|
||||
|
||||
public void SetRunHud(string phase, int bet, int stage, int stageCount, int target, int quota, int storedRolls)
|
||||
{
|
||||
if (turnText != null)
|
||||
turnText.text = text;
|
||||
turnText.text = $"Turn {stage}/{stageCount}";
|
||||
if (phaseText != null)
|
||||
phaseText.text = $"Phase: {phase}";
|
||||
if (betText != null)
|
||||
betText.text = $"Bet: {bet}";
|
||||
if (stageText != null)
|
||||
stageText.text = $"Stage: {stage}/{stageCount}";
|
||||
if (targetText != null)
|
||||
targetText.text = $"Target: {target}";
|
||||
if (quotaText != null)
|
||||
quotaText.text = $"Quota: {quota}";
|
||||
if (storedRollsText != null)
|
||||
storedRollsText.text = $"Stored Rolls: {storedRolls}";
|
||||
}
|
||||
|
||||
public void SetCurrencyText(int amount)
|
||||
@@ -80,5 +107,61 @@ namespace YachtDice.UI
|
||||
if (inventoryButton != null)
|
||||
inventoryButton.onClick.RemoveAllListeners();
|
||||
}
|
||||
|
||||
private void EnsureHudBindings()
|
||||
{
|
||||
if (turnText == null)
|
||||
return;
|
||||
|
||||
phaseText ??= turnText;
|
||||
betText ??= CreateHudLabel("Bet Text", -34);
|
||||
stageText ??= CreateHudLabel("Stage Text", -68);
|
||||
targetText ??= CreateHudLabel("Target Text", -102);
|
||||
quotaText ??= CreateHudLabel("Quota Text", -136);
|
||||
storedRollsText ??= CreateHudLabel("Stored Rolls Text", -170);
|
||||
}
|
||||
|
||||
private void EnsureGameOverBindings()
|
||||
{
|
||||
if (gameOverPanel == null)
|
||||
return;
|
||||
|
||||
if (finalScoreText == null || finalScoreText == currencyText)
|
||||
{
|
||||
TMP_Text source = turnText != null ? turnText : currencyText;
|
||||
if (source == null)
|
||||
return;
|
||||
|
||||
finalScoreText = Instantiate(source, gameOverPanel.transform);
|
||||
finalScoreText.name = "Final Score Text";
|
||||
|
||||
if (finalScoreText.transform is RectTransform rect)
|
||||
{
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.anchoredPosition = new Vector2(0f, 40f);
|
||||
rect.sizeDelta = new Vector2(360f, 60f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TMP_Text CreateHudLabel(string objectName, float yOffset)
|
||||
{
|
||||
var clone = Instantiate(turnText, turnText.transform.parent);
|
||||
clone.name = objectName;
|
||||
clone.text = string.Empty;
|
||||
|
||||
if (clone.transform is RectTransform rect && turnText.transform is RectTransform sourceRect)
|
||||
{
|
||||
rect.anchorMin = sourceRect.anchorMin;
|
||||
rect.anchorMax = sourceRect.anchorMax;
|
||||
rect.pivot = sourceRect.pivot;
|
||||
rect.sizeDelta = sourceRect.sizeDelta;
|
||||
rect.anchoredPosition = sourceRect.anchoredPosition + new Vector2(0f, yOffset);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace YachtDice.UI.Presentation
|
||||
|
||||
public void PrepareForRoll()
|
||||
{
|
||||
_view.SetRollButtonState(false, _gameLoopController.CurrentRoll, _gameLoopController.MaxRollsPerTurn);
|
||||
_view.SetRollButtonPending();
|
||||
_view.SetDiceInteractable(false);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace YachtDice.UI.Presentation
|
||||
{
|
||||
var canRollAgain = _gameLoopController.CanRoll;
|
||||
_view.SetRollButtonState(canRollAgain, rollNumber, _gameLoopController.MaxRollsPerTurn);
|
||||
_view.SetDiceInteractable(true);
|
||||
_view.SetDiceInteractable(canRollAgain);
|
||||
_view.SetAllDiceValues(_diceManager.GetCurrentValues());
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace YachtDice.UI.Presentation
|
||||
_gameLoopController.OnQuotaChanged += HandleQuotaChanged;
|
||||
_gameLoopController.OnCycleCompleted += HandleCycleCompleted;
|
||||
_gameLoopController.OnPhaseChanged += HandlePhaseChanged;
|
||||
_gameLoopController.OnShopAvailabilityChanged += HandleShopAvailabilityChanged;
|
||||
|
||||
_dicePanelPresenter.RollClicked += HandleRollClicked;
|
||||
_dicePanelPresenter.DiceToggled += HandleDiceToggled;
|
||||
@@ -72,13 +73,14 @@ namespace YachtDice.UI.Presentation
|
||||
_gameInfoPresenter.NewGameClicked += HandleNewGameClicked;
|
||||
_gameInfoPresenter.ShopClicked += HandleShopClicked;
|
||||
_gameInfoPresenter.InventoryClicked += HandleInventoryClicked;
|
||||
_shopController.OnCloseRequested += HandleShopCloseRequested;
|
||||
|
||||
_currencyBank.OnBalanceChanged += HandleCurrencyChanged;
|
||||
_playerModel.OnChanged += HandlePlayerChangedForSave;
|
||||
|
||||
_saveService.Load();
|
||||
_gameInfoPresenter.SetCurrencyText(_currencyBank.Balance);
|
||||
_gameInfoPresenter.SetShopButtonInteractable(false);
|
||||
_gameInfoPresenter.SetShopButtonInteractable(_gameLoopController.CanOpenShopManually());
|
||||
_gameLoopController.StartNewGame();
|
||||
}
|
||||
|
||||
@@ -95,6 +97,7 @@ namespace YachtDice.UI.Presentation
|
||||
_gameLoopController.OnQuotaChanged -= HandleQuotaChanged;
|
||||
_gameLoopController.OnCycleCompleted -= HandleCycleCompleted;
|
||||
_gameLoopController.OnPhaseChanged -= HandlePhaseChanged;
|
||||
_gameLoopController.OnShopAvailabilityChanged -= HandleShopAvailabilityChanged;
|
||||
|
||||
_dicePanelPresenter.RollClicked -= HandleRollClicked;
|
||||
_dicePanelPresenter.DiceToggled -= HandleDiceToggled;
|
||||
@@ -102,6 +105,7 @@ namespace YachtDice.UI.Presentation
|
||||
_gameInfoPresenter.NewGameClicked -= HandleNewGameClicked;
|
||||
_gameInfoPresenter.ShopClicked -= HandleShopClicked;
|
||||
_gameInfoPresenter.InventoryClicked -= HandleInventoryClicked;
|
||||
_shopController.OnCloseRequested -= HandleShopCloseRequested;
|
||||
|
||||
_currencyBank.OnBalanceChanged -= HandleCurrencyChanged;
|
||||
|
||||
@@ -115,12 +119,14 @@ namespace YachtDice.UI.Presentation
|
||||
_dicePanelPresenter.ResetForNewTurn();
|
||||
_dicePanelPresenter.SetRollingEnabled(true);
|
||||
_scoreCardPresenter.ClearAllPreviews();
|
||||
_gameInfoPresenter.SetShopButtonInteractable(_gameLoopController.CanOpenShopManually());
|
||||
}
|
||||
|
||||
private void HandleRollComplete(int rollNumber)
|
||||
{
|
||||
_dicePanelPresenter.HandleRollComplete(rollNumber);
|
||||
_scoreCardPresenter.UpdatePreviewScores();
|
||||
_gameInfoPresenter.SetShopButtonInteractable(_gameLoopController.CanOpenShopManually());
|
||||
}
|
||||
|
||||
private void HandleScored(CategoryDefinition category, int finalScore)
|
||||
@@ -143,9 +149,11 @@ namespace YachtDice.UI.Presentation
|
||||
|
||||
private void HandleRollClicked()
|
||||
{
|
||||
if (!_gameLoopController.Roll())
|
||||
return;
|
||||
|
||||
_dicePanelPresenter.PrepareForRoll();
|
||||
_scoreCardPresenter.SetAllInteractable(false);
|
||||
_gameLoopController.Roll();
|
||||
}
|
||||
|
||||
private void HandleDiceToggled(int index)
|
||||
@@ -175,14 +183,9 @@ namespace YachtDice.UI.Presentation
|
||||
return;
|
||||
|
||||
if (_shopController.IsOpen)
|
||||
{
|
||||
_shopController.Close();
|
||||
_gameLoopController.CompleteShop();
|
||||
}
|
||||
else
|
||||
{
|
||||
_shopController.Open();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleInventoryClicked()
|
||||
@@ -208,16 +211,14 @@ namespace YachtDice.UI.Presentation
|
||||
|
||||
private void HandleShopOpened()
|
||||
{
|
||||
_shopController.Open();
|
||||
_gameInfoPresenter.SetShopButtonInteractable(true);
|
||||
_dicePanelPresenter.SetRollingEnabled(false);
|
||||
_gameInfoPresenter.SetShopButtonInteractable(_gameLoopController.CanOpenShopManually());
|
||||
UpdateRunInfoText();
|
||||
}
|
||||
|
||||
private void HandleShopClosed()
|
||||
{
|
||||
_shopController.Close();
|
||||
_gameInfoPresenter.SetShopButtonInteractable(false);
|
||||
_gameInfoPresenter.SetShopButtonInteractable(_gameLoopController.CanOpenShopManually());
|
||||
UpdateRunInfoText();
|
||||
}
|
||||
|
||||
@@ -240,16 +241,36 @@ namespace YachtDice.UI.Presentation
|
||||
|
||||
private void HandlePhaseChanged(RunPhase phase)
|
||||
{
|
||||
if (phase != RunPhase.Shop)
|
||||
_gameInfoPresenter.SetShopButtonInteractable(false);
|
||||
|
||||
_gameInfoPresenter.SetShopButtonInteractable(_gameLoopController.CanOpenShopManually());
|
||||
UpdateRunInfoText();
|
||||
}
|
||||
|
||||
private void UpdateRunInfoText()
|
||||
{
|
||||
var info = $"Bet {_gameLoopController.CurrentBet} | Stage {_gameLoopController.CurrentStage}/3 | Target {_gameLoopController.CurrentStageTarget} | Quota {_gameLoopController.CurrentBaseQuota} | Bank {_gameLoopController.StoredRolls}";
|
||||
_gameInfoPresenter.SetRunInfoText(info);
|
||||
var phase = _gameLoopController.CurrentPhase.ToString();
|
||||
_gameInfoPresenter.SetRunHud(
|
||||
phase,
|
||||
_gameLoopController.CurrentBet,
|
||||
_gameLoopController.CurrentStage,
|
||||
3,
|
||||
_gameLoopController.CurrentStageTarget,
|
||||
_gameLoopController.CurrentBaseQuota,
|
||||
_gameLoopController.StoredRolls);
|
||||
}
|
||||
|
||||
private void HandleShopCloseRequested()
|
||||
{
|
||||
_shopController.Close();
|
||||
_gameLoopController.CompleteShop();
|
||||
}
|
||||
|
||||
private void HandleShopAvailabilityChanged(bool isAvailable)
|
||||
{
|
||||
if (!isAvailable && _shopController.IsOpen)
|
||||
_shopController.Close();
|
||||
|
||||
_gameInfoPresenter.SetShopButtonInteractable(isAvailable);
|
||||
UpdateRunInfoText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace YachtDice.UI.Presentation
|
||||
_view.SetRunInfoText(text);
|
||||
}
|
||||
|
||||
public void SetRunHud(string phase, int bet, int stage, int stageCount, int target, int quota, int storedRolls)
|
||||
{
|
||||
_view.SetRunHud(phase, bet, stage, stageCount, target, quota, storedRolls);
|
||||
}
|
||||
|
||||
public void SetCurrencyText(int amount)
|
||||
{
|
||||
_view.SetCurrencyText(amount);
|
||||
|
||||
Reference in New Issue
Block a user