#include "TextApp.h" #include "../Core/Menu.h" #include #include #include #include #include #include #include static const int FRAMES_PER_INPUT = FRAMES_PER_SECOND / 2; // the number of frames that will pass everytime the player gives an input static const int MAXIMUM_PIECE_SIZE = 10; // the maximum size of pieces that will be loaded and utilizable static const int DEFAULT_PIECE_SIZE = 4; // the default size of pieces that will be selected when first running the app static const int MAXIMUM_BOARD_WIDTH = 30; // the maximum selectable width of the board static const int MAXIMUM_BOARD_HEIGHT = 40; // the maximum selectable height of the board static const Gamemode DEFAULT_GAMEMODE = SPRINT; // the gamemode that will be used when starting a new game TextApp::TextApp() { this->defaultKeybinds(); this->gameMenu.getPiecesList().loadPieces(MAXIMUM_PIECE_SIZE); this->gameMenu.getPiecesList().selectAllPieces(DEFAULT_PIECE_SIZE); this->gameMenu.getPlayerControls().setDAS(FRAMES_PER_INPUT); this->gameMenu.getPlayerControls().setARR(FRAMES_PER_INPUT); this->gameMenu.getPlayerControls().setSDR(0); } void TextApp::run() { bool quit = false; while (!quit) { std::cout << "\n\n\n"; std::cout << "===| WELCOME TO JMINOS! |===" << std::endl; std::cout << "1- Change pieces" << std::endl; std::cout << "2- Change board" << std::endl; std::cout << "3- See controls" << std::endl; std::cout << "4- Start game" << std::endl; std::cout << "5- Quit" << std::endl; std::cout << "Choice: "; std::string answer; std::getline(std::cin, answer); int selectedAnswer = 0; try { selectedAnswer = std::stoi(answer); } catch (std::exception ignored) {} switch (selectedAnswer) { case 1 : {this->choosePieces(); break;} case 2 : {this->chooseBoardSize(); break;} case 3 : {this->seeKeybinds(); break;} case 4 : {this->startGame(); break;} case 5 : {quit = true; break;} default : std::cout << "Invalid answer!" << std::endl; } } std::cout << "===| SEE YA NEXT TIME! |===" << std::endl; } void TextApp::choosePieces() { std::cout << "\n\n\n"; std::cout << "Choose which piece sizes to play with (from 1 to " << MAXIMUM_PIECE_SIZE << "), separate mutltiple sizes with blank spaces." << std::endl; std::cout << "Choice: "; std::string answer; std::getline(std::cin, answer); this->gameMenu.getPiecesList().unselectAll(); std::cout << "Selected pieces of sizes:"; std::stringstream answerStream(answer); for (std::string size; std::getline(answerStream, size, ' ');) { try { int selectedSize = std::stoi(size); if (selectedSize >= 1 && selectedSize <= MAXIMUM_PIECE_SIZE) { if (this->gameMenu.getPiecesList().selectAllPieces(selectedSize)) { std::cout << " " << selectedSize; } } } catch (std::exception ignored) {} } std::string waiting; std::getline(std::cin, waiting); } void TextApp::chooseBoardSize() { std::string answer; std::cout << "\n\n\n"; std::cout << "Current board width and height: " << this->gameMenu.getBoardWidth() << "x" << this->gameMenu.getBoardHeight() << std::endl; std::cout << "Choose the width of the board (from 1 to " << MAXIMUM_BOARD_WIDTH << ")." << std::endl; std::cout << "Choice: "; std::getline(std::cin, answer); try { int selectedSize = std::stoi(answer); if (selectedSize >= 1 && selectedSize <= MAXIMUM_BOARD_WIDTH) { this->gameMenu.setBoardWidth(selectedSize); } } catch (std::exception ignored) {} std::cout << "Choose the height of the board (from 1 to " << MAXIMUM_BOARD_HEIGHT << ")." << std::endl; std::cout << "Choice: "; std::getline(std::cin, answer); try { int selectedSize = std::stoi(answer); if (selectedSize >= 1 && selectedSize <= MAXIMUM_BOARD_HEIGHT) { this->gameMenu.setBoardHeight(selectedSize); } } catch (std::exception ignored) {} std::cout << "New board width and height: " << this->gameMenu.getBoardWidth() << "x" << this->gameMenu.getBoardHeight(); std::string waiting; std::getline(std::cin, waiting); } void TextApp::seeKeybinds() const { std::cout << "\n\n\n"; std::cout << "Quit/Pause/Retry: quit/pause/retry" << std::endl; std::cout << "Hold : h" << std::endl; std::cout << "Soft/Hard drop : sd/hd" << std::endl; std::cout << "Move left/right : l/r" << std::endl; std::cout << "Rotate 0/CW/180/CCW: c/cw/cc/ccw" << std::endl; std::cout << "\n"; std::cout << "To do several actions at the same time, separe them with blank spaces." << std::endl; std::string waiting; std::getline(std::cin, waiting); } void TextApp::defaultKeybinds() { this->keybinds.clear(); this->keybinds.insert({"pause", PAUSE}); this->keybinds.insert({"retry", RETRY}); this->keybinds.insert({"h", HOLD}); this->keybinds.insert({"sd", SOFT_DROP}); this->keybinds.insert({"hd", HARD_DROP}); this->keybinds.insert({"l", MOVE_LEFT}); this->keybinds.insert({"r", MOVE_RIGHT}); this->keybinds.insert({"c", ROTATE_0}); this->keybinds.insert({"cw", ROTATE_CW}); this->keybinds.insert({"cc", ROTATE_180}); this->keybinds.insert({"ccw", ROTATE_CCW}); } void TextApp::startGame() const { Game game = this->gameMenu.startGame(DEFAULT_GAMEMODE); game.start(); std::cout << "\n\n\n"; this->printGame(game); bool quit = false; bool paused = false; std::string answer; while (!quit) { std::cout << "Actions: "; std::getline(std::cin, answer); std::stringstream answerStream(answer); std::vector actions; for (std::string action; std::getline(answerStream, action, ' ');) { actions.push_back(action); } std::set playerActions; std::set lastFrameActions; bool retrying = false; for (std::string action : actions) { if (action == "quit") { quit = true; } else { try { Action playerAction = this->keybinds.at(action); if (playerAction == RETRY) { retrying = true; } else if (playerAction == PAUSE) { paused = (!paused); } else if (playerAction == SOFT_DROP || playerAction == MOVE_LEFT || playerAction == MOVE_RIGHT) { playerActions.insert(playerAction); lastFrameActions.insert(playerAction); } else if (playerAction == HOLD || playerAction == HARD_DROP) { lastFrameActions.insert(playerAction); } else { playerActions.insert(playerAction); } } catch (std::exception ignored) {} } } if (!paused && !quit) { if (retrying) { game.reset(); game.start(); } else { for (int i = 0; i < (FRAMES_PER_INPUT - 1); i++) { game.nextFrame(playerActions); } game.nextFrame(lastFrameActions); } } if (!quit) { std::cout << "\n\n\n"; if (paused) { std::cout << "--<[PAUSED]>--" << std::endl; } this->printGame(game); } if (game.hasLost()) { quit = true; std::cout << "You lost!" << std::endl; } else if (game.hasWon()) { quit = true; std::cout << "You won!" << std::endl; } } } void TextApp::printGame(const Game& game) const { int maxHeight = game.getBoard().getGridHeight(); if (game.getActivePiece() != nullptr) { for (const Position& position : game.getActivePiece()->getPositions()) { maxHeight = std::max(maxHeight, position.y + game.getActivePiecePosition().y); } } bool lineCountPrinted = false; bool holdBoxStartedPrinting = false; bool holdBoxFinishedPrinting = false; bool nextQueueStartedPrinting = false; bool nextQueueFinishedPrinting = false; int nextQueuePrintedPiece; int printedPieceLineHeight; for (int y = maxHeight; y >= 0; y--) { for (int x = 0; x < game.getBoard().getWidth(); x++) { /* BOARD PRINTING */ bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position(x, y) - game.getActivePiecePosition())); bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position(x, y) - game.getGhostPiecePosition())); Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position(x, y)); if (isActivePieceHere || isGhostPieceHere) { std::cout << getConsoleColorCode(block); } else { std::cout << getResetConsoleColorCode(); } if (block != NOTHING && (!(isGhostPieceHere && !isActivePieceHere))) { std::cout << "*"; } else { if (y < game.getBoard().getBaseHeight()) { if (isGhostPieceHere) { std::cout << "="; } else { std::cout << "-"; } } else { std::cout << " "; } } } if (y < game.getBoard().getGridHeight()) { /* SIDEBAR PRINTING */ std::cout << " "; if (!lineCountPrinted) { std::cout << getResetConsoleColorCode() << "Lines: " << game.getClearedLines(); lineCountPrinted = true; } else if (!holdBoxFinishedPrinting) { if (!holdBoxStartedPrinting) { std::cout << getResetConsoleColorCode() << "Hold:"; printedPieceLineHeight = (game.getHeldPiece() == nullptr) ? -1 : (game.getHeldPiece()->getLength() - 1); holdBoxStartedPrinting = true; } else { for (int i = 0; i < game.getHeldPiece()->getLength(); i++) { if (game.getHeldPiece()->getPositions().contains(Position(i, printedPieceLineHeight))) { std::cout << getConsoleColorCode(game.getHeldPiece()->getBlockType()) << "*"; } else { std::cout << getResetConsoleColorCode() << "-"; } } printedPieceLineHeight--; } if (printedPieceLineHeight < 0) { holdBoxFinishedPrinting = true; } } else if (!nextQueueFinishedPrinting) { if (!nextQueueStartedPrinting) { std::cout << getResetConsoleColorCode() << "Next:"; printedPieceLineHeight = (game.getNextPieces().size() == 0) ? -1 : (game.getNextPieces().at(0).getLength() - 1); nextQueuePrintedPiece = 0; nextQueueStartedPrinting = true; } else { for (int i = 0; i < game.getNextPieces().at(nextQueuePrintedPiece).getLength(); i++) { if (game.getNextPieces().at(nextQueuePrintedPiece).getPositions().contains(Position(i, printedPieceLineHeight))) { std::cout << getConsoleColorCode(game.getNextPieces().at(nextQueuePrintedPiece).getBlockType()) << "*"; } else { std::cout << getResetConsoleColorCode() << "-"; } } printedPieceLineHeight--; } if (printedPieceLineHeight < 0) { nextQueuePrintedPiece++; if (nextQueuePrintedPiece >= game.getNextPieces().size()) { nextQueueFinishedPrinting = true; } else { printedPieceLineHeight = game.getNextPieces().at(nextQueuePrintedPiece).getLength() - 1; } } } } std::cout << "\n"; } std::cout << getResetConsoleColorCode(); }