1477 lines
49 KiB
C++
1477 lines
49 KiB
C++
// ----- Headers -----
|
|
#include <iostream>
|
|
#include <windows.h>
|
|
#include <conio.h>
|
|
#include <fstream>
|
|
#include <ctime>
|
|
#include <vector>
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
|
|
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<string> 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;
|
|
}
|
|
}
|
|
|
|
|
|
|