// Headers #include "includes/menu.h" #include "includes/getchar.h" #include "includes/board.h" #include "includes/cursorhide.h" #include "includes/structs.h" #include "includes/mechanic.h" #include "includes/history.h" #include #include #include #include #include using namespace std::chrono; // ----- Global variables ----- // Game components int board_lenght = 80; int board_width = 30; std::string board[30][80]; std::string hud[30][20]; int hudLength = 20; int hudWidth = 30; int velocitySpeed[3] = {60,70,80}; int bricksCount = 36; Paddle paddle; Ball ball; Player player; Brick bricks[36]; int bricks_idx[36], bricks_idy[36]; // Characters std::string ballChar = "\033[32m●\033[0m"; std::string block = "█"; std::string blockRed = "\033[31m█\033[0m"; std::string blockGreen= "\033[32m█\033[0m"; std::string blockBlue = "\033[34m█\033[0m"; std::string blockYellow = "\033[33m█\033[0m"; std::string trCorner = "\033[34m╗\033[0m"; std::string tlCorner = "\033[34m╔\033[0m"; std::string brCorner = "\033[34m╝\033[0m"; std::string blCorner = "\033[34m╚\033[0m"; std::string paddeleLine = "\033[32m╍\033[0m"; std::string horizontalLine = "\033[34m═\033[0m"; std::string verticalLine = "\033[34m║\033[0m"; // ----- Functions prototype ----- void setup(); void deallocation(); void locatePaddle(int x); void boardRender(); void ballMoveTask(); void endGame(); void inputThread(); // ----- Main function ----- std::atomic running(true); /* used in order to manage the thread and prevent threads racing (racing : a thread reading a variable created by another when it's not fully written) */ std::atomic paused(false); int main(){ using namespace std::chrono; show_console_cursor(false); // hides the cursor // Unicode settings system("chcp 65001"); // for showing the unicode characters in terminal // Menu while (true) { system("clear"); char opt = optionChoosenByUser(); switch (opt) { case '1': { // written in scope to maintain the threads setup(); // sets up the board running.store(true); system("clear"); std::cout << "\n\n Enter your name : "; std::cin >> player.name; system("clear"); std::cout << "\n\n PRESS ANY KEY TO START THE GAME. \n"; getch(); running = true; std::thread threadOne(boardRender); // creates a thread for board rendering std::thread threadTwo(ballMoveTask); // created a thread for ball movement std::thread threadThree(inputThread); system("clear"); while (running && !player.gameover && !player.won) { // small sleep for threads to finish their jobs std::this_thread::sleep_for(milliseconds(100)); } running = false; threadOne.join(); threadTwo.join(); threadThree.detach(); if (player.gameover || player.won) { system("clear"); endGame(); std::cout << "\n\n Press enter twice to return to main menu\n"; getch(); saveData(player.name, player.score); } } break; case '2': helpMenu(); break; case '3': showHistory(); break; case '4': system("clear"); std::cout << std::endl; std::cout << " Press \"c\" to confirm\n"; char confirmChar = getch(); if (confirmChar == 'c'){ system("clear"); return 0; } system("clear"); break; } } } // ----- Functions ----- void locatePaddle(int x){ // receives starting point x because y stays the same board[board_width - 2][x] = paddeleLine; } // setup the board when starting new game void setup(){ /* only the starting char of bricks and the paddle change. because it would be easier to layout them. */ // clearing board for (int i = 0; i < board_width; i++){ for (int j = 0; j < board_lenght; j++) { board[i][j] = " "; } } // setting up variables player.gameover = false; player.won = false; player.score = 0; player.health = 3; bricksCount = 36; // setting up bricks /* How it works : it goes in a row and when ever it reaches the start of a brick it places that into the bricks_idx and does the same thing for the y. Note : x and y in this function are swapped compared to cartesian system. */ int counterX = 1, counterY = 10; for(int i = 0; i < 36; i++){ // saving the location of each brick bricks[i].loc.x = counterX; bricks[i].loc.y = counterY; bricks_idx[i] = counterX; bricks_idy[i] = counterY; counterY += 5; if(i == 11 || i == 23){ // 11 is where firsto row of bricks end and 23 is end of the second row counterY = 10; counterX++; } } // setting up brick scores /* first row(index 0 - 11) : 1000 second row(index 12 - 23) : 500 third row(index 24 - 35) : 200 */ for(int i = 0; i < 12; i++){ bricks[i].score = 1000; } for(int i = 12; i < 24; i++){ bricks[i].score = 500; } for(int i = 24; i < 36; i++){ bricks[i].score = 200; } // setting up borders for(int i = 1; i < board_lenght - 1; i++){ board[0][i] = horizontalLine; // top board[board_width - 1][i] = horizontalLine; // bottom } for(int i = 1; i < board_width - 1; i++){ board[i][0] = verticalLine; // right board[i][board_lenght - 1] = verticalLine; // left } // setting up corners board[0][0] = tlCorner; board[0][board_lenght - 1] = trCorner; board[board_width - 1][0] = blCorner; board[board_width - 1][board_lenght - 1] = brCorner; // setting up bricks for(int i = 1; i <= 3; i++){ for(int j = 1; j < board_lenght - 1; j++){ if(isBrick(i, j)){ board[i][j] = block; j += 4; } } } // setup paddle paddle.start_loc.x = (board_lenght - 10) / 2 - 1; paddle.start_loc.y = board_width - 2; locatePaddle(paddle.start_loc.x); // setup ball : locations are chosen such that the ball hits the paddle initially ball.loc.x = 22; ball.loc.y = 15; /* Note : the velocity below is not suitable for configuring the speed in order to change the speed, change the value of interval in ballMoveTask changing the velocity below would affect the ball collision angle.*/ ball.v.vX = 1; ball.v.vY = 1; board[ball.loc.y][ball.loc.x] = ballChar; } // Board rendering void boardRender() { auto interval = milliseconds(16); auto next_time = steady_clock::now(); while (running) { next_time += interval; drawBoard(); std::this_thread::sleep_until(next_time); } } // Input processing thread void inputThread() { while (running && !player.gameover && !player.won) { int q = inputProccessing(paddle); if (q == 0) { running = false; break; } } } // Ball rendering void ballMoveTask(){ auto next_time = steady_clock::now(); while (running) { auto interval = milliseconds(velocitySpeed[abs(ball.v.vX) - 1]); next_time += interval; if (!paused){ ballmover(ball); if (player.gameover || player.won){ running = false; // closes the threads break; } } std::this_thread::sleep_until(next_time); } } void endGame(){ // Reporting if (player.gameover){ std::cout << "\n\n Game over.\n"; }else { std::cout << "\n\n Game finished. \n Congrats! You Won!"; } std::cout << "\n Your score : "; std::cout << player.score; }