// Game.h - Core Tetris game logic (board, piece mechanics, scoring events only) #pragma once #include #include #include #include #include #include #include #include #include "../../core/GravityManager.h" enum PieceType { I, O, T, S, Z, J, L, PIECE_COUNT }; using Shape = std::array; // four rotation bitmasks // Game runtime mode enum class GameMode { Endless, Challenge }; // Special obstacle blocks used by Challenge mode enum class AsteroidType : uint8_t { Normal = 0, Armored = 1, Falling = 2, Core = 3 }; struct AsteroidCell { AsteroidType type{AsteroidType::Normal}; uint8_t hitsRemaining{1}; bool gravityEnabled{false}; uint8_t visualState{0}; }; class Game { public: static constexpr int COLS = 10; static constexpr int ROWS = 20; static constexpr int TILE = 28; // logical cell size in pixels (render layer decides use) struct Piece { PieceType type{PIECE_COUNT}; int rot{0}; int x{3}; int y{-2}; }; explicit Game(int startLevel = 0, GameMode mode = GameMode::Endless) : mode(mode) { reset(startLevel); } void reset(int startLevel = 0); void startChallengeRun(int startingLevel = 1); // resets stats and starts challenge level 1 (or provided) void beginNextChallengeLevel(); // advances to the next challenge level preserving score/time // Simulation ----------------------------------------------------------- void tickGravity(double frameMs); // advance gravity accumulator & drop void softDropBoost(double frameMs); // accelerate fall while held void hardDrop(); // instant drop & lock void setSoftDropping(bool on); // mark if player holds Down void move(int dx); // horizontal move void rotate(int dir); // +1 cw, -1 ccw (simple wall-kick) void holdCurrent(); // swap with hold (once per spawn) // Accessors ----------------------------------------------------------- const std::array& boardRef() const { return board; } const Piece& current() const { return cur; } const Piece& next() const { return nextPiece; } const Piece& held() const { return hold; } bool canHoldPiece() const { return canHold; } bool isGameOver() const { return gameOver; } bool isPaused() const { return paused; } void setPaused(bool p); GameMode getMode() const { return mode; } void setMode(GameMode m) { mode = m; } int score() const { return _score; } int lines() const { return _lines; } int level() const { return _level; } int challengeLevel() const { return challengeLevelIndex; } int asteroidsRemaining() const { return asteroidsRemainingCount; } int asteroidsTotal() const { return asteroidsTotalThisLevel; } bool isChallengeComplete() const { return challengeComplete; } bool isChallengeLevelActive() const { return challengeLevelActive; } bool isChallengeAdvanceQueued() const { return challengeAdvanceQueued; } int queuedChallengeLevel() const { return challengeQueuedLevel; } int consumeQueuedChallengeLevel(); // returns next level if queued, else 0 int startLevelBase() const { return startLevel; } double elapsed() const; // Now calculated from start time void updateElapsedTime(); // Update elapsed time from system clock bool isSoftDropping() const { return softDropping; } const std::array, COLS*ROWS>& asteroidCells() const { return asteroidGrid; } const std::vector& getRecentAsteroidExplosions() const { return recentAsteroidExplosions; } void clearRecentAsteroidExplosions() { recentAsteroidExplosions.clear(); } // Block statistics const std::array& getBlockCounts() const { return blockCounts; } // Line clearing effects support bool hasCompletedLines() const { return !completedLines.empty(); } const std::vector& getCompletedLines() const { return completedLines; } void clearCompletedLines(); // Actually remove the lines from the board // Sound effect callbacks using SoundCallback = std::function; // Callback for line clear sounds (number of lines) using LevelUpCallback = std::function; // Callback for level up sounds using AsteroidDestroyedCallback = std::function; // Callback when an asteroid is fully destroyed void setSoundCallback(SoundCallback callback) { soundCallback = callback; } void setLevelUpCallback(LevelUpCallback callback) { levelUpCallback = callback; } void setAsteroidDestroyedCallback(AsteroidDestroyedCallback callback) { asteroidDestroyedCallback = callback; } // Shape helper -------------------------------------------------------- static bool cellFilled(const Piece& p, int cx, int cy); // Gravity tuning accessors (public API for HUD/tuning) void setGravityGlobalMultiplier(double m) { gravityGlobalMultiplier = m; } double getGravityGlobalMultiplier() const; double getGravityMs() const; double getFallAccumulator() const { return fallAcc; } // Debug: time accumulated toward next drop void setLevelGravityMultiplier(int level, double m); // Visual effect hooks void updateVisualEffects(double frameMs); bool hasHardDropShake() const { return hardDropShakeTimerMs > 0.0; } double hardDropShakeStrength() const; const std::vector& getHardDropCells() const { return hardDropCells; } uint32_t getHardDropFxId() const { return hardDropFxId; } uint64_t getCurrentPieceSequence() const { return pieceSequence; } // Additional stats int tetrisesMade() const { return _tetrisesMade; } int maxCombo() const { return _maxCombo; } int comboCount() const { return _comboCount; } private: static constexpr int ASTEROID_BASE = 100; // sentinel offset for board encoding static constexpr int ASTEROID_MAX_LEVEL = 100; std::array board{}; // 0 empty else color index Piece cur{}, hold{}, nextPiece{}; // current, held & next piece bool canHold{true}; bool paused{false}; std::vector bag; // 7-bag randomizer std::mt19937 rng{ std::random_device{}() }; std::array blockCounts{}; // Count of each piece type used int _score{0}; int _lines{0}; int _level{1}; int _tetrisesMade{0}; int _currentCombo{0}; int _maxCombo{0}; int _comboCount{0}; double gravityMs{800.0}; double fallAcc{0.0}; Uint64 _startTime{0}; // Performance counter at game start Uint64 _pausedTime{0}; // Time spent paused (in performance counter ticks) Uint64 _lastPauseStart{0}; // When the current pause started bool gameOver{false}; int startLevel{0}; bool softDropping{false}; // true while player holds Down key // Line clearing support std::vector completedLines; // Rows that are complete and ready for effects // Sound effect callbacks SoundCallback soundCallback; LevelUpCallback levelUpCallback; AsteroidDestroyedCallback asteroidDestroyedCallback; // Gravity tuning ----------------------------------------------------- // Global multiplier applied to all level timings (use to slow/speed whole-game gravity) double gravityGlobalMultiplier{1.0}; // Gravity manager encapsulates frames table, multipliers and conversions GravityManager gravityMgr; // Backwards-compatible accessors (delegate to gravityMgr) double computeGravityMsForLevel(int level) const; // Impact FX timers double hardDropShakeTimerMs{0.0}; static constexpr double HARD_DROP_SHAKE_DURATION_MS = 320.0; std::vector hardDropCells; uint32_t hardDropFxId{0}; uint64_t pieceSequence{0}; // Challenge mode state ------------------------------------------------- GameMode mode{GameMode::Endless}; int challengeLevelIndex{1}; int asteroidsRemainingCount{0}; int asteroidsTotalThisLevel{0}; bool challengeComplete{false}; std::array, COLS*ROWS> asteroidGrid{}; uint32_t challengeSeedBase{0}; std::mt19937 challengeRng{ std::random_device{}() }; bool challengeLevelActive{false}; bool challengeAdvanceQueued{false}; int challengeQueuedLevel{0}; // Asteroid SFX latency mitigation std::optional pendingAsteroidDestroyType; bool asteroidDestroySoundPreplayed{false}; // Recent asteroid explosion positions (grid coords) for renderer FX std::vector recentAsteroidExplosions; // Internal helpers ---------------------------------------------------- void refillBag(); void spawn(); bool collides(const Piece& p) const; void lockPiece(); int checkLines(); // Find completed lines and store them void actualClearLines(); // Actually remove lines from board bool tryMoveDown(); // one-row fall; returns true if moved void clearAsteroidGrid(); void setupChallengeLevel(int level, bool preserveStats); void placeAsteroidsForLevel(int level); AsteroidType chooseAsteroidTypeForLevel(int level); AsteroidCell makeAsteroidForType(AsteroidType t) const; void handleAsteroidsOnClearedRows(const std::vector& clearedRows, std::array& outBoard, std::array, COLS*ROWS>& outAsteroids); void applyAsteroidGravity(); // Gravity tuning helpers (public API declared above) };