fixed game logic
This commit is contained in:
@@ -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``
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user