Skip to content

Archived on 2026-10-24. This is a historical implementation note, not current setup instructions or an active task list. Original claims, code examples, file paths, and completion statements have not all been revalidated. Use the current documentation and issue register for maintenance decisions.

Audit correction (2026-10-24): current source still has separate reward/pause gates and SetRewarding does not update time scale. The completion claims below do not establish resolved reward behavior; see GAME-02.

GameState Refactoring Summary

Overview

This document summarizes the complete refactoring of the GameState system to fix logic issues, remove unused states, and create a unified state management system with proper time-scale control.

Problems Identified

1. Redundant/Conflicting Reward States (4 flags)

The reward system had 4 separate states managing overlapping logic: - isDisplayingReward - During reward animation - isRewardEvent - Reward event is active - isRerollReward - During reroll operation - isSelectingReward - Player can select rewards

Issue: These states were not properly coordinated, causing potential conflicts and confusion.

2. Multiple Pause Tracking Systems

The game had THREE separate pause tracking mechanisms: 1. GameStateService.isPaused - Used everywhere 2. GameMenuManager.isGamePaused - Private bool for settings 3. GameStateService.isSettings - Never used anywhere

Issue: When rewards were displayed, PauseGame() was called (Time.timeScale = 0), but if player opened settings and closed them, ResumeGame() would be called, resuming time even though we were still in reward display. This caused the game to continue during reward selection.

3. Unused States (6 flags)

The following states were defined but never used: - isGameRunning - Never referenced in codebase - isBossFight - Never referenced in codebase - isLevelUp - Never referenced in codebase - isBonusTime - Never referenced in codebase - isSettings - Never referenced in codebase - isRewarding - Defined but never used

4. Missing State Blocking Logic

Nothing prevented: - Settings opening during rewards - Pausing during GameOver sequence - GameOver triggering during reward animation - Multiple reward queues processing simultaneously

Solution Implemented

Unified State System

States Kept (3 active + 1 achievement):

ACTIVE STATES:
1. isPaused       - Manual pause (settings open)
2. isRewarding    - In reward event
3. isGameOver     - Game over sequence active

ACHIEVEMENT FLAG:
4. isBossDefeated - Achievement flag (not a state)

States Removed (10 total):

❌ isGameRunning    - Never used
❌ isBossFight      - Never used
❌ isLevelUp        - Never used
❌ isBonusTime      - Never used
❌ isSettings       - Never used in GameState
❌ isRewarding      - Was unused, now actively used
❌ isDisplayingReward - Merged into isRewarding
❌ isRewardEvent    - Merged into isRewarding
❌ isRerollReward   - Merged into isRewarding
❌ isSelectingReward - Merged into isRewarding

Reward Phase System

Instead of 4 separate flags, we now use a single isRewarding state with internal phases:

public enum RewardPhase
{
    None,          // No reward active
    Rolling,        // During roll/reroll animation
    Displaying,     // Cards showing but not yet selectable
    Selecting       // Player can select cards
}

This is tracked internally in RewardRoller.CurrentPhase.

Time Scale Management

Centralized Time Control

public bool ShouldTimeBePaused => isPaused || isRewarding || isGameOver;

public void UpdateTimeScale()
{
    Time.timeScale = ShouldTimeBePaused ? 0f : 1f;
}

Fixed Pause/Resume Logic

  • When isPaused is set, UpdateTimeScale() is automatically called
  • When settings close, we set isPaused = false (NOT call ResumeGame)
  • UpdateTimeScale() checks ALL states to determine if time should stay paused

This fixes the bug where closing settings during rewards would resume time incorrectly.

Blocking Logic Properties

public bool CanPause => !isRewarding && !isGameOver;
public bool CanOpenSettings => !isRewarding && !isGameOver;

These properties prevent invalid state transitions.

Files Modified

1. GameStateService.cs

Changes: - Removed 10 unused state properties and methods - Added RewardPhase enum for internal tracking - Added ShouldTimeBePaused property - Added CanPause and CanOpenSettings blocking properties - Added UpdateTimeScale() method - Reduced from 13 states to 3 active states

2. GameManager.cs

Changes: - Removed direct Time.timeScale manipulation (now handled by GameStateService) - Modified isPaused setter to call UpdateTimeScale() - Made reward properties Obsolete and mapped to RewardRoller.CurrentPhase - Simplified PauseGame() and ResumeGame() methods

3. RewardRoller.cs

Changes: - Added CurrentPhase property for phase tracking - Set isRewarding = true when reward starts - Set CurrentPhase through reward lifecycle - Added CompleteReward() method to reset reward state - Updated checks to use CurrentPhase instead of separate flags - Integrated with GameStateService for state management

4. GameMenuManager.cs

Changes: - Added check for CanPause before opening settings - Modified settings close to set isPaused = false instead of calling ResumeGame - This allows UpdateTimeScale() to check if rewards are still active

5. UpgradeSelection.cs

Changes: - Replaced checks for isSelectingReward with RewardPhase.Selecting - Removed check for menuManager.isSetting (now handled by GameState) - Updated to call RewardRoller.CompleteReward() instead of setting individual flags

6. StatusPanel.cs

Changes: - Replaced check for menuManager.isSetting with GameManager.isPaused - Simplified pause detection logic

How It Works

Reward Flow

  1. Reward Enqueued → ProcessRewardQueue() checks if we can start
  2. Roll Starts → isRewarding = true, CurrentPhase = Rolling
  3. Animation Plays → Time is paused automatically
  4. Cards Display → CurrentPhase = Displaying
  5. Selection Ready → CurrentPhase = Selecting, player can navigate/select
  6. Reward Complete → CompleteReward() called, isRewarding = false, time resumes

Settings During Rewards

  1. Player tries to open settings
  2. CanPause returns false (because isRewarding = true)
  3. Settings cannot open, warning logged
  4. Time remains paused (correct behavior)

Settings After Rewards Complete

  1. Player opens settings → isPaused = true, UpdateTimeScale() sets Time.timeScale = 0
  2. Player closes settings → isPaused = false
  3. UpdateTimeScale() checks: isRewarding = false, isGameOver = false → Time.timeScale = 1
  4. Game resumes (correct behavior)

Benefits

1. Fixed Bugs

  • ✅ Settings no longer resume time during rewards
  • ✅ Impossible to open settings during rewards
  • ✅ Time scale automatically managed
  • ✅ No more conflicting state flags

2. Cleaner Code

  • ✅ Removed 10 unused states
  • ✅ 4 reward flags → 1 state + phases
  • ✅ Centralized time management
  • ✅ Clear blocking logic

3. Better Maintainability

  • ✅ Single source of truth for game states
  • ✅ Easier to add new blocking logic
  • ✅ Clear state transitions
  • ✅ Reduced complexity from 13 to 3 active states

4. Backward Compatibility

  • ✅ Legacy properties still work (marked Obsolete)
  • ✅ Existing code using old properties will still compile
  • ✅ Gradual migration possible

Migration Guide

For New Code

// OLD (deprecated):
if (GameManager.instance.isSelectingReward)
if (GameManager.instance.isRewardEvent)
if (GameManager.instance.isDisplayingReward)

// NEW (use phases):
if (GameManager.instance.RewardRoller.CurrentPhase == RewardPhase.Selecting)
if (GameManager.instance.RewardRoller.CurrentPhase != RewardPhase.None)
if (GameManager.instance.RewardRoller.CurrentPhase == RewardPhase.Rolling || 
    GameManager.instance.RewardRoller.CurrentPhase == RewardPhase.Displaying)

For Checking Pause State

// Check if paused:
if (GameManager.instance.isPaused)

// Check if can pause:
if (GameManager.instance.GameState.CanPause)

// Manual pause:
GameManager.instance.isPaused = true; // Automatically calls UpdateTimeScale()

// Manual resume:
GameManager.instance.isPaused = false; // Automatically calls UpdateTimeScale()

For Time Scale

// Don't manipulate Time.timeScale directly!
// Instead, change state:

// To pause:
GameManager.instance.isPaused = true;

// To resume:
GameManager.instance.isPaused = false;

// The system will automatically manage Time.timeScale based on:
// - isPaused (manual pause)
// - isRewarding (reward display)
// - isGameOver (game over)

Testing Checklist

  • [ ] Can pause during normal gameplay
  • [ ] Cannot pause during rewards
  • [ ] Cannot pause during game over
  • [ ] Settings cannot open during rewards
  • [ ] Settings cannot open during game over
  • [ ] Settings close correctly (time resumes if appropriate)
  • [ ] Rewards work end-to-end (roll → display → select → complete)
  • [ ] Reroll works during selection phase
  • [ ] Reroll cannot happen during animation
  • [ ] Time pauses correctly during rewards
  • [ ] Time resumes correctly after rewards complete
  • [ ] Time stays paused if settings closed during rewards
  • [ ] GameOver blocks all actions correctly
  • [ ] Legacy properties still work (backward compatibility)

Future Improvements

  1. Add More Blocking Logic
  2. Can the player dash during rewards?
  3. Can the player attack during rewards?
  4. Add IsPlayerInputEnabled property

  5. Add State Transition Events

  6. OnRewardStarted
  7. OnRewardCompleted
  8. OnStateChanged

  9. Separate Achievement System

  10. Move isBossDefeated to AchievementManager
  11. Create dedicated achievement tracking system

  12. Add State Validation

  13. Validate state transitions
  14. Log warnings for invalid transitions
  15. Add debug mode for state visualization