5 Commits

23 changed files with 392 additions and 94 deletions
+89 -17
View File
@@ -1,30 +1,102 @@
# Pong Reloaded
# 🏓 Pong Reloaded
A classic arcade game rebuilt from the ground up using modern C++ and the Raylib library.
A classic arcade game rebuilt from the ground up using **Modern C++ (C++20)** and the **Raylib** library. Designed with performance, customization, and modularity in mind.
[![C++ Version](https://img.shields.io/badge/Language-C%2B%2B20-blue?logo=c%2B%2B&style=flat-square)](https://en.cppreference.com/w/cpp/compiler_support/20)
[![Raylib](https://img.shields.io/badge/Framework-Raylib--5.0-red?style=flat-square)](https://www.raylib.com/)
[![Platform](https://img.shields.io/badge/Platform-Windows-0078d7?logo=windows&style=flat-square)](https://www.microsoft.com/windows)
[![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](LICENSE.txt)
---
## Credits & Attributions
## 🚀 About & Motivation
### Code Base Template
**Pong Reloaded** is an educational and exploratory game development project. Built as a student engineering activity, it serves as a personal challenge to push past standard university curricula and experiment with low-level graphic structures.
The key goals of this project:
* **Master Raylib & Game Design:** Experiment with 2D rendering pipelines, sound synthesis, custom sprites, and real-time game state management.
* **Optimize for Low Resources:** Implement performance-first programming in C++, minimizing memory overhead and achieving a highly optimized execution path that runs perfectly on low-end laptops.
* **Challenge Knowledge Boundaries:** Apply advanced Object-Oriented Programming (OOP) concepts, robust state machine architectures, and direct Win32 API integrations.
> [!NOTE]
> This project is developed within the **Helios Additional Activity** initiative at the **Technical University of Cluj-Napoca (UTCN)** *Faculty of Electronics, Telecommunications and Information Technology (ETTI)*.
---
## 🎮 Features
* **👾 Zero-Dependency Portability:** The game compiles into a single, standalone executable. Textures, audio, and icon files are embedded directly into the binary's resource section (`.rc`) using Windows PE resources, ensuring it never breaks due to missing assets.
* **⚡ Highly Optimized Engine:** Optimized game loops, zero-allocation physics updates in the hot-path, and efficient CPU sleeping patterns. Smooth frame rates on hardware with minimal system resources.
* **🤖 Dynamic AI Opponent:** A singleplayer CPU opponent powered by adaptive movement algorithms with three difficulty settings: **Easy**, **Normal**, and **Hard**.
* **👥 Local Co-op Multiplayer:** Local 1v1 couch-co-op mode featuring an interactive **Multiplayer Lobby** where both players toggle ready states before initiating the match.
* **🎨 Premium Settings & Theme Engine:**
* **Screen Settings:** Customizable resolutions (1280x800, 1600x900, 1920x1080) and Fullscreen / Windowed modes.
* **Framerate Controls:** Selectable 60 FPS, 144 FPS, or VSync limits.
* **Score Limits:** Custom thresholds (5, 11, 15, or 21 points).
* **Visual Themes:** Toggleable background space themes (Default, Galaxy, Trap), custom ball skins, and paddle textures.
* **🛠️ Developer Console:** Built-in console overlay toggled dynamically via `F7` for real-time engine tracing and logging.
---
## 💾 Quick Install & Play
No installers, DLLs, or complex dependency installations are required!
1. Head over to the [**Latest Releases**](https://github.com/Dr20Ervin/Pong-Reloaded/releases) section.
2. Download the `pong-reloaded.exe` binary.
3. Double-click the `.exe` and start playing immediately!
---
## ⌨️ Controls Reference
| Context | Action / Key | Player 1 (Left Paddle) | Player 2 / Singleplayer (Right Paddle) |
| :--- | :--- | :--- | :--- |
| **Menu Navigation** | Up / Down | — | `Arrow Up` / `Arrow Down` |
| **Menu Confirm** | Enter | — | `Enter` |
| **Lobby Ready Toggle** | Ready State | `W` | `Arrow Up` |
| **Gameplay Movement**| Up / Down | `W` / `S` | `Arrow Up` / `Arrow Down` |
| **Gameplay Actions** | Pause / Resume | — | `P` |
| **Gameplay Actions** | Force Game Over / Reset | — | `Space` |
| **Developer Tools** | Toggle Dev Console | — | `F7` |
---
## 🛠️ Building From Source
### Prerequisites
* Windows OS
* Visual Studio 2022 (with "Desktop development with C++" workload)
* Raylib library installed/configured
### Steps
1. Clone this repository:
```bash
git clone https://github.com/Dr20Ervin/Pong-Reloaded.git
cd Pong-Reloaded
```
2. Open `pong-reloaded.sln` in Visual Studio.
3. Build the project under `Release` / `x64` configuration to generate the optimized, standalone executable.
---
## 📜 Credits & Attributions
### Codebase Template
* **Original Author:** educ8s (Nick Koumaris)
* **Original Repository:** [github.com/educ8s/Cpp-Pong-Game-Raylib](https://github.com/educ8s/Cpp-Pong-Game-Raylib)
* **Description:** This project utilizes core architectural patterns and Raylib integrations adapted from the original open-source template repository listed above.
* **Original Repository:** [educ8s/Cpp-Pong-Game-Raylib](https://github.com/educ8s/Cpp-Pong-Game-Raylib)
* This project adapts core architectural concepts and Raylib integrations from the original repository.
### Asset Credits & Attribution
The multimedia resources (graphics, sprites, and audio files) used in this game are not my personal creations. They have been curated and adapted from the open-source community for educational development.
### Asset Credits
The graphical and audio resources used in this game were curated from the open-source gaming community:
* **Original Project:** Moddable Pong
* **Author / Creator:** Endless OS Foundation & Endless Studios
* **Author/Creator:** Endless OS Foundation & Endless Studios
* **Website:** [endlessos.org](https://endlessos.org)
* **Source Code Repository:** [github.com/endlessm/moddable-pong](https://github.com/endlessm/moddable-pong/)
* **Usage Notice:** These assets are integrated strictly for non-commercial, academic layout validation and development milestones. All rights, ownership, and copyrights belong entirely to the original creators at the Endless OS Foundation.
* **Repository:** [endlessm/moddable-pong](https://github.com/endlessm/moddable-pong/)
* *Notice:* All assets are utilized strictly for academic research, non-commercial development, and validation milestones. All rights and copyrights belong to their respective owners.
---
## Features
## 📄 License
* **Clean Object-Oriented Architecture:** Developed using rigorous OOP principles, utilizing a base abstract `GameObject` class with virtual override structures for specialized modular entities (`Ball`, `Paddle`, and `CpuPaddle`).
* **Robust Game State Machine:** Features an integrated game loop managing distinct application states including `MainMenu`, `DifficultySelect`, `Multiplayer`, `Settings`, and `Playing`.
* **Dynamic AI Opponent:** A singleplayer CPU opponent equipped with configurable movement velocities tailored across three distinct difficulty tiers: Easy, Normal, and Hard.
* **Enhanced Visual Layout:** Fully upgraded with high-resolution texture mapping via Raylib's `DrawTexturePro`, featuring dedicated game court backdrops, localized wall segments, styled central dashed lines, and custom entity sprites.
* **Academic Context:** This work is part of a student engineering project within the Helios Additional_activity initiative at the Technical University of Cluj-Napoca (UTCN) - Faculty of Electronics, Telecommunications and Information Technology (ETTI).
This project is licensed under the [MIT License](LICENSE.txt).
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

+8 -2
View File
@@ -42,7 +42,10 @@ struct GameConfig {
int maxScoreOption = 0; // 0 = 5, 1 = 11, 2 = 15, 3 = 21
int maxScore = 5;
bool sfxEnabled = true;
int selectedSettingLine = 0; // 0 = Resolution, 1 = Framerate, 2 = Screen Mode, 3 = Score Limit, 4 = Sound, 5 = Back
int spaceThemeOption = 0; // 0 = Default, 1 = Galaxy, 2 = Trap
int ballThemeOption = 0; // 0 = Default, 1 = Banana, 2 = Beach, 3 = Cloudy, 4 = Simple
int paddleThemeOption = 0; // 0 = Default, 1 = Cloudy, 2 = CPU, 3 = Human
int selectedSettingLine = 0; // 0 = Resolution, 1 = Framerate, 2 = Screen Mode, 3 = Score Limit, 4 = Sound, 5 = Space, 6 = Ball, 7 = Paddle, 8 = Back
};
struct GameContext {
@@ -69,12 +72,13 @@ struct GameContext {
Texture2D LoadTextureFromResource(int id);
Sound LoadSoundFromResource(int id);
void SetWindowIconFromResource(int id);
// Forward declarations
class Ball;
class Paddle;
class CpuPaddle;
void ApplyPaddleTextures(const GameContext& ctx, Paddle& player, CpuPaddle& cpu);
// Game state update and draw routines
void ResetBall(Ball& ball);
void DrawCourt(const GameContext& ctx, int screenWidth, int screenHeight);
@@ -121,6 +125,7 @@ public:
bool Update() override;
void Draw() override;
void ReloadTexture(int resourceId);
};
class Ball : public GameObject {
@@ -147,6 +152,7 @@ public:
bool Update() override;
void Draw() override;
void ReloadTexture(int resourceId);
};
class CpuPaddle : public Paddle {
+10 -10
View File
@@ -1,21 +1,21 @@
#pragma once
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <mutex>
#include <cstdarg>
#include <raylib.h>
// Global colors
Color Green = Color{ 38, 185, 154, 255 };
Color Dark_Green = Color{ 20, 160, 133, 255 };
Color Light_Green = Color{ 129, 204, 184, 255 };
Color Yellow = Color{ 243, 213, 91, 255 };
extern Color Green;
extern Color Dark_Green;
extern Color Light_Green;
extern Color Yellow;
// DevLog buffering & function prototype
std::vector<std::string> logHistory;
std::mutex logMutex;
// DevLog buffering & global variables
extern std::vector<std::string> logHistory;
extern std::mutex logMutex;
extern bool isConsoleVisible;
void CustomLogCallback(int logLevel, const char* text, va_list args);
+2 -1
View File
@@ -1,4 +1,5 @@
#pragma once
#include "game.h"
#include <string>
#include <vector>
@@ -22,7 +23,7 @@ public:
// Settings and lobby routines
void ApplyResolution(int width, int height);
void ApplyFramerate(int option);
void UpdateSettingsState(GameContext& ctx);
void UpdateSettingsState(GameContext& ctx, Ball& ball, Paddle& player, CpuPaddle& cpu);
void DrawSettingsState(const GameContext& ctx, int screenWidth, int screenHeight);
void UpdateLobbyState(GameContext& ctx);
void DrawLobbyState(const GameContext& ctx, int screenWidth, int screenHeight);
+26 -11
View File
@@ -1,15 +1,30 @@
#pragma once
#define IDI_ICON1 100
#define IDI_ICON1 100
#define IDR_TEX_BASIC_SPACE 101
#define IDR_TEX_WALLS 102
#define IDR_TEX_LINE 103
#define IDR_TEX_BALL 104
#define IDR_TEX_PADDLE 105
#define IDR_TEX_PADDLE2 106
// Backgrounds
#define IDR_TEX_SPACE_DEFAULT 101
#define IDR_TEX_SPACE_GALAXY 107
#define IDR_TEX_SPACE_TRAP 108
#define IDR_TEX_WALLS 102
#define IDR_TEX_LINE 103
// Balls
#define IDR_TEX_BALL_DEFAULT 104
#define IDR_TEX_BALL_BANANA 109
#define IDR_TEX_BALL_BEACH 110
#define IDR_TEX_BALL_CLOUDY 111
#define IDR_TEX_BALL_SIMPLE 112
// Paddles
#define IDR_TEX_PADDLE_DEFAULT 105
#define IDR_TEX_PADDLE_CLOUDY 106
#define IDR_TEX_PADDLE_CPU 113
#define IDR_TEX_PADDLE_HUMAN 114
// Audio
#define IDR_SND_PADDLE_HIT 201
#define IDR_SND_WALL_HIT 202
#define IDR_SND_SCORE 203
#define IDR_SND_BANANA 204
#define IDR_SND_PADDLE_HIT 201
#define IDR_SND_WALL_HIT 202
#define IDR_SND_SCORE 203
#define IDR_SND_BANANA 204
+12 -4
View File
@@ -153,11 +153,19 @@
</ItemGroup>
<ItemGroup>
<Image Include="assets\icon.ico" />
<Image Include="assets\textures\ball\basic_ball_5.png" />
<Image Include="assets\textures\ball\banana_ball.png" />
<Image Include="assets\textures\ball\beach_ball.png" />
<Image Include="assets\textures\ball\cloudy__ball.png" />
<Image Include="assets\textures\ball\default_ball.png" />
<Image Include="assets\textures\ball\simple_ball.png" />
<Image Include="assets\textures\hud\line.png" />
<Image Include="assets\textures\paddles\basic_paddle.png" />
<Image Include="assets\textures\paddles\basic_paddle_2.png" />
<Image Include="assets\textures\spaces\basic_space.png" />
<Image Include="assets\textures\paddles\cloudy_paddle.png" />
<Image Include="assets\textures\paddles\cpu_paddle.png" />
<Image Include="assets\textures\paddles\default_paddle.png" />
<Image Include="assets\textures\paddles\human_paddle.png" />
<Image Include="assets\textures\spaces\default_space.png" />
<Image Include="assets\textures\spaces\galaxy_space.png" />
<Image Include="assets\textures\spaces\trap_space.png" />
<Image Include="assets\textures\spaces\walls.png" />
</ItemGroup>
<ItemGroup>
+54 -18
View File
@@ -22,6 +22,18 @@
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Resource Files\textures\ball">
<UniqueIdentifier>{89cd7d41-de3f-4bd4-a3b4-c7aab8c5ffe9}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files\textures\hud">
<UniqueIdentifier>{3a7b2941-0560-4ab7-b8eb-255ef37bd3a4}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files\textures\paddles">
<UniqueIdentifier>{03e96034-55b9-4397-9f56-467d7c351e20}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files\textures\spaces">
<UniqueIdentifier>{5b322e46-cf19-4411-bcdd-5c3ca59fc236}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp">
@@ -61,27 +73,51 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="assets\textures\ball\basic_ball_5.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\hud\line.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\paddles\basic_paddle.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\paddles\basic_paddle_2.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\spaces\basic_space.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\spaces\walls.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\icon.ico">
<Filter>Resource Files</Filter>
</Image>
<Image Include="assets\textures\ball\banana_ball.png">
<Filter>Resource Files\textures\ball</Filter>
</Image>
<Image Include="assets\textures\ball\beach_ball.png">
<Filter>Resource Files\textures\ball</Filter>
</Image>
<Image Include="assets\textures\ball\cloudy__ball.png">
<Filter>Resource Files\textures\ball</Filter>
</Image>
<Image Include="assets\textures\ball\default_ball.png">
<Filter>Resource Files\textures\ball</Filter>
</Image>
<Image Include="assets\textures\ball\simple_ball.png">
<Filter>Resource Files\textures\ball</Filter>
</Image>
<Image Include="assets\textures\hud\line.png">
<Filter>Resource Files\textures\hud</Filter>
</Image>
<Image Include="assets\textures\paddles\cloudy_paddle.png">
<Filter>Resource Files\textures\paddles</Filter>
</Image>
<Image Include="assets\textures\paddles\cpu_paddle.png">
<Filter>Resource Files\textures\paddles</Filter>
</Image>
<Image Include="assets\textures\paddles\default_paddle.png">
<Filter>Resource Files\textures\paddles</Filter>
</Image>
<Image Include="assets\textures\paddles\human_paddle.png">
<Filter>Resource Files\textures\paddles</Filter>
</Image>
<Image Include="assets\textures\spaces\walls.png">
<Filter>Resource Files\textures\spaces</Filter>
</Image>
<Image Include="assets\textures\spaces\default_space.png">
<Filter>Resource Files\textures\spaces</Filter>
</Image>
<Image Include="assets\textures\spaces\galaxy_space.png">
<Filter>Resource Files\textures\spaces</Filter>
</Image>
<Image Include="assets\textures\spaces\trap_space.png">
<Filter>Resource Files\textures\spaces</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<None Include="assets\audio\banana_wall_hit.ogg">
+26 -11
View File
@@ -1,15 +1,30 @@
#include "include/resource.h"
IDI_ICON1 ICON "assets/icon.ico"
IDI_ICON1 ICON "assets/icon.ico"
IDR_TEX_BASIC_SPACE RCDATA "assets/textures/spaces/basic_space.png"
IDR_TEX_WALLS RCDATA "assets/textures/spaces/walls.png"
IDR_TEX_LINE RCDATA "assets/textures/hud/line.png"
IDR_TEX_BALL RCDATA "assets/textures/ball/basic_ball_5.png"
IDR_TEX_PADDLE RCDATA "assets/textures/paddles/basic_paddle.png"
IDR_TEX_PADDLE2 RCDATA "assets/textures/paddles/basic_paddle_2.png"
// Backgrounds
IDR_TEX_SPACE_DEFAULT RCDATA "assets/textures/spaces/default_space.png"
IDR_TEX_SPACE_GALAXY RCDATA "assets/textures/spaces/galaxy_space.png"
IDR_TEX_SPACE_TRAP RCDATA "assets/textures/spaces/trap_space.png"
IDR_TEX_WALLS RCDATA "assets/textures/spaces/walls.png"
IDR_TEX_LINE RCDATA "assets/textures/hud/line.png"
// Balls
IDR_TEX_BALL_DEFAULT RCDATA "assets/textures/ball/default_ball.png"
IDR_TEX_BALL_BANANA RCDATA "assets/textures/ball/banana_ball.png"
IDR_TEX_BALL_BEACH RCDATA "assets/textures/ball/beach_ball.png"
IDR_TEX_BALL_CLOUDY RCDATA "assets/textures/ball/cloudy__ball.png"
IDR_TEX_BALL_SIMPLE RCDATA "assets/textures/ball/simple_ball.png"
// Paddles
IDR_TEX_PADDLE_DEFAULT RCDATA "assets/textures/paddles/default_paddle.png"
IDR_TEX_PADDLE_CLOUDY RCDATA "assets/textures/paddles/cloudy_paddle.png"
IDR_TEX_PADDLE_CPU RCDATA "assets/textures/paddles/cpu_paddle.png"
IDR_TEX_PADDLE_HUMAN RCDATA "assets/textures/paddles/human_paddle.png"
// Audio
IDR_SND_PADDLE_HIT RCDATA "assets/audio/paddle_hit.ogg"
IDR_SND_WALL_HIT RCDATA "assets/audio/wall_hit.ogg"
IDR_SND_SCORE RCDATA "assets/audio/score.ogg"
IDR_SND_BANANA RCDATA "assets/audio/banana_wall_hit.ogg"
IDR_SND_PADDLE_HIT RCDATA "assets/audio/paddle_hit.ogg"
IDR_SND_WALL_HIT RCDATA "assets/audio/wall_hit.ogg"
IDR_SND_SCORE RCDATA "assets/audio/score.ogg"
IDR_SND_BANANA RCDATA "assets/audio/banana_wall_hit.ogg"
+41 -2
View File
@@ -1,4 +1,6 @@
#include "game.h"
#include "main.h"
#include "resource.h"
// Paddle implementation
bool Paddle::Update() {
@@ -40,6 +42,17 @@ void Paddle::Draw() {
}
}
void Paddle::ReloadTexture(int resourceId) {
if (texture.id > 0) {
UnloadTexture(texture);
}
if (resourceId > 0) {
texture = LoadTextureFromResource(resourceId);
} else {
texture = { 0 };
}
}
// Ball implementation
bool Ball::Update() {
@@ -69,6 +82,17 @@ void Ball::Draw() {
}
}
void Ball::ReloadTexture(int resourceId) {
if (texture.id > 0) {
UnloadTexture(texture);
}
if (resourceId > 0) {
texture = LoadTextureFromResource(resourceId);
} else {
texture = { 0 };
}
}
// Game state routines and helpers
void ResetBall(Ball& ball) {
@@ -321,7 +345,7 @@ void DrawGameOverState(const GameContext& ctx, int screenWidth, int screenHeight
if (ctx.isMultiplayer) {
if (ctx.score.player_score > ctx.score.player2_score) {
winnerText = "PLAYER 1 WINS!";
winnerColor = Color{ 38, 185, 154, 255 };
winnerColor = Green;
}
else if (ctx.score.player2_score > ctx.score.player_score) {
winnerText = "PLAYER 2 WINS!";
@@ -333,7 +357,7 @@ void DrawGameOverState(const GameContext& ctx, int screenWidth, int screenHeight
else {
if (ctx.score.player_score > ctx.score.cpu_score) {
winnerText = "YOU WIN!";
winnerColor = Color{ 38, 185, 154, 255 };
winnerColor = Green;
}
else if (ctx.score.cpu_score > ctx.score.player_score) {
winnerText = "CPU WINS!";
@@ -360,3 +384,18 @@ void DrawGameOverState(const GameContext& ctx, int screenWidth, int screenHeight
int instWidth = MeasureText("Press SPACEBAR to return to Main Menu", 20);
DrawText("Press SPACEBAR to return to Main Menu", screenWidth / 2 - instWidth / 2, panelY + 340, 20, YELLOW);
}
void ApplyPaddleTextures(const GameContext& ctx, Paddle& player, CpuPaddle& cpu) {
int playerResId = IDR_TEX_PADDLE_DEFAULT;
if (ctx.config.paddleThemeOption == 1) playerResId = IDR_TEX_PADDLE_CLOUDY;
else if (ctx.config.paddleThemeOption == 2) playerResId = IDR_TEX_PADDLE_CPU;
else if (ctx.config.paddleThemeOption == 3) playerResId = IDR_TEX_PADDLE_HUMAN;
player.ReloadTexture(playerResId);
if (ctx.isMultiplayer) {
cpu.ReloadTexture(playerResId);
} else {
cpu.ReloadTexture(IDR_TEX_PADDLE_CPU);
}
}
+25 -6
View File
@@ -3,13 +3,24 @@
#include "menu.h"
#include "resource.h"
constexpr const char* gameVersion = "0.9.1";
// Define global colors
Color Green = Color{ 38, 185, 154, 255 };
Color Dark_Green = Color{ 20, 160, 133, 255 };
Color Light_Green = Color{ 129, 204, 184, 255 };
Color Yellow = Color{ 243, 213, 91, 255 };
std::vector<std::string> logHistory;
std::mutex logMutex;
bool isConsoleVisible = false;
#ifdef _WIN32
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
extern "C" {
int __stdcall AllocConsole();
int __stdcall FreeConsole();
}
bool isConsoleVisible = false;
#endif
int main() {
@@ -27,7 +38,7 @@ int main() {
GameContext ctx;
// Asset textures loading
ctx.courtBackground = LoadTextureFromResource(IDR_TEX_BASIC_SPACE);
ctx.courtBackground = LoadTextureFromResource(IDR_TEX_SPACE_DEFAULT);
ctx.wallsTexture = LoadTextureFromResource(IDR_TEX_WALLS);
ctx.lineTexture = LoadTextureFromResource(IDR_TEX_LINE);
@@ -40,21 +51,21 @@ int main() {
Ball ball(
Vector2{ screen_width / 2.0f, screen_height / 2.0f },
Yellow, 20.0f,
IDR_TEX_BALL
IDR_TEX_BALL_DEFAULT
);
ResetBall(ball);
Paddle player(
Vector2{ screen_width - 20.0f - 10.0f - 25.0f, screen_height / 2.0f - 60.0f },
WHITE, 25.0f, 120.0f,
IDR_TEX_PADDLE
IDR_TEX_PADDLE_DEFAULT
);
CpuPaddle cpu(
Vector2{ 20.0f + 10.0f, screen_height / 2.0f - 60.0f },
WHITE, 25.0f, 120.0f,
Difficulty::Normal,
IDR_TEX_PADDLE2
IDR_TEX_PADDLE_DEFAULT
);
// Menu instantiation
@@ -133,6 +144,7 @@ int main() {
ctx.isPaused = false;
ResetBall(ball);
ctx.currentState = GameState::Playing;
ApplyPaddleTextures(ctx, player, cpu);
}
else if (selected == 3) {
ctx.currentState = GameState::MainMenu;
@@ -140,13 +152,19 @@ int main() {
break;
}
case GameState::MultiplayerLobby:
{
GameState oldState = ctx.currentState;
UpdateLobbyState(ctx);
if (ctx.currentState == GameState::Multiplayer && oldState != GameState::Multiplayer) {
ApplyPaddleTextures(ctx, player, cpu);
}
break;
}
case GameState::Multiplayer:
UpdateMultiplayerState(ctx, ball, player, cpu);
break;
case GameState::Settings:
UpdateSettingsState(ctx);
UpdateSettingsState(ctx, ball, player, cpu);
break;
case GameState::Playing:
UpdatePlayingState(ctx, ball, player, cpu);
@@ -170,6 +188,7 @@ int main() {
DrawText("Use UP/DOWN to navigate | ENTER to select",
screen_width / 2 - menuHintWidth / 2,
screen_height - 80, 20, WHITE);
DrawText(TextFormat("v%s", gameVersion), 20, screen_height - 40, 20, LIGHTGRAY);
break;
}
case GameState::DifficultySelect:
+99 -12
View File
@@ -1,4 +1,6 @@
#include "menu.h"
#include "main.h"
#include "resource.h"
int Menu::Update() {
// Menu navigation
@@ -63,15 +65,15 @@ void ApplyFramerate(int option) {
}
}
void UpdateSettingsState(GameContext& ctx) {
void UpdateSettingsState(GameContext& ctx, Ball& ball, Paddle& player, CpuPaddle& cpu) {
// Menu navigation
if (IsKeyPressed(KEY_UP)) {
ctx.config.selectedSettingLine--;
if (ctx.config.selectedSettingLine < 0) ctx.config.selectedSettingLine = 5;
if (ctx.config.selectedSettingLine < 0) ctx.config.selectedSettingLine = 8;
}
if (IsKeyPressed(KEY_DOWN)) {
ctx.config.selectedSettingLine++;
if (ctx.config.selectedSettingLine > 5) ctx.config.selectedSettingLine = 0;
if (ctx.config.selectedSettingLine > 8) ctx.config.selectedSettingLine = 0;
}
// Settings adjustments (LEFT/RIGHT or ENTER)
@@ -124,6 +126,67 @@ void UpdateSettingsState(GameContext& ctx) {
}
}
else if (ctx.config.selectedSettingLine == 5) {
bool changed = false;
if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_ENTER)) {
ctx.config.spaceThemeOption = (ctx.config.spaceThemeOption + 1) % 3;
changed = true;
}
else if (IsKeyPressed(KEY_LEFT)) {
ctx.config.spaceThemeOption = (ctx.config.spaceThemeOption - 1 + 3) % 3;
changed = true;
}
if (changed) {
int resId = IDR_TEX_SPACE_DEFAULT;
if (ctx.config.spaceThemeOption == 1) resId = IDR_TEX_SPACE_GALAXY;
else if (ctx.config.spaceThemeOption == 2) resId = IDR_TEX_SPACE_TRAP;
if (ctx.courtBackground.id > 0) {
UnloadTexture(ctx.courtBackground);
}
ctx.courtBackground = LoadTextureFromResource(resId);
}
}
else if (ctx.config.selectedSettingLine == 6) {
bool changed = false;
if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_ENTER)) {
ctx.config.ballThemeOption = (ctx.config.ballThemeOption + 1) % 5;
changed = true;
}
else if (IsKeyPressed(KEY_LEFT)) {
ctx.config.ballThemeOption = (ctx.config.ballThemeOption - 1 + 5) % 5;
changed = true;
}
if (changed) {
int resId = IDR_TEX_BALL_DEFAULT;
if (ctx.config.ballThemeOption == 1) resId = IDR_TEX_BALL_BANANA;
else if (ctx.config.ballThemeOption == 2) resId = IDR_TEX_BALL_BEACH;
else if (ctx.config.ballThemeOption == 3) resId = IDR_TEX_BALL_CLOUDY;
else if (ctx.config.ballThemeOption == 4) resId = IDR_TEX_BALL_SIMPLE;
ball.ReloadTexture(resId);
}
}
else if (ctx.config.selectedSettingLine == 7) {
bool changed = false;
if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_ENTER)) {
ctx.config.paddleThemeOption = (ctx.config.paddleThemeOption + 1) % 4;
changed = true;
}
else if (IsKeyPressed(KEY_LEFT)) {
ctx.config.paddleThemeOption = (ctx.config.paddleThemeOption - 1 + 4) % 4;
changed = true;
}
if (changed) {
int resId = IDR_TEX_PADDLE_DEFAULT;
if (ctx.config.paddleThemeOption == 1) resId = IDR_TEX_PADDLE_CLOUDY;
else if (ctx.config.paddleThemeOption == 2) resId = IDR_TEX_PADDLE_CPU;
else if (ctx.config.paddleThemeOption == 3) resId = IDR_TEX_PADDLE_HUMAN;
player.ReloadTexture(resId);
cpu.ReloadTexture(resId);
}
}
else if (ctx.config.selectedSettingLine == 8) {
if (IsKeyPressed(KEY_ENTER)) {
ctx.currentState = GameState::MainMenu;
}
@@ -155,21 +218,45 @@ void DrawSettingsState(const GameContext& ctx, int screenWidth, int screenHeight
if (ctx.config.sfxEnabled) sfxStr += "ON";
else sfxStr += "OFF";
std::string spaceThemeStr = "Space Theme: ";
if (ctx.config.spaceThemeOption == 0) spaceThemeStr += "Default";
else if (ctx.config.spaceThemeOption == 1) spaceThemeStr += "Galaxy";
else if (ctx.config.spaceThemeOption == 2) spaceThemeStr += "Trap";
std::string ballThemeStr = "Ball Theme: ";
if (ctx.config.ballThemeOption == 0) ballThemeStr += "Default";
else if (ctx.config.ballThemeOption == 1) ballThemeStr += "Banana";
else if (ctx.config.ballThemeOption == 2) ballThemeStr += "Beach";
else if (ctx.config.ballThemeOption == 3) ballThemeStr += "Cloudy";
else if (ctx.config.ballThemeOption == 4) ballThemeStr += "Simple";
std::string paddleThemeStr = "Paddle Theme: ";
if (ctx.config.paddleThemeOption == 0) paddleThemeStr += "Default";
else if (ctx.config.paddleThemeOption == 1) paddleThemeStr += "Cloudy";
else if (ctx.config.paddleThemeOption == 2) paddleThemeStr += "CPU Style";
else if (ctx.config.paddleThemeOption == 3) paddleThemeStr += "Human Style";
// Menu options highlighting and drawing
Color resColor = (ctx.config.selectedSettingLine == 0) ? YELLOW : WHITE;
Color fpsColor = (ctx.config.selectedSettingLine == 1) ? YELLOW : WHITE;
Color modeColor = (ctx.config.selectedSettingLine == 2) ? YELLOW : WHITE;
Color scoreColor = (ctx.config.selectedSettingLine == 3) ? YELLOW : WHITE;
Color sfxColor = (ctx.config.selectedSettingLine == 4) ? YELLOW : WHITE;
Color backColor = (ctx.config.selectedSettingLine == 5) ? YELLOW : WHITE;
Color spaceColor = (ctx.config.selectedSettingLine == 5) ? YELLOW : WHITE;
Color ballColor = (ctx.config.selectedSettingLine == 6) ? YELLOW : WHITE;
Color paddleColor = (ctx.config.selectedSettingLine == 7) ? YELLOW : WHITE;
Color backColor = (ctx.config.selectedSettingLine == 8) ? YELLOW : WHITE;
int startY = screenHeight / 2 - 90;
int startY = screenHeight / 2 - 80;
DrawText(resStr.c_str(), screenWidth / 2 - MeasureText(resStr.c_str(), 30) / 2, startY, 30, resColor);
DrawText(fpsStr.c_str(), screenWidth / 2 - MeasureText(fpsStr.c_str(), 30) / 2, startY + 45, 30, fpsColor);
DrawText(modeStr.c_str(), screenWidth / 2 - MeasureText(modeStr.c_str(), 30) / 2, startY + 90, 30, modeColor);
DrawText(scoreLimitStr.c_str(), screenWidth / 2 - MeasureText(scoreLimitStr.c_str(), 30) / 2, startY + 135, 30, scoreColor);
DrawText(sfxStr.c_str(), screenWidth / 2 - MeasureText(sfxStr.c_str(), 30) / 2, startY + 180, 30, sfxColor);
DrawText("Back", screenWidth / 2 - MeasureText("Back", 30) / 2, startY + 225, 30, backColor);
DrawText(fpsStr.c_str(), screenWidth / 2 - MeasureText(fpsStr.c_str(), 30) / 2, startY + 40, 30, fpsColor);
DrawText(modeStr.c_str(), screenWidth / 2 - MeasureText(modeStr.c_str(), 30) / 2, startY + 80, 30, modeColor);
DrawText(scoreLimitStr.c_str(), screenWidth / 2 - MeasureText(scoreLimitStr.c_str(), 30) / 2, startY + 120, 30, scoreColor);
DrawText(sfxStr.c_str(), screenWidth / 2 - MeasureText(sfxStr.c_str(), 30) / 2, startY + 160, 30, sfxColor);
DrawText(spaceThemeStr.c_str(), screenWidth / 2 - MeasureText(spaceThemeStr.c_str(), 30) / 2, startY + 200, 30, spaceColor);
DrawText(ballThemeStr.c_str(), screenWidth / 2 - MeasureText(ballThemeStr.c_str(), 30) / 2, startY + 240, 30, ballColor);
DrawText(paddleThemeStr.c_str(), screenWidth / 2 - MeasureText(paddleThemeStr.c_str(), 30) / 2, startY + 280, 30, paddleColor);
DrawText("Back", screenWidth / 2 - MeasureText("Back", 30) / 2, startY + 320, 30, backColor);
int settingsHintWidth = MeasureText("UP/DOWN to navigate | LEFT/RIGHT to change settings | ENTER to select", 20);
DrawText("UP/DOWN to navigate | LEFT/RIGHT to change settings | ENTER to select",
@@ -219,7 +306,7 @@ void DrawLobbyState(const GameContext& ctx, int screenWidth, int screenHeight) {
DrawText("Press 'W' to toggle ready", p1X, p1Y + 75, 18, GRAY);
if (ctx.p1Ready) {
DrawRectangle(p1X, p1Y + 115, 220, 50, GREEN);
DrawRectangle(p1X, p1Y + 115, 220, 50, Green);
DrawText("READY", p1X + 110 - MeasureText("READY", 20)/2, p1Y + 130, 20, BLACK);
} else {
DrawRectangle(p1X, p1Y + 115, 220, 50, RED);
@@ -234,7 +321,7 @@ void DrawLobbyState(const GameContext& ctx, int screenWidth, int screenHeight) {
DrawText("Press 'UP' to toggle ready", p2X, p2Y + 75, 18, GRAY);
if (ctx.p2Ready) {
DrawRectangle(p2X, p2Y + 115, 220, 50, GREEN);
DrawRectangle(p2X, p2Y + 115, 220, 50, Green);
DrawText("READY", p2X + 110 - MeasureText("READY", 20)/2, p2Y + 130, 20, BLACK);
} else {
DrawRectangle(p2X, p2Y + 115, 220, 50, RED);