From c673c3429d8890aad971cd550351d873d5d071be Mon Sep 17 00:00:00 2001 From: Sarazpr Date: Tue, 14 Apr 2026 14:17:07 +0000 Subject: [PATCH] Upload files to "/" --- FinalProject.cpp | 1476 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1476 insertions(+) create mode 100644 FinalProject.cpp diff --git a/FinalProject.cpp b/FinalProject.cpp new file mode 100644 index 0000000..f846bf1 --- /dev/null +++ b/FinalProject.cpp @@ -0,0 +1,1476 @@ +// ----- 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; + } +} + + +