fixed game logic

This commit is contained in:
2025-03-23 21:41:37 +01:00
parent 507bc9cc86
commit 38008e00bc
7 changed files with 90 additions and 59 deletions

View File

@@ -19,7 +19,7 @@ If you need to change the toolchain (for example using gcc):
### Run the project ### Run the project
``xmake run`` ``xmake run``
Note that the program will genereate the polyomino files for you. This can be quite long so it only does it up to size 10. Note that the program will generate the polyomino files for you. This can be quite long so it only does it up to size 10.
If for some reasons you wanna run the command line version: If for some reasons you wanna run the command line version:
``xmake run text`` ``xmake run text``

View File

@@ -77,8 +77,9 @@ Since this game uses polyomino of high sizes which are very unplayable, we will
5. Do the same as step 4 but now we move the piece one line up every time 5. Do the same as step 4 but now we move the piece one line up every time
6. Cancel the rotation 6. Cancel the rotation
Kicking is primarly designed for rotating, but it is also applied when a piece spawns into a wall. _Note: at step 3, the direction which is checked first is actually the last movement done by the player._
0° rotations will first try to move the piece one position down, and if it can't try kicking the piece, only registering a kick if the piece couldn't move down. Kicking is primarly designed for rotating, but there is also a 0° rotation applied when a piece spawns into a wall.
0° rotations will first try to move the piece one position down, and if it can't, try kicking the piece, only registering a kick if the piece couldn't move down.
## Detecting spins ## Detecting spins

View File

@@ -55,6 +55,8 @@ void Game::initialize() {
void Game::nextFrame(const std::set<Action>& playerActions) { void Game::nextFrame(const std::set<Action>& playerActions) {
if (this->lost || this->hasWon()) return; if (this->lost || this->hasWon()) return;
bool pieceJustLocked = false;
if (this->started) { if (this->started) {
bool AREJustEnded = (this->leftARETime == 1); bool AREJustEnded = (this->leftARETime == 1);
if (this->leftARETime > 0) { if (this->leftARETime > 0) {
@@ -84,13 +86,14 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
if (this->lost) { if (this->lost) {
if (initialRotation == NONE) { if (initialRotation == NONE) {
this->lost = (!this->board.rotate(initialRotation)); this->board.rotate(NONE);
} this->lost = this->board.activePieceInWall();
}
if (this->lost) { if (this->lost) {
this->framesPassed++; this->framesPassed++;
return; return;
} }
}
}
/* HOLD */ /* HOLD */
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) { if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
@@ -158,6 +161,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
this->score += HARD_DROP_SCORE; this->score += HARD_DROP_SCORE;
} }
this->lockPiece(); this->lockPiece();
pieceJustLocked = true;
} }
// no need to apply gravity and lock delay if the piece was hard dropped // no need to apply gravity and lock delay if the piece was hard dropped
else { else {
@@ -187,6 +191,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
if ((this->totalLockDelay > this->parameters.getLockDelay()) || (this->totalForcedLockDelay > this->parameters.getForcedLockDelay())) { if ((this->totalLockDelay > this->parameters.getLockDelay()) || (this->totalForcedLockDelay > this->parameters.getForcedLockDelay())) {
this->lockPiece(); this->lockPiece();
pieceJustLocked = true;
} }
} }
@@ -201,12 +206,12 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
} }
} }
this->heldActions = playerActions;
if ((!this->started) || this->leftARETime > 0) { if ((!this->started) || this->leftARETime > 0) {
for (Action action : playerActions) { for (Action action : playerActions) {
if ((!pieceJustLocked) && (!heldActions.contains(action))) {
this->initialActions.insert(action); this->initialActions.insert(action);
} }
}
if (this->heldDAS >= 0) { if (this->heldDAS >= 0) {
if (playerActions.contains(MOVE_LEFT)) this->heldDAS = -1; if (playerActions.contains(MOVE_LEFT)) this->heldDAS = -1;
@@ -219,6 +224,8 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
else this->heldDAS = 0; else this->heldDAS = 0;
} }
} }
this->heldActions = playerActions;
} }
void Game::resetPiece(bool newPiece) { void Game::resetPiece(bool newPiece) {
@@ -302,6 +309,11 @@ void Game::lockPiece() {
if (this->leftARETime == 0) { if (this->leftARETime == 0) {
this->lost = this->board.spawnNextPiece(); this->lost = this->board.spawnNextPiece();
this->resetPiece(true); this->resetPiece(true);
if (this->lost) {
this->board.rotate(NONE);
this->lost = this->board.activePieceInWall();
}
} }
} }
} }

View File

@@ -36,9 +36,12 @@ void GameBoard::initialize() {
this->activePiece = nullptr; this->activePiece = nullptr;
this->heldPiece = nullptr; this->heldPiece = nullptr;
this->isLastMoveKick = false; this->isLastMoveKick = false;
this->movedLeftLast = false;
} }
bool GameBoard::moveLeft() { bool GameBoard::moveLeft() {
this->movedLeftLast = true;
if (this->activePieceInWall(Position{-1, 0})) { if (this->activePieceInWall(Position{-1, 0})) {
return false; return false;
} }
@@ -50,6 +53,8 @@ bool GameBoard::moveLeft() {
} }
bool GameBoard::moveRight() { bool GameBoard::moveRight() {
this->movedLeftLast = false;
if (this->activePieceInWall(Position{1, 0})) { if (this->activePieceInWall(Position{1, 0})) {
return false; return false;
} }
@@ -137,30 +142,21 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePos
bool overlapsRight = true; bool overlapsRight = true;
int i = (j == 0) ? 1 : 0; int i = (j == 0) ? 1 : 0;
do { do {
// check right before left arbitrarly, we don't decide this with rotations since it would still be arbitrary with 180° rotations // we first check the side to which the player moved last
if (overlapsRight) { if (movedLeftLast) {
Position shift{+i, j};
if (!this->activePieceOverlaps(safePositions, shift)) {
overlapsLeft = false;
}
else {
if (!this->activePieceInWall(shift)) {
this->activePiecePosition += shift;
return true;
}
}
}
if (overlapsLeft) { if (overlapsLeft) {
Position shift{-i, j}; if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true;
if (!this->activePieceOverlaps(safePositions, shift)) { }
overlapsLeft = false; if (overlapsRight) {
if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true;
}
} }
else { else {
if (!this->activePieceInWall(shift)) { if (overlapsRight) {
this->activePiecePosition += shift; if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true;
return true;
} }
if (overlapsLeft) {
if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true;
} }
} }
@@ -177,6 +173,26 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePos
return false; return false;
} }
bool GameBoard::tryFittingKickedPiece(const std::set<Position>& safePositions, const Position& shift, bool& overlaps) {
if (!this->activePieceOverlaps(safePositions, shift)) {
overlaps = false;
}
else {
if (!this->activePieceInWall(shift)) {
this->activePiecePosition += shift;
return true;
}
}
return false;
}
bool GameBoard::activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift) const {
for (Position position : this->activePiece->getPositions()) {
if (safePositions.contains(position + this->activePiecePosition + shift)) return true;
}
return false;
}
bool GameBoard::hold(Rotation initialRotation) { bool GameBoard::hold(Rotation initialRotation) {
std::swap(this->activePiece, this->heldPiece); std::swap(this->activePiece, this->heldPiece);
@@ -236,6 +252,13 @@ bool GameBoard::spawnNextPiece() {
return this->activePieceInWall(); return this->activePieceInWall();
} }
bool GameBoard::activePieceInWall(const Position& shift) const {
for (Position position : this->activePiece->getPositions()) {
if (this->board.getBlock(position + this->activePiecePosition + shift) != NOTHING) return true;
}
return false;
}
bool GameBoard::touchesGround() const { bool GameBoard::touchesGround() const {
return this->activePieceInWall(Position{0, -1}); return this->activePieceInWall(Position{0, -1});
} }
@@ -257,6 +280,8 @@ LineClear GameBoard::lockPiece() {
this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType()); this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType());
} }
this->activePiece = nullptr;
return LineClear{this->board.clearRows(), isLockedInPlace, (!isLockedInPlace) && this->isLastMoveKick}; return LineClear{this->board.clearRows(), isLockedInPlace, (!isLockedInPlace) && this->isLastMoveKick};
} }
@@ -291,20 +316,6 @@ const std::vector<Piece>& GameBoard::getNextPieces() const {
return this->nextQueue; return this->nextQueue;
} }
bool GameBoard::activePieceInWall(const Position& shift) const {
for (Position position : this->activePiece->getPositions()) {
if (this->board.getBlock(position + this->activePiecePosition + shift) != NOTHING) return true;
}
return false;
}
bool GameBoard::activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift) const {
for (Position position : this->activePiece->getPositions()) {
if (safePositions.contains(position + this->activePiecePosition + shift)) return true;
}
return false;
}
void GameBoard::goToSpawnPosition() { void GameBoard::goToSpawnPosition() {
int lowestPosition = this->activePiece->getLength() - 1; int lowestPosition = this->activePiece->getLength() - 1;
for (Position position : this->activePiece->getPositions()) { for (Position position : this->activePiece->getPositions()) {

View File

@@ -22,6 +22,7 @@ class GameBoard {
int nextQueueLength; // the number of next pieces seeable at a time int nextQueueLength; // the number of next pieces seeable at a time
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
bool isLastMoveKick; // wheter the last action the piece did was kicking bool isLastMoveKick; // wheter the last action the piece did was kicking
bool movedLeftLast; // wheter the last sideway movement was a left one
public: public:
/** /**
@@ -72,6 +73,18 @@ class GameBoard {
*/ */
bool tryKicking(bool testingBottom, const std::set<Position>& safePositions); bool tryKicking(bool testingBottom, const std::set<Position>& safePositions);
/**
* Tries fitting the kicked active piece at the specified position
* @return If it suceeded
*/
bool tryFittingKickedPiece(const std::set<Position>& safePositions, const Position& shift, bool& overlaps);
/**
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
* @return If the shifted active piece overlaps with one of the position
*/
bool activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const;
public: public:
/** /**
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece * Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
@@ -85,6 +98,12 @@ class GameBoard {
*/ */
bool spawnNextPiece(); bool spawnNextPiece();
/**
* Checks if one of the active piece's positions touches a wall in the board
* @return If the active piece is in a wall
*/
bool activePieceInWall(const Position& shift = Position{0, 0}) const;
/** /**
* Checks is the active piece as a wall directly below one of its position * Checks is the active piece as a wall directly below one of its position
* @return If it touches a ground * @return If it touches a ground
@@ -134,18 +153,6 @@ class GameBoard {
const std::vector<Piece>& getNextPieces() const; const std::vector<Piece>& getNextPieces() const;
private: private:
/**
* Checks if one of the active piece's positions touches a wall in the board
* @return If the active piece spawned in a wall
*/
bool activePieceInWall(const Position& shift = Position{0, 0}) const;
/**
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
* @return If the shifted active piece overlaps with one of the position
*/
bool activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const;
/** /**
* Sets the active piece to its spawn position * Sets the active piece to its spawn position
*/ */

View File

@@ -38,7 +38,7 @@ void GameParameters::clearLines(int lineNumber) {
// level increments every 10 lines, stats only changes on level up // level increments every 10 lines, stats only changes on level up
if (previousLines / 10 < this->clearedLines / 10) { if (previousLines / 10 < this->clearedLines / 10) {
this->level = this->clearedLines / 10; this->level += (this->clearedLines / 10 - previousLines / 10);
this->updateStats(); this->updateStats();
} }
break; break;

View File

@@ -74,7 +74,7 @@ void GameSettingsAppMenu::drawFrame() const {
this->placeText(text, "ULTRA", 50.f, 25.f, 2, 1); this->placeText(text, "ULTRA", 50.f, 25.f, 2, 1);
this->placeText(text, "MASTER", 5.f, 35.f, 0, 2); this->placeText(text, "MASTER", 5.f, 35.f, 0, 2);
this->placeText(text, "TODO", 25.f, 35.f, 1, 2); this->placeText(text, "TODO", 25.f, 35.f, 1, 2);
this->placeText(text, "TOOD", 50.f, 35.f, 2, 2); this->placeText(text, "TODO", 50.f, 35.f, 2, 2);
this->renderWindow->display(); this->renderWindow->display();
} }