345 lines
13 KiB
C++
345 lines
13 KiB
C++
#include "TextApp.h"
|
|
|
|
#include "../Core/Menu.h"
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <exception>
|
|
|
|
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<std::string> actions;
|
|
for (std::string action; std::getline(answerStream, action, ' ');) {
|
|
actions.push_back(action);
|
|
}
|
|
|
|
std::set<Action> playerActions;
|
|
std::set<Action> 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();
|
|
}
|