commit e08d9401b51a30410a9fe62281d51930ab00d5b9 Author: Your Name Date: Mon Apr 13 21:57:07 2026 +0330 final project diff --git a/Breakout.cpp b/Breakout.cpp new file mode 100644 index 0000000..3ec255d --- /dev/null +++ b/Breakout.cpp @@ -0,0 +1,617 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +const int SCREEN_WIDTH = 80; +const int SCREEN_HEIGHT = 26; +const int FIELD_START_Y = 2; +const int MAX_BRICKS = 300; +const int MAX_POWERUPS = 20; +const int MAX_SCORES = 50; + +const string FILE_HIGHSCORE = "scores.txt"; +const string FILE_SAVEGAME = "save.dat"; +const string FILE_HISTORY = "history.txt"; + +enum ConsoleColor { + BLACK = 0, BLUE = 1, GREEN = 2, CYAN = 3, RED = 4, MAGENTA = 5, BROWN = 6, + LIGHTGRAY = 7, DARKGRAY = 8, LIGHTBLUE = 9, LIGHTGREEN = 10, LIGHTCYAN = 11, + LIGHTRED = 12, LIGHTMAGENTA = 13, YELLOW = 14, WHITE = 15 +}; + +struct Vec2 { + float x, y; +}; + +struct Brick { + int x, y; + int hp; + int maxHp; + bool active; + int color; +}; + +struct Ball { + Vec2 pos; + Vec2 vel; + bool active; + bool stuckToPaddle; +}; + +struct PowerUp { + Vec2 pos; + char type; + int color; + bool active; +}; + +struct GameState { + int score; + int lives; + int level; + string playerName; + bool isGameOver; + bool isVictory; + int paddleWidth; + float paddleX; +}; + +struct ScoreEntry { + string name; + int score; + int level; +}; + +GameState game; +Ball ball; + +Brick bricks[MAX_BRICKS]; +int brickCount = 0; + +PowerUp powerups[MAX_POWERUPS]; +int powerupCount = 0; + +char screenBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; +int colorBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; +char prevBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; +int prevColorBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; + +int gameSpeed = 30; +string systemMessage = ""; +int messageTimer = 0; + +void SetupConsole() { + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_CURSOR_INFO cursorInfo; + GetConsoleCursorInfo(hOut, &cursorInfo); + cursorInfo.bVisible = FALSE; + SetConsoleCursorInfo(hOut, &cursorInfo); + + string cmd = "mode con cols=" + to_string(SCREEN_WIDTH) + " lines=" + to_string(SCREEN_HEIGHT); + system(cmd.c_str()); + SetConsoleTitle("Breakout - Final Project"); +} + +void GotoXY(int x, int y) { + COORD coord; + coord.X = x; + coord.Y = y; + SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); +} + +void SetColor(int color) { + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); +} + +void ShowCursor(bool show) { + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_CURSOR_INFO cursorInfo; + GetConsoleCursorInfo(hOut, &cursorInfo); + cursorInfo.bVisible = show; + SetConsoleCursorInfo(hOut, &cursorInfo); +} + +void InitBuffers() { + for(int y=0; y= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) { + screenBuffer[y][x] = ch; + colorBuffer[y][x] = color; + } +} + +void DrawTextToBuffer(int x, int y, string text, int color) { + for(size_t i=0; i 8) rows = 8; + + for(int i=0; i= SCREEN_HEIGHT - 1 && powerups[i].pos.y < SCREEN_HEIGHT + 1) { + if(powerups[i].pos.x >= game.paddleX && powerups[i].pos.x <= game.paddleX + game.paddleWidth) { + powerups[i].active = false; + game.score += 100; + + if(powerups[i].type == 'W') { + game.paddleWidth += 4; + if(game.paddleWidth > 20) game.paddleWidth = 20; + ShowMessage("WIDE PADDLE!"); + } + else if(powerups[i].type == 'L') { + game.lives++; + ShowMessage("EXTRA LIFE!"); + } + else if(powerups[i].type == 'F') { + game.score += 500; + ShowMessage("BONUS POINTS!"); + } + } + } + + if(powerups[i].pos.y > SCREEN_HEIGHT) powerups[i].active = false; + } +} + +void UpdateBall() { + if(ball.stuckToPaddle) { + ball.pos.x = game.paddleX + game.paddleWidth / 2; + ball.pos.y = SCREEN_HEIGHT - 2; + return; + } + + ball.pos.x += ball.vel.x; + ball.pos.y += ball.vel.y; + + if(ball.pos.x <= 1) { + ball.pos.x = 1; + ball.vel.x *= -1; + } + if(ball.pos.x >= SCREEN_WIDTH - 2) { + ball.pos.x = SCREEN_WIDTH - 2; + ball.vel.x *= -1; + } + if(ball.pos.y <= FIELD_START_Y) { + ball.pos.y = FIELD_START_Y; + ball.vel.y *= -1; + } + + if(ball.pos.y >= SCREEN_HEIGHT) { + game.lives--; + if(game.lives <= 0) { + game.isGameOver = true; + } else { + ball.stuckToPaddle = true; + for(int i=0; i= SCREEN_HEIGHT - 2 && ball.pos.y < SCREEN_HEIGHT) { + if(ball.pos.x >= game.paddleX && ball.pos.x <= game.paddleX + game.paddleWidth) { + ball.vel.y = -fabs(ball.vel.y); + float hitPos = (ball.pos.x - (game.paddleX + game.paddleWidth / 2)); + ball.vel.x = hitPos * 0.25f; + + // Prevent sticking + ball.pos.y = SCREEN_HEIGHT - 2; + } + } + + bool levelCleared = true; + for(int i=0; i= bricks[i].x && ball.pos.x < bricks[i].x + 4 && (int)ball.pos.y == bricks[i].y) { + ball.vel.y *= -1; + bricks[i].hp--; + game.score += 10; + + if(bricks[i].hp == 2) bricks[i].color = YELLOW; + else if(bricks[i].hp == 1) bricks[i].color = LIGHTGREEN; + + if(bricks[i].hp <= 0) { + bricks[i].active = false; + game.score += 50; + SpawnPowerUp(bricks[i].x + 1, bricks[i].y); + } + break; + } + } + + if(levelCleared) { + game.score += 1000; + InitLevel(game.level + 1); + ShowMessage("LEVEL COMPLETE!"); + } +} + +void SaveGame() { + ofstream f(FILE_SAVEGAME); + if(f.is_open()) { + f << game.playerName << endl; + f << game.score << " " << game.lives << " " << game.level << endl; + f << game.paddleX << " " << game.paddleWidth << endl; + f << ball.pos.x << " " << ball.pos.y << " " << ball.vel.x << " " << ball.vel.y << " " << ball.stuckToPaddle << endl; + + f << brickCount << endl; + for(int i=0; i> game.score >> game.lives >> game.level; + f >> game.paddleX >> game.paddleWidth; + f >> ball.pos.x >> ball.pos.y >> ball.vel.x >> ball.vel.y >> ball.stuckToPaddle; + ball.active = true; + + f >> brickCount; + for(int i=0; i> bricks[i].x >> bricks[i].y >> bricks[i].hp >> bricks[i].maxHp >> bricks[i].active >> bricks[i].color; + } + + f >> powerupCount; + for(int i=0; i> powerups[i].pos.x >> powerups[i].pos.y >> powerups[i].type >> powerups[i].color >> powerups[i].active; + } + + f.close(); + + game.isGameOver = false; + game.isVictory = false; + + return true; + } + return false; +} + +void SaveHistory(string result) { + ofstream f(FILE_HISTORY, ios::app); + if(f.is_open()) { + time_t now = time(0); + string dt = ctime(&now); + dt = dt.substr(0, dt.length() - 1); + f << game.playerName << " | " << result << " | Sc: " << game.score << " | Lv: " << game.level << " | " << dt << endl; + f.close(); + } +} + +void SaveHighScore() { + ofstream f(FILE_HIGHSCORE, ios::app); + if(f.is_open()) { + f << game.playerName << " " << game.score << " " << game.level << endl; + f.close(); + } +} + +void RunGame() { + InitBuffers(); + systemMessage = ""; + messageTimer = 0; + + while(!game.isGameOver && !game.isVictory) { + if(_kbhit()) { + char k = _getch(); + if(k == 'a' || k == 'A') game.paddleX -= 2.5; + if(k == 'd' || k == 'D') game.paddleX += 2.5; + if(k == ' ') ball.stuckToPaddle = false; + + if(k == 'p' || k == 'P') { + ShowMessage("PAUSED"); + ClearBuffer(); + DrawTextToBuffer(35, 12, "PAUSED", RED); + RenderFrame(); + _getch(); + } + + if(k == 's' || k == 'S') SaveGame(); + if(k == 27) break; + } + + if(game.paddleX < 1) game.paddleX = 1; + if(game.paddleX + game.paddleWidth > SCREEN_WIDTH - 2) + game.paddleX = SCREEN_WIDTH - 1 - game.paddleWidth; + + UpdateBall(); + CheckCollisions(); + UpdatePowerUps(); + + ClearBuffer(); + + for(int x=0; x 0) { + DrawTextToBuffer(SCREEN_WIDTH/2 - systemMessage.length()/2, SCREEN_HEIGHT/2, systemMessage, GREEN); + messageTimer--; + } + + for(int i=0; i> scores[count].name >> scores[count].score >> scores[count].level) { + count++; + } + f.close(); + + for(int i=0; i> game.playerName; + ShowCursor(false); + + game.score = 0; + game.lives = 3; + InitLevel(1); + RunGame(); + } + else if(k == '2') { + if(LoadGame()) { + RunGame(); + } + else { + cout << "\n\t No save file!"; + Sleep(1000); + } + } + else if(k == '3') ShowHighScores(); + else if(k == '4') ShowSettings(); + else if(k == '5') ShowHelp(); + else if(k == '6') exit(0); + } +} + +int main() { + srand(time(0)); + SetupConsole(); + + ofstream f(FILE_HISTORY, ios::app); f.close(); + ofstream f2(FILE_HIGHSCORE, ios::app); f2.close(); + + ShowMenu(); + return 0; +}