#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; }