// ----- Headers ----- #include #include #include #include #include #include #include #include #include #include using namespace std; // ------- COLORS --------- #define RESET "\033[0m" #define RED "\033[31m" #define GREEN "\033[32m" #define CYAN "\033[36m" #define BLUE "\033[34m" #define YELLOW "\033[33m" #define MAGENTA "\033[35m" #define WHITE "\033[37m" #define BRIGHTBLACK "\033[90m" #define BRIGHTBLUE "\033[94m" #define BRIGHTRED "\033[91m" #define ORANGE "\033[38;5;202m" #define PINK "\033[38;5;201m" #define PURPLE "\033[38;5;93m" #define BLACK "\033[30m" #define LIGHTBLUE "\033[94m" #define LIGHTMAGENTA "\033[95m" #define BOLDYELLOW "\033[1;33m" #define BRIGHTMAGENTA "\033[95m" #define GOLD "\033[93m" // ========== rendering =========== // ----- Structs ----- struct paddelInfo { float x; int y; int size; int speed; }; struct ballInfo { float x; float y; float i_speed; float j_speed; bool stuckToPaddle; }; struct ThemeStyle { string borderColor; string paddleColor; string simpleBlockColor; string hardBlockColor; string ballColor; string cursorColor; string topicColor; string ballChar; string cursorChar; string topicChar; string paddleChar; string blockChar; }; struct RandomItem { int x, y; int power; bool active; }; ThemeStyle CurrentTheme; paddelInfo paddel = {10.0f, 18, 5, 1}; ballInfo ball = {10.0f, 17.0f, 0.0f, 0.0f, false}; RandomItem Items[12]; // ----- Glubal variables ----- char board[19][20]; int heal = 3; int score = 0; int bestscore = 0; string player; int simpleBlocksDestroyed = 0; int hardBlocksDestroyed = 0; string WinOrLose; int Theme = -1; int level = -1; clock_t slowStartTime = 0; clock_t slowCooldownTime = 0; const int slowDuration = 5; // seconds const int slowCooldown = 15; // seconds int normalBallDelay = 65; int slowBallDelay = 110; int ballDelay = 65; int cooldownRemaining = 0; clock_t powerOn; clock_t now; bool pKeyPressed = false; bool Epressed = false; bool paused = false; int length = (paddel.size-1)/2; bool start = false; bool Isstarted = false; bool LoadGameAvailable = false; bool needRedraw = true; bool slowActive = false; bool difaultTheme = true; bool difaultlevel = true; // ----- Function Declarations ----- void initialSituation(); void DrawBoard(); void startGame (); int Menu(); void inputHandeling(); void timing(); void WaitForKeyRelease(); void HideCursor(); void drawGameover(int choice); int GameOverMenu(); string getPlayerName(); void drawBoxAnimation(int x, int y, int w, int h); void saveGame(const string& player); void clearSaveGame(); bool loadGame(string& player); void saveHistory(const string& player, int score); int extractScore(const string& line); void showHistory(); void gotoxy(int x, int y); void ClearInputBuffer(); void displayInform(); void DrawHelpItems(); void DrawSetting(); void DeterminThemeAnfLevel(); void PlayBreakoutIntro(); void creatrandomItems(); void ActivateRandomItem(int bx, int by); void DrawRandomItems(); void ApplyPower(int power); void RemovePower(); void HandleBlockDestruction(int x, int y); void UpdateBall(); int main() { SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); HideCursor(); int choice = 0; DeterminThemeAnfLevel(); while (true) { int selected = Menu(); if (selected == 0) { // New Game system("cls"); needRedraw = true; Isstarted = false; player = getPlayerName(); system("cls"); WaitForKeyRelease(); ClearInputBuffer(); startGame(); needRedraw = false; } else if (selected == 1 && LoadGameAvailable) { // Load Game system("cls"); gotoxy(25, 13); cout << RED "__1__" RESET ; Sleep(500); system("cls"); gotoxy(25, 13); cout << RED "__2__" RESET ; Sleep(500); system("cls"); gotoxy(25, 13); cout << RED "__3__" RESET ; Sleep(200); system("cls"); if (loadGame(player)) { Isstarted = true; if (ball.i_speed == 0 && ball.j_speed == 0) { start = false; } else { start = true; } startGame(); } else { system("cls"); cout << RED << "No saved game found!" << RESET; Sleep(1500); } } else if (selected == 2) { // Setting DrawSetting(); DeterminThemeAnfLevel(); } else if (selected == 3) { // History showHistory(); } else if (selected == 4) { // Help system("cls"); DrawHelpItems(); // wait for exit while (true) { ClearInputBuffer(); char key = _getch(); if (key == 69 || key == 101) { // E ClearInputBuffer(); break; } Sleep(50); } } else if (selected == 5) { // exit clearSaveGame(); break; } } } int Menu() { PlayBreakoutIntro(); string options[] = { "New Game", "Load Game", "Setting ⚙️", "Game History 🗂️", "Help", "Exit" }; gotoxy(10,9); cout << BRIGHTBLUE << "│" << RESET << YELLOW << " Welcome to Breakout" << RESET; gotoxy(10,10); cout << BRIGHTBLUE << "│" << RESET << YELLOW << " Use ▲ ▼ to Move Up & Down" << RESET; drawBoxAnimation(40, 8, 29, 12); int selected = 0; while (true) { for (int i = 0; i < 6; i++) { gotoxy(47, 11 + i); // Load Game if (i == 1 && !LoadGameAvailable) { cout << BRIGHTBLACK << " " << options[i] << RESET; continue; } if (i == selected) cout << CurrentTheme.cursorColor << "> " << RESET << WHITE << options[i] << RESET; else cout << " " << options[i]; } char key = _getch(); // UP if (key == 72 && selected > 0) { selected--; if (selected == 1 && !LoadGameAvailable) selected--; } // DOWN if (key == 80 && selected < 5) { selected++; if (selected == 1 && !LoadGameAvailable) selected++; } // ENTER if (key == 13) { if (selected == 1 && !LoadGameAvailable) continue; return selected; } } } void startGame() { bool pKeyPressed = false; bool paused = false; int length = (paddel.size-1)/2; if(!Isstarted && needRedraw) { for (int x = 0 ; x < 5 ; x++) for (int y = 0 ; y < 20; y++) { if (y == 0 || y == 19) board[x][y] = ' '; else{ if(level == 6) board[x][y] = rand() %2 == 0 ? '1' : '2'; else if(level == 7) board[x][y] = '2'; } } for (int x = 5 ; x < 19; x++) for (int y = 0 ; y < 20 ; y++) board[x][y] = ' '; paddel.x = 10.0f; paddel.y = 18; heal = 3; simpleBlocksDestroyed= 0; hardBlocksDestroyed = 0; score = 0; } if (needRedraw) { ball.x = 10.0f; ball.y = 17.0f; ball.i_speed = 0.0f; ball.j_speed = 0.0f; } DrawBoard(); WaitForKeyRelease(); ClearInputBuffer(); while (true) { displayInform(); // Dynamic Info clock_t frameStart = clock(); // --- MOVE PADDLE --- if (GetAsyncKeyState('A') & 0x8000 && !paused) { if (paddel.x - (float)length >= 0.0f) { paddel.x -= paddel.speed; if (!start) ball.x = paddel.x; DrawBoard(); } } if (GetAsyncKeyState('D') & 0x8000 && !paused) { if (paddel.x + (float)length <= 19.0f) { paddel.x += paddel.speed; if (!start) ball.x = paddel.x; DrawBoard(); } } // --- START GAME --- if (GetAsyncKeyState(VK_SPACE) & 0x8000 && !start && !paused) { start = true; ball.i_speed = (rand() % 2 == 0) ? 1.0f : -1.0f; ball.j_speed = -1.0f; DrawBoard(); } // --- EXIT GAME --- if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) { if (Isstarted) { needRedraw = false; saveGame(player); LoadGameAvailable = true; } } if (GetAsyncKeyState('P') & 0x8000) { if (!pKeyPressed) { paused = !paused; start = !paused; if(!Isstarted && paused) { start = false; paused = false; } pKeyPressed = true; } } else { pKeyPressed = false; } if (GetAsyncKeyState('E') & 0x8000) { Isstarted = false; start = false; WaitForKeyRelease(); ClearInputBuffer(); break; } clock_t now = clock(); if ((GetAsyncKeyState(VK_RETURN) & 0x0001) && !slowActive && (now - slowCooldownTime) >= slowCooldown * CLOCKS_PER_SEC && start && !paused) { slowActive = true; slowStartTime = now; slowCooldownTime = now; ballDelay = slowBallDelay; } if (slowActive && (now - slowStartTime) >= slowDuration * CLOCKS_PER_SEC) { slowActive = false; ballDelay = normalBallDelay; } if ((now - slowCooldownTime) < slowCooldown * CLOCKS_PER_SEC) { cooldownRemaining = slowCooldown - (now - slowCooldownTime) / CLOCKS_PER_SEC; } // --- MOVE BALL --- if (start) { Isstarted = true; int bx = (int)round(ball.x); int by = (int)round(ball.y); HandleBlockDestruction(bx,by); UpdateBall(); DrawBoard(); DrawRandomItems(); RemovePower (); if (ball.y >= 18) { Isstarted = false; WaitForKeyRelease(); ClearInputBuffer(); heal --; if (heal <= 0 || simpleBlocksDestroyed == 90) { if (heal <= 0) {WinOrLose = RED "Lose" RESET;} if (simpleBlocksDestroyed == 90) {WinOrLose = GREEN "Win" RESET;} saveHistory(player, score); int result = GameOverMenu(); if (result == 0) { // back to menu Isstarted = false; needRedraw = true; start = false; heal = 3; simpleBlocksDestroyed = 0; hardBlocksDestroyed = 0; score = 0; return; } else if (result == 1) { // new game // reset board for (int x = 0; x < 5; x++) for (int y = 0; y < 20; y++){ if (level == 6) board[x][y] = (y == 0 || y == 19) ? ' ' : ((rand() % 2 == 0) ? '1' : '2'); else if(level == 7) board[x][y] = (y == 0 || y == 19) ? ' ' : '2'; } for (int x = 5; x < 19; x++) for (int y = 0; y < 20; y++) board[x][y] = ' '; heal = 3; simpleBlocksDestroyed = 0; hardBlocksDestroyed = 0; score = 0; system("cls"); } } // reset ball & paddle state start = false; paddel.x = 10.0f; paddel.y = 18; ball.x = 10.0f; ball.y = 17.0f; ball.i_speed = 0.0f; ball.j_speed = 0.0f; DrawBoard(); } } while ((clock() - frameStart) * 1000 / CLOCKS_PER_SEC < ballDelay) { Sleep(1); } } } void displayInform() { if (heal == 3) { gotoxy(36, 4); cout << " ❤️❤️❤️ "; } else if (heal == 2) { gotoxy(36, 4); cout << " ❤️❤️ "; } else if (heal == 1) { gotoxy(36, 4); cout << " ❤️ "; } if (cooldownRemaining > 1) { gotoxy(74,25); cout << cooldownRemaining; gotoxy(75,25); cout << "s "; } else { gotoxy(74,25); cout << RED " READY " RESET; } } void WaitForKeyRelease() { while ((GetAsyncKeyState(VK_RETURN) & 0x8000) || (GetAsyncKeyState(VK_SPACE) & 0x8000) ||(GetAsyncKeyState(VK_ESCAPE) & 0x8000)) { Sleep(50); } } void ClearInputBuffer() { while (_kbhit())_getch(); } void HideCursor() { HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO info; info.dwSize = 100; info.bVisible = FALSE; SetConsoleCursorInfo(consoleHandle, &info); } void drawGameover(int choice) { system("cls"); string options[2] = {"Back to Menu", "New Game"}; string Display = ""; Display += CurrentTheme.borderColor; Display += "\n ┌───────────────────────────────────────┐\n"; Display += " │ │\n"; Display += " ├───────────────────┬───────────────────┤\n"; Display += " │ │ │\n"; Display += " │ │ │\n"; Display += " ├───────────────────┼───────────────────┤\n"; Display += " │ │ │\n"; Display += " │ │ │\n"; Display += " │ │ │\n"; Display += " └───────────────────┴───────────────────┘"; Display += RESET; gotoxy(30, 7); cout << Display; // Win / Lose text gotoxy(20, 9); if (simpleBlocksDestroyed == 90) cout << "YOU WIN"; else if (heal <= 0) cout << BRIGHTRED << "YOU LOSE" << RESET; // Scores title gotoxy(11, 11); cout << "Score"; gotoxy(29, 11); cout << "Best Score"; // Scores values gotoxy(12, 12); cout << score; gotoxy(31, 12); cout << YELLOW << bestscore << RESET; // Options gotoxy(8, 15); cout << options[0]; gotoxy(30, 15); cout << options[1]; // Selector box if (choice == 0) { gotoxy(5, 14); cout << YELLOW << "┌───────────────┐" << RESET; gotoxy(5, 16); cout << YELLOW << "└───────────────┘" << RESET; } else if (choice == 1) { gotoxy(25, 14); cout << YELLOW << "┌───────────────┐" << RESET; gotoxy(25, 16); cout << YELLOW << "└───────────────┘" << RESET; } } int saveBestScore(int currentScore) { ifstream inFile("bestscore.txt"); int savedBest = 0; if (inFile.is_open()) { inFile >> savedBest; inFile.close(); } if (currentScore > savedBest) { savedBest = currentScore; ofstream outFile("bestscore.txt"); outFile << savedBest; outFile.close(); } return savedBest; } int GameOverMenu() { int choice = 0; bestscore = saveBestScore(score); while (true) { drawGameover(choice); char input = _getch(); if (input == 'a' || input == 'A') { choice--; if (choice < 0) choice = 1; } else if (input == 'd' || input == 'D') { choice++; if (choice > 1) choice = 0; } else if (input == '\r') { ClearInputBuffer(); return choice; } } } void gotoxy(int x, int y) { cout << "\033[" << y << ";" << x << "H"; } void drawBoxAnimation(int x, int y, int w, int h) { // x : right,left y : up,down w : width h : hight gotoxy(x, y); cout << CurrentTheme.borderColor << "╔" << RESET; for (int i = 0; i < w - 2; i++) { cout << CurrentTheme.borderColor << "═" << RESET; Sleep(9); } cout << CurrentTheme.borderColor << "╗" << RESET; for (int i = 1; i < h - 1; i++) { gotoxy(x, y + i); cout << CurrentTheme.borderColor << "║" << RESET; gotoxy(x + w - 1, y + i); cout << CurrentTheme.borderColor << "║" << RESET; Sleep(30); } gotoxy(x, y + h - 1); cout << CurrentTheme.borderColor << "╚" << RESET; for (int i = 0; i < w - 2; i++) cout << CurrentTheme.borderColor << "═" << RESET; cout << CurrentTheme.borderColor << "╝" << RESET; } string getPlayerName() { string name = ""; drawBoxAnimation(40, 8, 24, 4); HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO info; GetConsoleCursorInfo(consoleHandle, &info); info.bVisible = TRUE; // Show cursor SetConsoleCursorInfo(consoleHandle, &info); while (name.length() == 0) { gotoxy(10, 9); cout << BRIGHTBLUE << "│" << RESET << YELLOW << CurrentTheme.topicColor << " Enter your Name\n" << RESET; gotoxy(10, 10); cout << BRIGHTBLUE << "│" << RESET << BRIGHTBLACK << " max(10)" << RESET; gotoxy(45, 9); cout << " "; gotoxy(45, 9); cin >> name; } info.bVisible = FALSE; // Hide cursor SetConsoleCursorInfo(consoleHandle, &info); return name.substr(0, 10); } // rendering function void DrawBoard() { int length = (paddel.size - 1) / 2; string frame; frame += CurrentTheme.borderColor + " ┌────────────────────┐\n" + RESET; for (int y = 0; y < 19; y++) { frame += CurrentTheme.borderColor + " │" + RESET; for (int x = 0; x < 20; x++) { string display = " "; // Paddle// Ball int i = (int)round(ball.x); int j = (int)round(ball.y); if ((int)round(paddel.x) - length <= x && (int)round(paddel.x) + length >= x && (int)round(paddel.y) == y) { display = CurrentTheme.paddleColor + CurrentTheme.paddleChar + RESET; } // Blocks else if (board[y][x] == '1') display = CurrentTheme.simpleBlockColor + CurrentTheme.blockChar + RESET; else if (board[y][x] == '2') display = CurrentTheme.hardBlockColor + CurrentTheme.blockChar + RESET; else if (board[y][x] == 'h') display = CurrentTheme.simpleBlockColor + CurrentTheme.blockChar + RESET; // Ball else if (board[y][x] == ' ' && i == x && j == y) display = CurrentTheme.ballChar; frame += display; } frame += CurrentTheme.borderColor + "│\n" + RESET; } frame += CurrentTheme.borderColor + " └────────────────────┘\n" + RESET; // --- Game Info --- frame += BRIGHTBLUE " │" RESET YELLOW " Press Esc to Save Game\n" RESET; frame += BRIGHTBLUE " │" RESET YELLOW " Press E to Exit\n" RESET; frame += BRIGHTBLUE " │" RESET YELLOW " Press P to Pause or Resume\n" RESET; frame += BRIGHTBLUE " │" RESET YELLOW " Press Enter to use your POWER after each 15s" RESET; // Power bar int barWidth = 10; int filled = (slowCooldown - cooldownRemaining) * barWidth / slowCooldown; frame += " POWER ["; for (int i = 0; i < barWidth; i++) frame += (i < filled ? "#" : "-"); frame += "] "; gotoxy(0, 0); cout << frame; gotoxy(30, 3); cout << "Player: " << player; gotoxy(30, 4); cout << "Lives: "; gotoxy(30, 5); cout << RED << "------------------" << RESET; gotoxy(30,6); cout << "Broken Blocks: " << to_string(simpleBlocksDestroyed + hardBlocksDestroyed); gotoxy(30,7); cout << "Score: " << to_string(score); } void HandleBlockDestruction(int x, int y) { if (x < 0 || x >= 20 || y < 0 || y >= 19) return; if (board[y][x] == '1') { simpleBlocksDestroyed++; score += 10; board[y][x] = ' '; } else if (board[y][x] == '2') { score += 10; board[y][x] = 'h'; } else if (board[y][x] == 'h') { hardBlocksDestroyed++; score += 10; board[y][x] = ' '; ActivateRandomItem(x, y); } } void UpdateBall() { if (ball.stuckToPaddle) { ball.x = paddel.x; ball.y = paddel.y - 1; return; } // Store previous position for accurate collision detection float prevX = ball.x; float prevY = ball.y; // Determine movement direction bool movingLeft = (ball.i_speed < -0.05f); bool movingRight = (ball.i_speed > 0.05f); bool movingUp = (ball.j_speed < -0.05f); bool movingDown = (ball.j_speed > 0.05f); // ===== STEP 1: WALL COLLISIONS ===== // Check vertical walls first if (movingLeft && ball.x + ball.i_speed <= 0) { ball.x = 0; ball.i_speed = fabs(ball.i_speed); // Reverse to right movingLeft = false; movingRight = true; } else if (movingRight && ball.x + ball.i_speed >= 19) { ball.x = 19; ball.i_speed = -fabs(ball.i_speed); // Reverse to left movingRight = false; movingLeft = true; } // Check top wall if (movingUp && ball.y + ball.j_speed <= 0) { ball.y = 0; ball.j_speed = fabs(ball.j_speed); // Reverse to down movingUp = false; movingDown = true; } // ===== STEP 2: MOVE BALL ===== ball.x += ball.i_speed; ball.y += ball.j_speed; // Get current cell positions int currX = (int)round(ball.x); int currY = (int)round(ball.y); int prevX_int = (int)round(prevX); int prevY_int = (int)round(prevY); // ===== STEP 3: BLOCK COLLISIONS WITH DIRECTIONAL PRIORITY ===== bool blockHit = false; // For DIAGONAL MOVEMENT: Check primary axes first before diagonal if (movingUp && movingRight) { // Priority 1: Check cell ABOVE (vertical collision) if (currY - 1 >= 0 && currX >= 0 && currX < 20 && (board[currY - 1][currX] == '1' || board[currY - 1][currX] == '2' || board[currY - 1][currX] == 'h')) { HandleBlockDestruction(currX, currY - 1); ball.j_speed = fabs(ball.j_speed); // Bounce down ball.y = currY - 0.4f; // Position below the block blockHit = true; } // Priority 2: Check cell to the RIGHT (horizontal collision) else if (currX + 1 < 20 && currY >= 0 && currY < 19 && (board[currY][currX + 1] == '1' || board[currY][currX + 1] == '2' || board[currY][currX + 1] == 'h')) { HandleBlockDestruction(currX + 1, currY); ball.i_speed = -fabs(ball.i_speed); // Bounce left ball.x = currX + 0.4f; // Position left of the block blockHit = true; } // Priority 3: Check DIAGONAL cell (top-right corner) else if (currX + 1 < 20 && currY - 1 >= 0 && (board[currY - 1][currX + 1] == '1' || board[currY - 1][currX + 1] == '2' || board[currY - 1][currX + 1] == 'h')) { // Corner hit - reverse vertical direction HandleBlockDestruction(currX + 1, currY - 1); ball.j_speed = fabs(ball.j_speed); // Bounce down ball.y = currY - 0.4f; blockHit = true; } } // Similar logic for other diagonal directions else if (movingUp && movingLeft) { // Priority 1: Above if (currY - 1 >= 0 && currX >= 0 && currX < 20 && (board[currY - 1][currX] == '1' || board[currY - 1][currX] == '2' || board[currY - 1][currX] == 'h')) { HandleBlockDestruction(currX, currY - 1); ball.j_speed = fabs(ball.j_speed); ball.y = currY - 0.4f; blockHit = true; } // Priority 2: Left else if (currX - 1 >= 0 && currY >= 0 && currY < 19 && (board[currY][currX - 1] == '1' || board[currY][currX - 1] == '2' || board[currY][currX - 1] == 'h')) { HandleBlockDestruction(currX - 1, currY); ball.i_speed = fabs(ball.i_speed); // Bounce right ball.x = currX - 0.4f; blockHit = true; } // Priority 3: Diagonal (top-left) else if (currX - 1 >= 0 && currY - 1 >= 0 && (board[currY - 1][currX - 1] == '1' || board[currY - 1][currX - 1] == '2' || board[currY - 1][currX - 1] == 'h')) { HandleBlockDestruction(currX - 1, currY - 1); ball.j_speed = fabs(ball.j_speed); ball.y = currY - 0.4f; blockHit = true; } } else if (movingDown && movingRight) { // Priority 1: Below if (currY + 1 < 19 && currX >= 0 && currX < 20 && (board[currY + 1][currX] == '1' || board[currY + 1][currX] == '2' || board[currY + 1][currX] == 'h')) { HandleBlockDestruction(currX, currY + 1); ball.j_speed = -fabs(ball.j_speed); ball.y = currY + 0.4f; blockHit = true; } // Priority 2: Right else if (currX + 1 < 20 && currY >= 0 && currY < 19 && (board[currY][currX + 1] == '1' || board[currY][currX + 1] == '2' || board[currY][currX + 1] == 'h')) { HandleBlockDestruction(currX + 1, currY); ball.i_speed = -fabs(ball.i_speed); ball.x = currX + 0.4f; blockHit = true; } // Priority 3: Diagonal (bottom-right) else if (currX + 1 < 20 && currY + 1 < 19 && (board[currY + 1][currX + 1] == '1' || board[currY + 1][currX + 1] == '2' || board[currY + 1][currX + 1] == 'h')) { HandleBlockDestruction(currX + 1, currY + 1); ball.j_speed = -fabs(ball.j_speed); ball.y = currY + 0.4f; blockHit = true; } } else if (movingDown && movingLeft) { // Priority 1: Below if (currY + 1 < 19 && currX >= 0 && currX < 20 && (board[currY + 1][currX] == '1' || board[currY + 1][currX] == '2' || board[currY + 1][currX] == 'h')) { HandleBlockDestruction(currX, currY + 1); ball.j_speed = -fabs(ball.j_speed); ball.y = currY + 0.4f; blockHit = true; } // Priority 2: Left else if (currX - 1 >= 0 && currY >= 0 && currY < 19 && (board[currY][currX - 1] == '1' || board[currY][currX - 1] == '2' || board[currY][currX - 1] == 'h')) { HandleBlockDestruction(currX - 1, currY); ball.i_speed = fabs(ball.i_speed); ball.x = currX - 0.4f; blockHit = true; } // Priority 3: Diagonal (bottom-left) else if (currX - 1 >= 0 && currY + 1 < 19 && (board[currY + 1][currX - 1] == '1' || board[currY + 1][currX - 1] == '2' || board[currY + 1][currX - 1] == 'h')) { HandleBlockDestruction(currX - 1, currY + 1); ball.j_speed = -fabs(ball.j_speed); ball.y = currY + 0.4f; blockHit = true; } } // For pure horizontal/vertical movement, check current cell else if (!blockHit && currX >= 0 && currX < 20 && currY >= 0 && currY < 19) { if (board[currY][currX] == '1' || board[currY][currX] == '2' || board[currY][currX] == 'h') { HandleBlockDestruction(currX, currY); if (movingLeft || movingRight) { ball.i_speed *= -1; // Horizontal bounce } if (movingUp || movingDown) { ball.j_speed *= -1; // Vertical bounce } blockHit = true; } } // ===== STEP 4: PADDLE COLLISION (lowest priority) ===== int paddleLength = paddel.size / 2; int paddleTop = paddel.y - 1; // Only check paddle collision if ball is moving downward and near paddle if (movingDown && currY >= paddleTop - 1 && currY <= paddel.y + 1) { float paddleLeft = paddel.x - paddleLength; float paddleRight = paddel.x + paddleLength; // Check if ball is within paddle's horizontal range if (ball.x >= paddleLeft && ball.x <= paddleRight) { // Calculate precise hit position for angle reflection float relativeHit = (ball.x - paddel.x) / (float)paddleLength; ball.i_speed = relativeHit * 1.3f; // Wider angle distribution // Ensure minimum horizontal speed to prevent straight up/down movement if (fabs(ball.i_speed) < 0.35f) { ball.i_speed = (ball.i_speed < 0) ? -0.35f : 0.35f; } ball.j_speed = -fabs(ball.j_speed); // Always bounce upward ball.y = paddleTop - 0.2f; // Position just above paddle } } } void saveGame(const string& player) { ofstream file("save.dat", ios::binary); if (!file) return; file.write((char*)&paddel.speed, sizeof(paddel.speed)); file.write((char*)&paddel.x, sizeof(paddel.x)); file.write((char*)&paddel.y, sizeof(paddel.y)); file.write((char*)&ball.i_speed, sizeof(ball.i_speed)); file.write((char*)&ball.j_speed, sizeof(ball.j_speed)); file.write((char*)&ball.x, sizeof(ball.x)); file.write((char*)&ball.y, sizeof(ball.y)); file.write((char*)&board, sizeof(board)); file.write((char*)&score, sizeof(score)); file.write((char*)&heal, sizeof(heal)); file.write((char*)&bestscore, sizeof(bestscore)); file.write((char*)&Theme ,sizeof(Theme)); int len = player.size(); file.write((char*)&len, sizeof(len)); file.write(player.c_str(), len); file.close(); } bool loadGame(string& player) { ifstream file("save.dat", ios::binary); if (!file) return false; file.read((char*)&paddel.speed, sizeof(paddel.speed)); file.read((char*)&paddel.x, sizeof(paddel.x)); file.read((char*)&paddel.y, sizeof(paddel.y)); file.read((char*)&ball.i_speed, sizeof(ball.i_speed)); file.read((char*)&ball.j_speed, sizeof(ball.j_speed)); file.read((char*)&ball.x, sizeof(ball.x)); file.read((char*)&ball.y, sizeof(ball.y)); file.read((char*)&board, sizeof(board)); file.read((char*)&score, sizeof(score)); file.read((char*)&heal, sizeof(heal)); file.read((char*)&bestscore, sizeof(bestscore)); file.read((char*)&Theme ,sizeof(Theme)); int len; file.read((char*)&len, sizeof(len)); player.resize(len); file.read(&player[0], len); file.close(); return true; } void saveHistory(const string& player, int score) { ofstream file("history.txt", ios::app); if (!file) return; time_t now = time(0); char* dt = ctime(&now); if (dt) dt[strlen(dt) - 1] = '\0'; file << player << " " << WinOrLose << " | Score: " << score << " | " << dt << " | Details: simple Blocks Destroyed: " << simpleBlocksDestroyed << " hard Blocks Destroyed: " << hardBlocksDestroyed << " | Lives: " << heal << " |" << endl; file.close(); } int extractScore(const string& line) { int pos = line.find("Score: "); if (pos == -1) return -1; pos += 7; // Length of "Score: " int end = line.find(" |", pos); if (end == -1) end = line.length(); int score = 0; for (int i = pos; i < end && isdigit(line[i]); i++) { score = score * 10 + (line[i] - '0'); } return score; } void showHistory() { system("cls"); ifstream file("history.txt"); gotoxy(55, 1); cout << YELLOW << "===== GAME HISTORY =====\n\n" << RESET; if (!file) { gotoxy(5, 7); cout << RED << "No history yet!" << RESET; Sleep(2000); return; } // Read all records vector history; string line; while (getline(file, line)) { if (!line.empty()) history.push_back(line); } file.close(); if (history.empty()) { gotoxy(5, 7); cout << RED << "No data available." << RESET; Sleep(2000); return; } // Sort by score descending (using int indices) sort(history.begin(), history.end(), [](const string& a, const string& b) { return extractScore(a) > extractScore(b); }); const int LINES_PER_PAGE = 6; int currentPage = 0; int totalRecords = history.size(); int totalPages = (totalRecords + LINES_PER_PAGE - 1) / LINES_PER_PAGE; while (true) { system("cls"); gotoxy(55, 1); cout << YELLOW << "===== GAME HISTORY =====" << RESET << endl; cout << "Page " << (currentPage + 1) << " of " << totalPages << " | Total Records: " << totalRecords << "\n\n"; int start = currentPage * LINES_PER_PAGE; int end = start + LINES_PER_PAGE; if (end > totalRecords) end = totalRecords; for (int i = start; i < end; ++i) { // Optional: Highlight top 3 scores if (i == 0) cout << GOLD; else if (i == 1) cout << GREEN; else if (i == 2) cout << YELLOW; cout << history[i] << RESET << endl; } cout << "\n[N]ext page | [P]revious page | [C]lear history | [E]xit: "; char key = _getch(); key = tolower(key); if (key == 'e') break; else if (key == 'n' && currentPage < totalPages - 1) currentPage++; else if (key == 'p' && currentPage > 0) currentPage--; else if (key == 'c') { cout << "\n" << RED << "Clear ALL history? (Y/N): " << RESET; char confirm = _getch(); if (tolower(confirm) == 'y') { ofstream clearFile("history.txt", ios::trunc); clearFile.close(); cout << GREEN << "\nHistory cleared!" << RESET; Sleep(1000); return; } } } } void clearSaveGame() { ofstream file("save.dat", ios::trunc); file.close(); } void DrawHelpItems () { cout << MAGENTA; cout << MAGENTA << " ╔═══════════════════" << RESET << YELLOW << " 👾HELP👾" << RESET << MAGENTA << "═══════════════════╗\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " 🎮 " << YELLOW << "In-Game Controls" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "A" << RESET << " / " << BRIGHTBLACK << "D" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Move paddle left / right" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "SPACE" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Launch the ball" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "P" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Pause / Resume game" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "ENTER" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Decrease ball speed" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "ESC" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "save The Game" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "E " << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Exit Game" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " 🎮 " << YELLOW << "Menu Controls" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "▲ / ▼" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Navigate menu" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "ENTER" << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Select option" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "E " << RESET << " " << BRIGHTBLUE << "→" << RESET << " " << WHITE << "Back" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " ❤️ " << GREEN << "Lives: Start with 3 lives" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << BRIGHTBLACK << "Press E to Exit" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ║" << RESET << " " << MAGENTA << "║\n"; cout << MAGENTA << " ╚═══════════════════════════════════════════════╝\n"; cout << RESET; } void PlayBreakoutIntro() { const char* logo[] = { " \033[94m██████\033[35m╗\033[0m \033[94m██████\033[35m╗\033[0m \033[94m███████\033[35m╗\033[0m \033[94m█████\033[35m╗\033[0m \033[94m██\033[35m╗\033[0m \033[94m██\033[35m╗\033[0m \033[94m██████\033[35m╗\033[0m \033[94m██\033[35m╗\033[0m \033[94m██\033[35m╗\033[0m\033[94m████████\033[35m╗\033[0m", " \033[94m██\033[35m╔══\033[0m\033[94m██\033[35m╗\033[0m\033[94m██\033[35m╔══\033[0m\033[94m██\033[35m╗\033[0m\033[94m██\033[35m╔════╝\033[0m\033[94m██\033[35m╔══\033[0m\033[94m██\033[35m╗\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m╔╝\033[0m\033[94m██\033[35m╔═══\033[0m\033[94m██\033[35m╗\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m\033[35m╚══\033[0m\033[94m██\033[35m╔══╝\033[0m", " \033[94m██████\033[35m╔╝\033[0m\033[94m██████\033[35m╔╝\033[0m\033[94m█████\033[35m╗\033[0m \033[94m███████\033[35m║\033[0m\033[94m█████\033[35m╔╝\033[0m \033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m ", " \033[94m██\033[35m╔══\033[0m\033[94m██\033[35m╗\033[0m\033[94m██\033[35m╔══\033[0m\033[94m██\033[35m╗\033[0m\033[94m██\033[35m╔══╝\033[0m \033[94m██\033[35m╔══\033[0m\033[94m██\033[35m║\033[0m\033[94m██\033[35m╔═\033[0m\033[94m██\033[35m╗\033[0m \033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m ", " \033[94m██████\033[35m╔╝\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m\033[94m███████\033[35m╗\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m║\033[0m\033[94m██\033[35m║\033[0m \033[94m██\033[35m╗\033[0m\033[35m╚\033[0m\033[94m██████\033[35m╔╝\033[0m\033[35m╚\033[0m\033[94m██████\033[35m╔╝\033[0m \033[94m██\033[35m║\033[0m ", LIGHTMAGENTA " ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ " RESET }; system("cls"); for (int i = 0; i < 6; i++) { cout << " " << LIGHTBLUE << logo[i] << RESET << endl; Sleep(90); } gotoxy(39, 7); cout << BRIGHTMAGENTA << " ♥ناریا بوخ یاه هچب حور هب میدقت "<< RESET; Sleep(800); } void DrawSetting () { system("cls"); string options[] = { BRIGHTBLUE "Select One Theme" RESET, "Skeleton", "Cute", "Galaxy", "Random", BRIGHTBLUE "Select a Level" RESET, "Easy", "Hard", }; drawBoxAnimation(40, 8, 29, 12); gotoxy(10, 9); cout << BRIGHTBLUE << "│" << RESET << YELLOW <<" Press ENTER to Choose" << RESET; gotoxy(10, 10); cout << BRIGHTBLUE << "│" << RESET << YELLOW << " Press E to Exit" << RESET; int selected; if (difaultTheme) selected = 1; else selected = Theme; while (true) { if (difaultTheme) { gotoxy(54,10); cout << CYAN "[ON]" RESET; } if (difaultlevel) { gotoxy(44,15); cout << CYAN "[ON]" RESET; } // redraw options for (int i = 0; i < 8; i++) { gotoxy(41, 9 + i); cout << " "; // clear line gotoxy(42, 9 + i); if (i == selected && i != 0 && i != 5) { cout << CurrentTheme.cursorColor << CurrentTheme.cursorChar << " " << options[i] << RESET; } else { cout << " " << options[i]; } // Show [ON] only for valid selections if (i >= 1 && i <= 4) { if (!difaultTheme && Theme == i) { cout << CYAN " [ON]" RESET; } } else if (i >= 6 && i <= 7) { if (!difaultlevel && level == i) { cout << CYAN " [ON]" RESET; } } } char key = _getch(); if (key == 72 && selected > 1) { if (selected == 6) selected--; selected--; } if (key == 80 && selected < 7) { if (selected == 4) selected++; selected++; } if (key == 13) { // ENTER if (selected < 5){ difaultTheme = false; if (Theme == selected) Theme = -1; else Theme = selected; } else if (selected >5) { difaultlevel = false; if (level == selected) level = -1; else level = selected; } } if (key == 69 || key == 101){ // E if (Theme == 4) Theme = rand() % 3 +1; break; } } } void DeterminThemeAnfLevel() { if (Theme == -1) Theme = 1; // Default (Skeleton) if (level == -1) level = 6; switch (Theme) { case 1: // Skeleton CurrentTheme.borderColor = WHITE; CurrentTheme.paddleColor = BRIGHTRED; CurrentTheme.simpleBlockColor = WHITE; CurrentTheme.hardBlockColor = BRIGHTBLACK; CurrentTheme.ballColor = WHITE; CurrentTheme.cursorColor = BRIGHTRED; CurrentTheme.paddleChar = "="; CurrentTheme.blockChar = "█"; CurrentTheme.ballChar = "●"; CurrentTheme.cursorChar = "🕷️"; break; case 2: // Cute CurrentTheme.borderColor = PINK; CurrentTheme.paddleColor = CYAN; CurrentTheme.simpleBlockColor = MAGENTA; CurrentTheme.hardBlockColor = ORANGE; CurrentTheme.ballColor = WHITE; CurrentTheme.cursorColor = CYAN; CurrentTheme.paddleChar = "="; CurrentTheme.blockChar = "█"; CurrentTheme.ballChar = "●"; CurrentTheme.cursorChar = "🍩"; break; case 3: // Galaxy CurrentTheme.borderColor = BRIGHTBLUE; CurrentTheme.paddleColor = BOLDYELLOW; CurrentTheme.simpleBlockColor = MAGENTA; CurrentTheme.hardBlockColor = BRIGHTMAGENTA; CurrentTheme.ballColor = WHITE; CurrentTheme.cursorColor = PURPLE; CurrentTheme.paddleChar = "═"; CurrentTheme.blockChar = "█"; CurrentTheme.ballChar = "●"; CurrentTheme.cursorChar = "🪐"; break; } } void creatrandomItems() { for (int i = 0; i < 12; i++) { Items[i].x = rand() % 18 + 1; Items[i].y = rand() % 6 + 1; Items[i].power = rand() % 3; Items[i].active = false; } } void ActivateRandomItem(int bx, int by) { if (rand() % 100 > 30) return; for (int i = 0; i < 12; i++) { if (!Items[i].active) { Items[i].x = bx; Items[i].y = by; Items[i].power = rand() % 3; Items[i].active = true; break; } } } void DrawRandomItems() { for (int i = 0; i < 12; i++) { if (!Items[i].active) continue; Items[i].y++; int length = paddel.size / 2; if (Items[i].y == paddel.y -1 && Items[i].x >= paddel.x - length && Items[i].x <= paddel.x + length) { ApplyPower(Items[i].power); Items[i].active = false; continue; } if (Items[i].y >= 19) { Items[i].active = false; continue; } gotoxy(Items[i].x + 4, Items[i].y + 1); cout << CurrentTheme.cursorColor << "⬇" << RESET; } } void ApplyPower(int power) { switch (power) { case 0: // Ball speed powerOn = clock(); paddel.speed = 2; break; case 1: // Bigger Paddle paddel.size += 2; if (paddel.size > 5) paddel.size = 5; powerOn = clock(); break; case 2: // Extra Life if (heal < 3) heal++; break; } } void RemovePower () { if (clock() - powerOn > 5 * CLOCKS_PER_SEC) { paddel.speed = 1; paddel.size = 3; } }