You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
5.4 KiB
189 lines
5.4 KiB
#pragma once |
|
#include <vector> |
|
#include <random> |
|
|
|
enum class PieceType { |
|
I = 0, O, T, S, Z, J, L, NONE |
|
}; |
|
|
|
struct Point { |
|
int x; |
|
int y; |
|
}; |
|
|
|
// Coordinates for all 7 Tetromino types in their 4 rotation states (0, 1, 2, 3) |
|
// Pivot anchor is (0,0). Screen space has Y-down, X-right. |
|
inline const Point TETROMINO_CELLS[7][4][4] = { |
|
// 0: I Piece |
|
{ |
|
{ {-1, 0}, {0, 0}, {1, 0}, {2, 0} }, // rot 0 |
|
{ {1, -1}, {1, 0}, {1, 1}, {1, 2} }, // rot 1 |
|
{ {-1, 1}, {0, 1}, {1, 1}, {2, 1} }, // rot 2 |
|
{ {0, -1}, {0, 0}, {0, 1}, {0, 2} } // rot 3 |
|
}, |
|
// 1: O Piece |
|
{ |
|
{ {0, 0}, {1, 0}, {0, 1}, {1, 1} }, // rot 0 |
|
{ {0, 0}, {1, 0}, {0, 1}, {1, 1} }, // rot 1 |
|
{ {0, 0}, {1, 0}, {0, 1}, {1, 1} }, // rot 2 |
|
{ {0, 0}, {1, 0}, {0, 1}, {1, 1} } // rot 3 |
|
}, |
|
// 2: T Piece |
|
{ |
|
{ {-1, 0}, {0, 0}, {1, 0}, {0, 1} }, // rot 0 |
|
{ {0, -1}, {0, 0}, {0, 1}, {-1, 0} }, // rot 1 |
|
{ {-1, 0}, {0, 0}, {1, 0}, {0, -1} }, // rot 2 |
|
{ {0, -1}, {0, 0}, {0, 1}, {1, 0} } // rot 3 |
|
}, |
|
// 3: S Piece |
|
{ |
|
{ {0, 0}, {1, 0}, {-1, 1}, {0, 1} }, // rot 0 |
|
{ {0, -1}, {0, 0}, {1, 0}, {1, 1} }, // rot 1 |
|
{ {0, -1}, {1, -1}, {-1, 0}, {0, 0} }, // rot 2 |
|
{ {-1, -1}, {-1, 0}, {0, 0}, {0, 1} } // rot 3 |
|
}, |
|
// 4: Z Piece |
|
{ |
|
{ {-1, 0}, {0, 0}, {0, 1}, {1, 1} }, // rot 0 |
|
{ {1, -1}, {1, 0}, {0, 0}, {0, 1} }, // rot 1 |
|
{ {-1, -1}, {0, -1}, {0, 0}, {1, 0} }, // rot 2 |
|
{ {0, -1}, {0, 0}, {-1, 0}, {-1, 1} } // rot 3 |
|
}, |
|
// 5: J Piece |
|
{ |
|
{ {-1, 0}, {0, 0}, {1, 0}, {-1, 1} }, // rot 0 |
|
{ {0, -1}, {0, 0}, {0, 1}, {-1, -1} }, // rot 1 |
|
{ {-1, 0}, {0, 0}, {1, 0}, {1, -1} }, // rot 2 |
|
{ {0, -1}, {0, 0}, {0, 1}, {1, 1} } // rot 3 |
|
}, |
|
// 6: L Piece |
|
{ |
|
{ {-1, 0}, {0, 0}, {1, 0}, {1, 1} }, // rot 0 |
|
{ {0, -1}, {0, 0}, {0, 1}, {-1, 1} }, // rot 1 |
|
{ {-1, 0}, {0, 0}, {1, 0}, {-1, -1} }, // rot 2 |
|
{ {0, -1}, {0, 0}, {0, 1}, {1, -1} } // rot 3 |
|
} |
|
}; |
|
|
|
enum class GameState { |
|
START, |
|
PLAYING, |
|
PAUSED, |
|
GAME_OVER |
|
}; |
|
|
|
class Game { |
|
public: |
|
static constexpr int BOARD_WIDTH = 10; |
|
static constexpr int BOARD_HEIGHT = 20; |
|
|
|
Game(); |
|
~Game() = default; |
|
|
|
void init(); |
|
void reset(); |
|
|
|
// Core game loop update (handles gravity, lock delay, game states) |
|
// Returns true if lines were cleared in this frame |
|
bool update(float dt); |
|
|
|
// Gameplay commands (inputs) |
|
bool moveLeft(); |
|
bool moveRight(); |
|
bool rotate(int dir); // dir = 1 (CW), -1 (CCW) |
|
void softDrop(); |
|
bool hardDrop(); // Returns true if piece locked |
|
void holdPiece(); |
|
|
|
// Query states |
|
GameState getState() const { return mState; } |
|
void setState(GameState state) { mState = state; } |
|
|
|
int getScore() const { return mScore; } |
|
int getLinesCleared() const { return mLinesCleared; } |
|
int getLevel() const { return mLevel; } |
|
int getCombo() const { return mCombo; } |
|
|
|
PieceType getCell(int x, int y) const { |
|
if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) return PieceType::NONE; |
|
return mBoard[y][x]; |
|
} |
|
|
|
PieceType getActivePieceType() const { return mActiveType; } |
|
int getActiveX() const { return mActiveX; } |
|
int getActiveY() const { return mActiveY; } |
|
int getActiveRotation() const { return mActiveRot; } |
|
|
|
// Relative cell coordinates of current falling block |
|
std::vector<Point> getActiveCells() const; |
|
|
|
// Absolute cell coordinates of where active piece would land (ghost projection) |
|
std::vector<Point> getGhostCells() const; |
|
|
|
PieceType getHoldPieceType() const { return mHoldType; } |
|
bool canHold() const { return mCanHold; } |
|
|
|
std::vector<PieceType> getNextQueue() const { return mNextQueue; } |
|
|
|
// Inter-thread game sound effects triggers |
|
bool flagMoveSFX = false; |
|
bool flagRotateSFX = false; |
|
bool flagLandSFX = false; |
|
bool flagLineClearSFX = false; |
|
bool flagTetrisClearSFX = false; |
|
bool flagLevelUpSFX = false; |
|
bool flagGameOverSFX = false; |
|
|
|
// Screen shake parameters |
|
float getScreenShakeIntensity() const { return mShakeIntensity; } |
|
void resetScreenShake() { mShakeIntensity = 0.0f; } |
|
|
|
// List of lines cleared on the current tick (for particle animations) |
|
std::vector<int> mClearedLinesThisTick; |
|
std::vector<Point> mLastLockedCells; |
|
|
|
private: |
|
GameState mState = GameState::START; |
|
|
|
// The grid: index y=0 is top, y=19 is bottom |
|
PieceType mBoard[BOARD_HEIGHT][BOARD_WIDTH]; |
|
|
|
// Falling piece details |
|
PieceType mActiveType = PieceType::NONE; |
|
int mActiveX = 0; |
|
int mActiveY = 0; |
|
int mActiveRot = 0; |
|
|
|
// Hold slot details |
|
PieceType mHoldType = PieceType::NONE; |
|
bool mCanHold = true; |
|
|
|
// 7-piece bag randomizer |
|
std::vector<PieceType> mNextQueue; |
|
std::vector<PieceType> mBag; |
|
std::mt19937 mRng; |
|
|
|
// Score metrics |
|
int mScore = 0; |
|
int mLinesCleared = 0; |
|
int mLevel = 1; |
|
int mCombo = -1; |
|
|
|
// Fall & Lock delay timers |
|
float mFallTimer = 0.0f; |
|
float mLockTimer = 0.0f; |
|
bool mIsLocking = false; |
|
float mShakeIntensity = 0.0f; |
|
|
|
float getFallDelay() const; |
|
|
|
void spawnPiece(); |
|
void fillBag(); |
|
PieceType popNextPiece(); |
|
|
|
bool checkCollision(const std::vector<Point>& cells, int dx, int dy) const; |
|
bool fits(PieceType type, int rot, int cx, int cy) const; |
|
|
|
void lockPiece(); |
|
void clearLines(); |
|
};
|
|
|