commit 99e865a9726ff00de491aa2ae60c3b66c6860292 Author: Parmis Jamami Date: Mon Apr 13 20:57:37 2026 +0330 add breakout diff --git a/Final_Project.cpp b/Final_Project.cpp new file mode 100644 index 0000000..e728702 --- /dev/null +++ b/Final_Project.cpp @@ -0,0 +1,1636 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "winmm.lib") + + + +using namespace std; + +struct paddleInfo { + int x, y; + int oldx; + int length; +}; + +struct ballInfo { + int x, y; + int oldx, oldy; + int vx , vy; + int speedx, speedy; +}; + +struct userInfo { + string userName; + string password; + int highScore; + +}; + +struct gameInfo { + int score; + string userName; + string date = ""; + int totalTime; + int remainedHearts; + bool result; +}; + + + +paddleInfo paddle; +ballInfo ball; +gameInfo game[1000]; +userInfo users[1000]; + + + + +string mainMenuOption[6] = {"New Game", "Load Game", "Help", "Game History", "Setting", "Exit"}; +string pauseMenuOption[4] = {"Continue", "Restart", "Save the game", "Quit"}; +string registerMenuOption[2] = {"Login to your account", "Creat account"}; +string endGameMenuOptions[2] = {"Play again", "Quit"}; +string settingMenuOptions[6] = {"Ball shape", "Clear game history", "Active sound", "Log out", "Delete aacount", "Quit"}; +string ballShapeOptions[8] = {"●","🟣","🔵","🟠","🌏","🥎","🏀","Quit"}; + +int currentUser; +int currentBall = 0; +int countGame=0; +int countUsers=0; +bool activeGame = false; +int boardWidth, boardLength; +int terLength, terWidth; +int startX, startY; +int remainedHearts = 3; +bool blockAlive[6][10]; + +int ballWidth = (ballShapeOptions[currentBall]=="●") ? 1 : 2; + +bool gamePaused = false; +bool endGame = false; +bool quitGame = false; +bool loadedGame=false; +bool loggedIn = false; +bool soundActive = true; + +time_t gameStartTime; //زمان شروع بازی +time_t pauseStartTime; // pause زمان شزوع +time_t gameEndTime; +int totalPauseSecond=0; +bool timerStarted=false; + + +string blocks[6][10] = { + {"\033[95m████","\033[35m████","\033[94m████","\033[96m████","\033[95m████","\033[35m████","\033[94m████","\033[95m████","\033[35m████","\033[94m████\033[0m"}, + {"\033[96m████","\033[95m████","\033[35m████","\033[94m████","\033[96m████","\033[95m████","\033[96m████","\033[35m████","\033[94m████","\033[35m████\033[0m"}, + {"\033[35m████","\033[94m████","\033[96m████","\033[95m████","\033[35m████","\033[94m████","\033[35m████","\033[95m████","\033[35m████","\033[94m████\033[0m"}, + {"\033[94m████","\033[96m████","\033[95m████","\033[35m████","\033[94m████","\033[96m████","\033[95m████","\033[94m████","\033[95m████","\033[96m████\033[0m"}, + {"\033[95m████","\033[35m████","\033[94m████","\033[96m████","\033[95m████","\033[35m████","\033[94m████","\033[95m████","\033[35m████","\033[94m████\033[0m"}, + {"\033[96m████","\033[95m████","\033[35m████","\033[94m████","\033[96m████","\033[95m████","\033[96m████","\033[35m████","\033[94m████","\033[35m████\033[0m"}}; + + + +void setCursorPosition(int x, int y); +void hideCursor(); + +void loadGameHistory(); +void sortAndSaveGames(); +void gameHistory(); +void clearGameHistory(); + +void loadUsers(); +void updateUsersInfo(); +void login(); +void creatAccount(); +void deleteAccount(); + +void mainMenu(); +void drawMainMenu(int currentOption); +void updateMainMenu(int previousOption, int currentOption); + +void pauseMenu(); +void drawPauseMenu(int currentOption); +void updatePauseMenu(int previousOption, int currentOption); + +void registerMenu(); +void drawRegisterMenu(int currentOption); +void updateRegisterMenu(int previousOption, int currentOption); + +void settingMenu(); +void drawSettingMenu(int currentOption); +void updateSettingMenu(int previousOption, int currentOption); + +void ballShapeMenu(); +void drawBallShapeMenu(int currentOption); +void updateBallShapeMenu(int previousOption, int currentOption); + +void endGameMenu(); +void drawEndGameMenu (int currentOption); +void updateEndGameMenu(int previousOption, int currentOption); + +void setupGame(); +void handleInput(); +void gameLoop(); +void drawGameScreen(); +void updateGameScreen(); +void updateBallLocation(); +void blockDistruction(int &nextX,int &nextY); +int remainedBlocks(); +void showGameResult(bool result); + +string getSaveFileName(const string& username); + +bool saveGame(); +bool loadGame(); +void drawHelp(); + +void drawBoard(int length, int width); + +void getTerminalSize(); + +void showGameTime(); + + + + + + + + +int main() { + + SetConsoleOutputCP(CP_UTF8); + loadGameHistory(); + loadUsers(); + mainMenu(); + +} + +void clearGameHistory() { + + countGame=0; + + //پاک کردن تاریخچه بازی + ofstream file("gameHistory.txt"); + file.close(); +} + +void deleteAccount() { + + drawBoard(58, 10); + setCursorPosition(startX + 2 , startY + 4); + cout<<"Are you sure you want to delete your account? (y/n) "; + + char input; + while(true) { + input = getch(); + if(input=='y' || input=='Y') break; + else if(input=='n' || input=='N') return; + } + + for(int i=currentUser ; i0) currentOption--; + updateSettingMenu(previousOption, currentOption); + break; + + case 's' : case 'S' : + previousOption = currentOption; + if(currentOption<5) currentOption++; + updateSettingMenu(previousOption, currentOption); + break; + + case 13 : + switch (currentOption) { + case 0: //shop + ballShapeMenu(); + drawSettingMenu(currentOption); + break; + case 1: //clear game history + clearGameHistory(); + drawBoard(42, 10); + setCursorPosition(startX + boardLength/2 - 11 , startY + 4); + cout<<"Game history cleared!✅"; + Sleep(1000); + drawSettingMenu(currentOption); + break; + case 2: //sound + soundActive = !soundActive; + drawSettingMenu(currentOption); + break; + case 3: //log out + loggedIn=false; + drawBoard(42, 10); + setCursorPosition(startX + boardLength/2 - 11 , startY + 4); + cout<<"You're logged out!✅"; + Sleep(1000); + drawSettingMenu(currentOption); + break; + case 4: //delete account + if(!loggedIn) login(); + if(loggedIn) deleteAccount(); + drawSettingMenu(currentOption); + break; + case 5: //quit + return; + } + } + } +} + + +void drawSettingMenu(int currentOption) { + + drawBoard(42, 20); + for(int i=0 ; i<6 ; i++) { + setCursorPosition(startX + boardLength/2 - settingMenuOptions[i].size()/2, startY + 2 + (i*3)); + if(i==currentOption) { + if(i==2 && soundActive) cout<<"\033[38;5;198m"<0) currentOption--; + updateBallShapeMenu(previousOption, currentOption); + break; + + case 's' : case 'S' : + previousOption = currentOption; + if(currentOption<7) currentOption++; + updateBallShapeMenu(previousOption, currentOption); + break; + + case 13 : + + switch (currentOption) { + case 0: + currentBall=0; + drawBallShapeMenu(currentOption); + break; + case 1: + currentBall=1; + ballWidth = 2; + drawBallShapeMenu(currentOption); + break; + case 2: + currentBall=2; + ballWidth = 2; + drawBallShapeMenu(currentOption); + break; + + case 3: + currentBall=3; + ballWidth = 2; + drawBallShapeMenu(currentOption); + break; + case 4: + currentBall=4; + ballWidth = 2; + drawBallShapeMenu(currentOption); + break; + case 5: + currentBall=5; + ballWidth = 2; + drawBallShapeMenu(currentOption); + break; + case 6: + currentBall=6; + ballWidth = 2; + drawBallShapeMenu(currentOption); + break; + case 7: //quit + return; + } + } + } +} + + +void drawBallShapeMenu(int currentOption) { + + drawBoard(42, 20); + + for(int i=0 ; i<8 ; i++) { + setCursorPosition(startX + boardLength/2 - ballShapeOptions[i].size()/2, startY+3+(i*2)); + + if(i==currentOption && i==currentBall) { //توپ انتخاب شده و توپی که کرسر روی ان قرار دارد + setCursorPosition(startX + boardLength/2 - ballShapeOptions[i].size()/2 - 1, startY+3+(i*2)); + cout<<"\033[38;5;198m<\033[0m"<\033[0m"<<"✅"; + } + else if(i==currentBall) { + cout<<"\033[38;5;198m<\033[0m"<\033[0m"; + } + else { + cout<از کزینه قبلی منو + setCursorPosition(startX + boardLength/2 - ballShapeOptions[previousOption].size()/2 - 1, startY + 3 + (previousOption*2)); + if(previousOption==currentBall) cout<<" "<\033[0m" <<"✅"; + else cout<<"\033[38;5;198m<\033[0m"<\033[0m"; +} + + +void loadGameHistory() { + + ifstream file("gameHistory.txt"); + + if(!file.is_open()) { + return; + } + + string line; + gameInfo temp; + while(getline(file, line) ) { + temp.userName = line; + + getline(file, line); + temp.date = line; + + getline(file, line); + temp.remainedHearts = stoi(line); + + getline(file, line); + temp.score = stoi(line); + + getline(file, line); + temp.result = stoi(line); + + getline(file, line); + temp.totalTime = stoi(line); + + game[countGame] = temp; + countGame++; + } + file.close(); +} + + +void sortAndSaveGames() { + + for(int i=0 ; i3) { //اگر صفحه قبل و بعد وجود داشت + cout<<"Press \u2192(next page) or \u2190(last page) or Q(menu)"; + } + else if(i==countGame-1 && i>3) { //صفحه قبل وجود داشته باشد اما بعد وجود نداشته باشد + cout<<"Press \u2190(last page) or Q(menu)"; + } + else cout<<"Press Q to go to menu"; //فقط یک صفحه داریم + + + while(true) { + + char input = getch(); + + if(input=='Q' || input=='q') return; + else if (input==77 && i3) { //برای رفتن به صفحه قبل + i=i-5-(i%4); + drawBoard(70, 26); + break; + } + } + } + } +} + +void loadUsers() { + + ifstream file("users.txt"); + + if(!file.is_open()) { + return; + } + + string line; + userInfo temp; + while(getline(file, line) ) { + + temp.userName = line; + + getline(file, line); + temp.password = line; + + getline(file, line); + temp.highScore = stoi(line); + + users[countUsers] = temp; + countUsers++; + } + file.close(); +} + + +void registerMenu() { + + int currentOption = 0; + int previousOption; + drawRegisterMenu(currentOption); + while(true) { + char input = getch(); + + switch (input) { + case 'w': case 'W' : + previousOption = currentOption; + if(currentOption>0) currentOption--; + updateRegisterMenu(previousOption, currentOption); + break; + + case 's' : case 'S' : + previousOption = currentOption; + if(currentOption<1) currentOption++; + updateRegisterMenu(previousOption, currentOption); + break; + + case 13 : + switch (currentOption) { + case 0: //login + login(); + return; + case 1: //create acoount + creatAccount(); + return; + } + } + } +} + + +void drawRegisterMenu(int currentOption) { + + drawBoard(42, 10); + for(int i=0 ; i<2 ; i++) { + setCursorPosition(startX + boardLength/2 - registerMenuOption[i].size()/2, startY + 3 + (i*3)); + if(i==currentOption) { + cout<<"\033[38;5;198m"<0) currentOption--; + updateEndGameMenu(previousOption, currentOption); + break; + + case 's' : case 'S' : + previousOption = currentOption; + if(currentOption<1) currentOption++; + updateEndGameMenu(previousOption, currentOption); + break; + + case 13 : + switch (currentOption) { + case 0: //play again + setupGame(); + gameLoop(); + return; + case 1: //quit + return; + } + } + } +} + + +void updateEndGameMenu(int previousOption, int currentOption) { + + setCursorPosition(startX + boardLength/2 - endGameMenuOptions[previousOption].size()/2, startY + 2 + (previousOption*2)); + cout<>tempName; + + for(int i=0 ; i>users[countUsers].password; + users[countUsers].highScore = 0; //رکورد اولیه + + setCursorPosition(startX + boardLength/2 - 10, startY + 5); + cout<<"Account created!✅"; + Sleep(1500); + currentUser = countUsers; + loggedIn = true; + + countUsers++; + + updateUsersInfo(); +} + + +void login() { + + string username, password; + drawBoard(42, 10); + setCursorPosition(startX + 3, startY + 3); + cout<<"Enter your username : "; + cin>>username; + setCursorPosition(startX + 3, startY + 5); + cout<<"Enter your password : "; + cin>>password; + + loggedIn = false; + for(int i=0 ; i0) currentOption--; + updateMainMenu(previousOption, currentOption); + break; + + case 's' : case 'S' : + previousOption = currentOption; + if(currentOption<5) currentOption++; + updateMainMenu(previousOption, currentOption); + break; + + case 13 : + + switch (currentOption) { + case 0: //new game + quitGame=false; + if(!loggedIn) registerMenu(); + if(loggedIn) { + gameLoop(); + } + drawMainMenu(currentOption); + break; + case 1: //load game + quitGame=false; + if(!loggedIn) login(); + if(loggedIn && loadGame()) { + gameLoop(); + } + else if (loggedIn && !loadGame()){ + drawBoard(42, 10); + setCursorPosition(startX + boardLength/2 - 8 , startY + 4); + cout<<"NO SAVED GAME!❌"<= startX + 1) { + paddle.x -= 3; + } + else paddle.x = startX + 1; + if(!activeGame) { + ball.oldx = ball.x; + ball.oldy = ball.y; + ball.x = paddle.x + paddle.length/2 - (ballWidth - 1); + } + break; + case 'd' : case 'D' : + paddle.oldx = paddle.x; + if(paddle.x + paddle.length + 3 <= startX + boardLength - 1) { + paddle.x += 3; + } + else paddle.x = (startX + boardLength - 1) - paddle.length; + if(!activeGame) { + ball.oldx = ball.x; + ball.oldy = ball.y; + ball.x = paddle.x + paddle.length/2 - (ballWidth - 1); + } + break; + case ' ' : // برای شروع بازی space + if(!activeGame) { + activeGame = true; + ball.vy = -1; + // تعیین جهت اولیه توپ با توجه به موقعیت راکت در شروع بازی + if(ball.x >= startX + boardLength/2) ball.vx = 1; + else if(ball.x < startX + boardLength/2) ball.vx = -1; + } + break; + case 'p' : case 'P' : // برنامه pause برای + pauseMenu(); + return; + } + } +} + +void blockDistruction(int &nextX , int &nextY) { + + + int ballLeft = nextX; + int ballRight = nextX + ballWidth - 1; + + for(int i=0 ; i<6 ; i++) { + for(int j=0 ; j<10 ; j++) { + + if(!blockAlive[i][j]) continue; + // مختصات شروع اجرها + int blockY = startY + 1 + (i); + int blockX = startX + 1 + (j*4); + + + int left = blockX; // سمت چپ بلوک + int right = blockX + 3 - (ballWidth - 1); //سمت راست بلوک + + bool hitInside = (ballRight >= left && ballLeft<= right && nextY == blockY); + if (!hitInside) continue; + + bool fromTop = ball.vy > 0; //حرکت از بالا به پایین + bool fromBottom = ball.vy < 0; // حرکت از پایین به بالا + bool fromLeft = ball.vx > 0; // حرکت از چپ به راست + bool fromRight = ball.vx < 0; //حرکت از راست به چپ + + bool topBlock = (i>0 && blockAlive[i-1][j]); //سالم بودن اجر بالا + bool bottomBlock = (i<5 && blockAlive[i+1][j]); //سالم بودن اجر پایین + bool leftBlock = (j>0 && blockAlive[i][j-1]); //سالم بودن اجر چپ + bool rightBlock = (j<9 && blockAlive[i][j+1]); // سالم بودن اجر راست + + // حرکت های مورب و برخورد با گوشه + if (fromTop && fromRight && topBlock && rightBlock) { + ball.vx *= -1; + ball.vy *= -1; + } + else if (fromTop && fromLeft && topBlock && leftBlock) { + ball.vx *= -1; + ball.vy *= -1; + } + else if (fromBottom && fromRight && bottomBlock && rightBlock) { + ball.vx *= -1; + ball.vy *= -1; + } + else if (fromBottom && fromLeft && bottomBlock && leftBlock) { + ball.vx *= -1; + ball.vy *= -1; + } + else { + bool cameFromSide = (ball.x + 1 < left && ballRight >= left) || (ball.x > right && ballLeft <= right); + + if(cameFromSide) ball.vx *= -1; //برخورد از چپ یا راست + else ball.vy *= -1; //برخورد از بالا یا پایین + } + + setCursorPosition(blockX,blockY); + cout<<" "; + blockAlive[i][j] = false; + if(soundActive) PlaySound(TEXT("sounds\\breakingBlock.wav"), NULL, SND_FILENAME | SND_ASYNC); + + game[countGame].score += 10; + if(game[countGame].score > users[currentUser].highScore) { + users[currentUser].highScore = game[countGame].score; + updateUsersInfo(); + } + + return; + } + } +} + + +void updateBallLocation() { + + int ballRight = ball.x; + int ballLeft = ball.x + ballWidth - 1; + //موقعیت بعدی توپ + int nextX = ball.x + (ball.vx * ball.speedx); + int nextY = ball.y + (ball.vy * ball.speedy); + + //برخورد توپ با دیوار سمت چپ + if (nextX <= startX + 1) { + ball.vx *= -1; + nextX = startX + 1; + } + //برخورد توپ با دیوار سمت راست + else if (nextX>= startX + boardLength - 2 - (ballWidth - 1)) { + ball.vx *= -1; + nextX = startX + boardLength - 2 - (ballWidth-1); + } + //برخورد توپ با سقف + if (nextY <= startY + 1) { + ball.vy *= -1; + nextY = startY + 1; + } + blockDistruction(nextX,nextY); + //افتادن توپ + if (nextY == paddle.y + 2) { + if(soundActive) PlaySound(TEXT("sounds\\Fall.wav"), NULL, SND_FILENAME | SND_ASYNC); + activeGame = false; + remainedHearts--; + nextX = paddle.x + paddle.length/2; + nextY = paddle.y - 1; + ball.speedx = 1; + } + + ball.oldx = ball.x; + ball.oldy = ball.y; + + ball.x = nextX; + ball.y = nextY; + + //برخورد توپ با راکت + if(ball.y==paddle.y-1 && ballLeft >= paddle.x && ballRight <= paddle.x + paddle.length && activeGame) { + + ball.vy = -1; + + int hit = (ball.x + ballWidth - 1) - paddle.x; + + if (hit <= 2) ball.vx = -2; + else if (hit <= 3) ball.vx = -1; + else if (hit >= 6) ball.vx = 2; + else if (hit >= 5) ball.vx = 1; + else ball.vx = 0; //hit=4 + } +} + + + +int remainedBlocks() { + int count = 0; + for(int i=0 ; i<6 ; i++){ + for(int j=0 ; j<10 ; j++){ + if(blockAlive[i][j]) count++; + } + } + return count; +} + + + +void gameLoop() { + + drawBoard(42, 25); + if(!loadedGame) setupGame(); + loadedGame=false; + drawGameScreen(); + + int frameCounter=0; + + while(true) { + + handleInput(); + + if(gamePaused) { + drawBoard(42, 25); + drawGameScreen(); + gamePaused=false; + } + + if(quitGame) return; + + if(activeGame && frameCounter%6==0) { + updateBallLocation(); + } + if(remainedBlocks()==0) { + showGameResult(true); + return; + } + if(remainedHearts==0) { + showGameResult(false); + return; + } + + frameCounter++; + updateGameScreen(); + Sleep(10); + } +} + +void updateGameScreen() { + + + if(ball.x != ball.oldx || activeGame) { + + //پاک کردن جای قبلی توپ + setCursorPosition(ball.oldx, ball.oldy); + cout<<" "; + //رسم دوباره توپ + setCursorPosition(ball.x, ball.y); + cout<<"\033[0m"<paddle.oldx) {//راکت یه سمت راست حرکت کرده است + + for(int i = paddle.oldx ; i < paddle.x ; i++) { + setCursorPosition(i, paddle.y); + cout<<" "; + } + } + + else {//راکت یه سمت چپ حرکت کرده است + + for(int i = paddle.x + paddle.length; i < paddle.oldx + paddle.length; i++) { + setCursorPosition(i, paddle.y); + cout<<" "; + } + } + + //رسم دوباره راکت + setCursorPosition(paddle.x, paddle.y); + for(int i=0 ; i0) currentOption--; + updatePauseMenu(previousOption, currentOption); + break; + + case 's' : case 'S' : + previousOption = currentOption; + if(currentOption<3) currentOption++; + updatePauseMenu(previousOption, currentOption); + break; + + case 13 : + switch (currentOption) { + case 0: //continue + totalPauseSecond+=difftime(time(0),pauseStartTime); + return; + case 1: //restart + setupGame(); + drawGameScreen(); + return; + case 2: //save + drawBoard(42, 10); + if(saveGame()){ + setCursorPosition(startX + boardLength/2 -4 , startY + 4); + cout<<"SAVED✅"; + Sleep(1000); + } + else{ + setCursorPosition(startX + boardLength/2 - 9, startY + 5); + cout<<"SAVE FAILED❌"; + Sleep(1000); + } + drawPauseMenu(currentOption); + break; + case 3: //quit + quitGame = true; + return; + } + } + } +} + + +void drawPauseMenu(int currentOption) { + + drawBoard(42, 20); + + for(int i=0 ; i<4 ; i++) { + setCursorPosition(startX + boardLength/2 - pauseMenuOption[i].size()/2, startY + 3 + (i*4)); + if(i==currentOption) { + cout<<"\033[38;5;198m"<> saveUser; + if(saveUser != users[currentUser].userName){ + file.close(); + return false; + } + int total; + file>>total; + file>>game[countGame].score; + file>>remainedHearts; + file>>activeGame; + file>>paddle.x>>paddle.y>>paddle.oldx>>paddle.length; + file>>ball.x>>ball.y>>ball.oldx>>ball.oldy>>ball.vx>>ball.vy>>ball.speedx>>ball.speedy; + for(int i=0 ; i<6 ; i++){ + for(int j=0;j<10;j++){ + file>>blockAlive[i][j]; + } + } + file.close(); + gameStartTime=time(0)-total; + totalPauseSecond=0; + timerStarted=true; + loadedGame=true; + return true; +} + + +void drawHelp(){ + + drawBoard(70, 26); + + + int x = startX + 2; + int y = startY + 1; + + setCursorPosition(x, y); + cout << "HOW TO PLAY BREAKOUT"; + + y++; + + setCursorPosition(x, y++); + cout << "You control a paddle at the bottom of the screen."; + + setCursorPosition(x, y++); + cout << "A ball bounces around and breaks the blocks above."; + + y++; + + setCursorPosition(x, y++); + cout << "GOAL:"; + setCursorPosition(x, y++); + cout << "- Destroy all blocks without losing the ball."; + + y++; + + setCursorPosition(x, y++); + cout << "CONTROLS:"; + setCursorPosition(x, y++); + cout << "A : Move paddle left"; + setCursorPosition(x, y++); + cout << "D : Move paddle right"; + setCursorPosition(x, y++); + cout << "SPACE : Start the ball"; + setCursorPosition(x, y++); + cout << "P : Pause the game"; + + y++; + + setCursorPosition(x, y++); + cout << "RULES:"; + setCursorPosition(x, y++); + cout << "- If the ball falls, you lose one heart."; + setCursorPosition(x, y++); + cout << "- You start with 3 hearts."; + + y++; + + setCursorPosition(x, y++); + cout << "WIN / LOSE:"; + setCursorPosition(x, y++); + cout << "- Break all blocks to win."; + setCursorPosition(x, y++); + cout << "- Lose all hearts to lose."; + + y += 2; + setCursorPosition(x, y++); + cout << "Press any key to return to menu..."; + + getch(); +} + + +void showGameTime(){ + + if(!timerStarted) return; + time_t now=time(0); + int playedSeconds; + if(gamePaused) playedSeconds=difftime(pauseStartTime,gameStartTime)-totalPauseSecond; + else playedSeconds=difftime(now,gameStartTime)-totalPauseSecond; + if(playedSeconds<0) playedSeconds=0; + int min= playedSeconds / 60; + int sec= playedSeconds % 60; + + cout<<"Time: "; + if(min<10) cout<<"0"; + cout<