[Add] Game Loop Playable

This commit is contained in:
2026-03-28 12:53:34 +07:00
parent 78ad76120f
commit f2173d2c73
20 changed files with 1433 additions and 122 deletions
+87 -20
View File
@@ -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;
}
}
}