Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2672a54b4c | |||
| ce3df94b68 | |||
| 25a7fefa04 | |||
| 1c7762ea3b | |||
| a651fa6870 | |||
| 1664939b69 | |||
| 434b9c3064 | |||
| 67f5fcf374 |
@@ -2,6 +2,10 @@
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
# Mark specific Raylib files as vendored to exclude from language stats
|
||||
include/raylib.h linguist-vendored
|
||||
include/raymath.h linguist-vendored
|
||||
include/rlgl.h linguist-vendored
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
|
||||
@@ -2,9 +2,29 @@
|
||||
|
||||
A classic arcade game rebuilt from the ground up using modern C++ and the Raylib library.
|
||||
|
||||
**Features:**
|
||||
* Clean, Object-Oriented entity architecture.
|
||||
* Game state management (Main Menu, Gameplay, Game Over).
|
||||
* A CPU AI opponent with adjustable difficulty levels (Easy, Normal, Hard).
|
||||
---
|
||||
|
||||
*This is a project developed for UTCN - ETTI Helios Additional_activity.*
|
||||
## Credits & Attributions
|
||||
|
||||
### Code Base 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.
|
||||
|
||||
### 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.
|
||||
* **Original Project:** Moddable Pong
|
||||
* **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.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
* **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).
|
||||
@@ -0,0 +1,16 @@
|
||||
# Asset Credits & Attribution
|
||||
|
||||
The multimedia resources (graphics, sprites, and audio files) contained within this folder are not my personal creations. They have been curated and adapted from the open-source community for educational development.
|
||||
|
||||
## Original Source Information
|
||||
* **Project:** Moddable Pong
|
||||
* **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. 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)**.
|
||||
|
||||
All rights, ownership, and copyrights belong entirely to the original creators at the Endless OS Foundation.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 179 B |
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
+37
-10
@@ -7,6 +7,9 @@
|
||||
// Game States & Configurations
|
||||
enum class GameState {
|
||||
MainMenu,
|
||||
DifficultySelect,
|
||||
Multiplayer,
|
||||
Settings,
|
||||
Playing,
|
||||
Paused,
|
||||
GameOver
|
||||
@@ -28,8 +31,6 @@ class GameObject {
|
||||
public:
|
||||
Vector2 position;
|
||||
Color color;
|
||||
|
||||
public:
|
||||
GameObject(Vector2 pos, Color c) : position(pos), color(c) {}
|
||||
virtual ~GameObject() = default;
|
||||
|
||||
@@ -43,9 +44,22 @@ class Paddle : public GameObject {
|
||||
public:
|
||||
float width;
|
||||
float height;
|
||||
Texture2D texture;
|
||||
|
||||
Paddle(Vector2 pos, Color c, float w, float h)
|
||||
public:
|
||||
Paddle(Vector2 pos, Color c, float w, float h, const std::string& texturePath = "")
|
||||
: GameObject(pos, c), width(w), height(h) {
|
||||
if (!texturePath.empty()) {
|
||||
texture = LoadTexture(texturePath.c_str());
|
||||
} else {
|
||||
texture = { 0 };
|
||||
}
|
||||
}
|
||||
|
||||
~Paddle() override {
|
||||
if (texture.id > 0) {
|
||||
UnloadTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
void Update() override;
|
||||
@@ -56,9 +70,22 @@ class Ball : public GameObject {
|
||||
public:
|
||||
float radius;
|
||||
Vector2 velocity;
|
||||
Texture2D texture;
|
||||
|
||||
Ball(Vector2 pos, Color c, float r)
|
||||
public:
|
||||
Ball(Vector2 pos, Color c, float r, const std::string& texturePath = "")
|
||||
: GameObject(pos, c), radius(r), velocity({ 5.0f, 5.0f }) {
|
||||
if (!texturePath.empty()) {
|
||||
texture = LoadTexture(texturePath.c_str());
|
||||
} else {
|
||||
texture = { 0 };
|
||||
}
|
||||
}
|
||||
|
||||
~Ball() override {
|
||||
if (texture.id > 0) {
|
||||
UnloadTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
void Update() override;
|
||||
@@ -71,8 +98,8 @@ private:
|
||||
Difficulty currentDifficulty;
|
||||
|
||||
public:
|
||||
CpuPaddle(Vector2 pos, Color c, float w, float h, Difficulty diff = Difficulty::Normal)
|
||||
: Paddle(pos, c, w, h) {
|
||||
CpuPaddle(Vector2 pos, Color c, float w, float h, Difficulty diff = Difficulty::Normal, const std::string& texturePath = "")
|
||||
: Paddle(pos, c, w, h, texturePath) {
|
||||
SetDifficulty(diff);
|
||||
}
|
||||
|
||||
@@ -101,11 +128,11 @@ public:
|
||||
void Update() override {}
|
||||
|
||||
void LimitMovement() {
|
||||
if (position.y <= 0) {
|
||||
position.y = 0;
|
||||
if (position.y <= 20.0f) {
|
||||
position.y = 20.0f;
|
||||
}
|
||||
if (position.y + height >= GetScreenHeight()) {
|
||||
position.y = GetScreenHeight() - height;
|
||||
if (position.y + height >= GetScreenHeight() - 20.0f) {
|
||||
position.y = GetScreenHeight() - 20.0f - height;
|
||||
}
|
||||
}
|
||||
};
|
||||
+1
-1
@@ -16,6 +16,6 @@ public:
|
||||
: title(menuTitle), options(menuOptions), selectedIndex(0) {
|
||||
}
|
||||
|
||||
void Update(GameState& currentState);
|
||||
int Update();
|
||||
void Draw();
|
||||
};
|
||||
@@ -145,6 +145,14 @@
|
||||
<ClInclude Include="include\raymath.h" />
|
||||
<ClInclude Include="include\rlgl.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="assets\textures\ball\basic_ball_5.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\spaces\walls.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
<Filter Include="Header Files\RayLib">
|
||||
<UniqueIdentifier>{2f179593-5e9f-4095-be57-3f12f03a9705}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files\textures">
|
||||
<UniqueIdentifier>{2d4ce555-d6fc-4e9c-a30e-eadb4c1336d0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files\audio">
|
||||
<UniqueIdentifier>{5f42ff18-01e2-4266-a4e0-6dc77ba54e75}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\main.cpp">
|
||||
@@ -45,4 +51,24 @@
|
||||
<Filter>Header Files</Filter>
|
||||
</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>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+30
-8
@@ -13,16 +13,27 @@ void Paddle::Update() {
|
||||
}
|
||||
|
||||
// Limit movement
|
||||
if (position.y <= 0) {
|
||||
position.y = 0;
|
||||
if (position.y <= 20.0f) {
|
||||
position.y = 20.0f;
|
||||
}
|
||||
if (position.y + height >= GetScreenHeight()) {
|
||||
position.y = GetScreenHeight() - height;
|
||||
if (position.y + height >= GetScreenHeight() - 20.0f) {
|
||||
position.y = GetScreenHeight() - 20.0f - height;
|
||||
}
|
||||
}
|
||||
|
||||
void Paddle::Draw() {
|
||||
DrawRectangleRounded(Rectangle{ position.x, position.y, width, height }, 0.8f, 0, color);
|
||||
if (texture.id > 0) {
|
||||
DrawTexturePro(
|
||||
texture,
|
||||
Rectangle{ 0.0f, 0.0f, (float)texture.width, (float)texture.height },
|
||||
Rectangle{ position.x, position.y, width, height },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
} else {
|
||||
DrawRectangleRounded(Rectangle{ position.x, position.y, width, height }, 0.8f, 0, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +43,23 @@ void Ball::Update() {
|
||||
position.x += velocity.x;
|
||||
position.y += velocity.y;
|
||||
|
||||
if (position.y + radius >= GetScreenHeight() || position.y - radius <= 0) {
|
||||
if (position.y + radius >= GetScreenHeight() - 20.0f || position.y - radius <= 20.0f) {
|
||||
velocity.y *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Ball::Draw() {
|
||||
// Cast to int as DrawCircle expects integers for coordinates
|
||||
DrawCircle((int)position.x, (int)position.y, radius, color);
|
||||
if (texture.id > 0) {
|
||||
DrawTexturePro(
|
||||
texture,
|
||||
Rectangle{ 0.0f, 0.0f, (float)texture.width, (float)texture.height },
|
||||
Rectangle{ position.x - radius, position.y - radius, radius * 2.0f, radius * 2.0f },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
} else {
|
||||
// Cast to int as DrawCircle expects integers for coordinates
|
||||
DrawCircle((int)position.x, (int)position.y, radius, color);
|
||||
}
|
||||
}
|
||||
+268
-32
@@ -8,8 +8,13 @@ Color Dark_Green = Color{ 20, 160, 133, 255 };
|
||||
Color Light_Green = Color{ 129, 204, 184, 255 };
|
||||
Color Yellow = Color{ 243, 213, 91, 255 };
|
||||
|
||||
int player_score = 0;
|
||||
int cpu_score = 0;
|
||||
struct ScoreBoard
|
||||
{
|
||||
int player_score = 0;
|
||||
int player2_score = 0;
|
||||
int cpu_score = 0;
|
||||
};
|
||||
|
||||
|
||||
// Helper function to reset the ball
|
||||
void ResetBall(Ball& ball, int screenWidth, int screenHeight) {
|
||||
@@ -25,78 +30,305 @@ int main() {
|
||||
std::cout << "Starting the game" << std::endl;
|
||||
const int screen_width = 1280;
|
||||
const int screen_height = 800;
|
||||
InitWindow(screen_width, screen_height, "My Pong Game!");
|
||||
InitWindow(screen_width, screen_height, "Pong Reloaded");
|
||||
SetTargetFPS(60);
|
||||
|
||||
// --- Instantiate Objects using the new Constructors ---
|
||||
|
||||
Ball ball(Vector2{ screen_width / 2.0f, screen_height / 2.0f }, Yellow, 20.0f);
|
||||
Ball ball(
|
||||
Vector2{ screen_width / 2.0f, screen_height / 2.0f },
|
||||
Yellow, 20.0f,
|
||||
"assets/textures/ball/basic_ball_5.png"
|
||||
);
|
||||
ball.velocity = Vector2{ 7.0f, 7.0f };
|
||||
|
||||
Paddle player(
|
||||
Vector2{ screen_width - 35.0f, screen_height / 2.0f - 60.0f },
|
||||
WHITE, 25.0f, 120.0f
|
||||
Vector2{ screen_width - 20.0f - 10.0f - 25.0f, screen_height / 2.0f - 60.0f },
|
||||
WHITE, 25.0f, 120.0f,
|
||||
"assets/textures/paddles/basic_paddle.png"
|
||||
);
|
||||
|
||||
CpuPaddle cpu(
|
||||
Vector2{ 10.0f, screen_height / 2.0f - 60.0f },
|
||||
Vector2{ 20.0f + 10.0f, screen_height / 2.0f - 60.0f },
|
||||
WHITE, 25.0f, 120.0f,
|
||||
Difficulty::Normal
|
||||
);
|
||||
Difficulty::Normal,
|
||||
"assets/textures/paddles/basic_paddle_2.png"
|
||||
);
|
||||
|
||||
// --- Setup Menu and Game State ---
|
||||
|
||||
GameState currentState = GameState::MainMenu;
|
||||
Menu mainMenu("PONG RELOADED", { "Start Game", "Quit" });
|
||||
float sessionPlayTime = 0.0f;
|
||||
bool isPaused = false;
|
||||
bool shouldQuit = false;
|
||||
Menu mainMenu("PONG RELOADED", { "Singleplayer", "Multiplayer", "Settings", "Quit" });
|
||||
Menu difficultyMenu("SELECT DIFFICULTY", { "Easy", "Normal", "Hard", "Back" });
|
||||
ScoreBoard score;
|
||||
|
||||
// --- Load Background and Wall Textures ---
|
||||
Texture2D courtBackground = LoadTexture("assets/textures/spaces/basic_space.png");
|
||||
Texture2D wallsTexture = LoadTexture("assets/textures/spaces/walls.png");
|
||||
Texture2D lineTexture = LoadTexture("assets/textures/hud/line.png");
|
||||
|
||||
// --- Main Game Loop ---
|
||||
|
||||
while (WindowShouldClose() == false && currentState != GameState::GameOver) {
|
||||
while (WindowShouldClose() == false && !shouldQuit) {
|
||||
BeginDrawing();
|
||||
ClearBackground(Dark_Green);
|
||||
|
||||
switch (currentState) {
|
||||
case GameState::MainMenu:
|
||||
{
|
||||
mainMenu.Update(currentState);
|
||||
int selected = mainMenu.Update();
|
||||
if (selected == 0) {
|
||||
currentState = GameState::DifficultySelect;
|
||||
}
|
||||
else if (selected == 1) {
|
||||
currentState = GameState::Multiplayer;
|
||||
}
|
||||
else if (selected == 2) {
|
||||
currentState = GameState::Settings;
|
||||
}
|
||||
else if (selected == 3) {
|
||||
shouldQuit = true;
|
||||
}
|
||||
mainMenu.Draw();
|
||||
break;
|
||||
}
|
||||
|
||||
case GameState::DifficultySelect:
|
||||
{
|
||||
int selected = difficultyMenu.Update();
|
||||
if (selected >= 0 && selected <= 2) {
|
||||
if (selected == 0) {
|
||||
cpu.SetDifficulty(Difficulty::Easy);
|
||||
}
|
||||
else if (selected == 1) {
|
||||
cpu.SetDifficulty(Difficulty::Normal);
|
||||
}
|
||||
else if (selected == 2) {
|
||||
cpu.SetDifficulty(Difficulty::Hard);
|
||||
}
|
||||
|
||||
// Reset game session stats
|
||||
score.player_score = 0;
|
||||
score.cpu_score = 0;
|
||||
sessionPlayTime = 0.0f;
|
||||
isPaused = false;
|
||||
ResetBall(ball, screen_width, screen_height);
|
||||
currentState = GameState::Playing;
|
||||
}
|
||||
else if (selected == 3) {
|
||||
currentState = GameState::MainMenu;
|
||||
}
|
||||
difficultyMenu.Draw();
|
||||
break;
|
||||
}
|
||||
|
||||
case GameState::Playing:
|
||||
{
|
||||
ball.Update();
|
||||
player.Update();
|
||||
cpu.Update(ball.position.y);
|
||||
|
||||
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ player.position.x, player.position.y, player.width, player.height })) {
|
||||
ball.velocity.x *= -1;
|
||||
// Toggle pause state with 'P' key
|
||||
if (IsKeyPressed(KEY_P)) {
|
||||
isPaused = !isPaused;
|
||||
}
|
||||
|
||||
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ cpu.position.x, cpu.position.y, cpu.width, cpu.height })) {
|
||||
ball.velocity.x *= -1;
|
||||
// Force GameOver with SPACEBAR key
|
||||
if (IsKeyPressed(KEY_SPACE)) {
|
||||
currentState = GameState::GameOver;
|
||||
}
|
||||
|
||||
if (ball.position.x + ball.radius >= screen_width) {
|
||||
cpu_score++;
|
||||
ResetBall(ball, screen_width, screen_height);
|
||||
}
|
||||
if (ball.position.x - ball.radius <= 0) {
|
||||
player_score++;
|
||||
ResetBall(ball, screen_width, screen_height);
|
||||
if (!isPaused) {
|
||||
sessionPlayTime += GetFrameTime();
|
||||
|
||||
ball.Update();
|
||||
player.Update();
|
||||
cpu.Update(ball.position.y);
|
||||
|
||||
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ player.position.x, player.position.y, player.width, player.height })) {
|
||||
ball.velocity.x *= -1;
|
||||
}
|
||||
|
||||
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ cpu.position.x, cpu.position.y, cpu.width, cpu.height })) {
|
||||
ball.velocity.x *= -1;
|
||||
}
|
||||
|
||||
if (ball.position.x + ball.radius >= screen_width - 20.0f) {
|
||||
score.cpu_score++;
|
||||
if (score.cpu_score >= 5) {
|
||||
currentState = GameState::GameOver;
|
||||
}
|
||||
else {
|
||||
ResetBall(ball, screen_width, screen_height);
|
||||
}
|
||||
}
|
||||
if (ball.position.x - ball.radius <= 20.0f) {
|
||||
score.player_score++;
|
||||
if (score.player_score >= 5) {
|
||||
currentState = GameState::GameOver;
|
||||
}
|
||||
else {
|
||||
ResetBall(ball, screen_width, screen_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawRectangle(screen_width / 2, 0, screen_width / 2, screen_height, Green);
|
||||
DrawCircle(screen_width / 2, screen_height / 2, 150, Light_Green);
|
||||
DrawLine(screen_width / 2, 0, screen_width / 2, screen_height, WHITE);
|
||||
// --- Draw Textured Court Background ---
|
||||
// Left Court (basic_space.png)
|
||||
DrawTexturePro(
|
||||
courtBackground,
|
||||
Rectangle{ 0.0f, 0.0f, (float)courtBackground.width, (float)courtBackground.height },
|
||||
Rectangle{ 20.0f, 20.0f, 570.0f, 760.0f },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
|
||||
DrawText(TextFormat("%i", cpu_score), screen_width / 4 - 20, 20, 80, WHITE);
|
||||
DrawText(TextFormat("%i", player_score), 3 * screen_width / 4 - 20, 20, 80, WHITE);
|
||||
// Right Court (basic_space.png)
|
||||
DrawTexturePro(
|
||||
courtBackground,
|
||||
Rectangle{ 0.0f, 0.0f, (float)courtBackground.width, (float)courtBackground.height },
|
||||
Rectangle{ 690.0f, 20.0f, 570.0f, 760.0f },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
|
||||
// Center strip (Dark Green background area) - already cleared by ClearBackground
|
||||
|
||||
// Center Circle
|
||||
DrawCircle(screen_width / 2, screen_height / 2, 50.0f, Color{ 102, 51, 153, 100 });
|
||||
DrawCircleLines(screen_width / 2, screen_height / 2, 50.0f, Color{ 50, 25, 75, 250 });
|
||||
|
||||
// Tiled Center Dashed Line (line.png)
|
||||
int lineY = 20;
|
||||
while (lineY < 780) {
|
||||
DrawTexture(lineTexture, screen_width / 2 - lineTexture.width / 2, lineY, WHITE);
|
||||
lineY += lineTexture.height;
|
||||
}
|
||||
|
||||
// Top Wall (walls.png)
|
||||
DrawTexturePro(
|
||||
wallsTexture,
|
||||
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
|
||||
Rectangle{ 0.0f, 0.0f, (float)screen_width, 20.0f },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
|
||||
// Bottom Wall (walls.png)
|
||||
DrawTexturePro(
|
||||
wallsTexture,
|
||||
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
|
||||
Rectangle{ 0.0f, (float)screen_height - 20.0f, (float)screen_width, 20.0f },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
|
||||
// Left Wall (walls.png)
|
||||
DrawTexturePro(
|
||||
wallsTexture,
|
||||
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
|
||||
Rectangle{ 0.0f, 0.0f, 20.0f, (float)screen_height },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
|
||||
// Right Wall (walls.png)
|
||||
DrawTexturePro(
|
||||
wallsTexture,
|
||||
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
|
||||
Rectangle{ (float)screen_width - 20.0f, 0.0f, 20.0f, (float)screen_height },
|
||||
Vector2{ 0.0f, 0.0f },
|
||||
0.0f,
|
||||
WHITE
|
||||
);
|
||||
|
||||
DrawText(TextFormat("%i", score.cpu_score), screen_width / 4 - 20, 20, 80, WHITE);
|
||||
DrawText(TextFormat("%i", score.player_score), 3 * screen_width / 4 - 20, 20, 80, WHITE);
|
||||
|
||||
// Draw Playtime Counter
|
||||
int minutes = (int)sessionPlayTime / 60;
|
||||
int seconds = (int)sessionPlayTime % 60;
|
||||
int timeTextWidth = MeasureText(TextFormat("%02i:%02i", minutes, seconds), 32);
|
||||
|
||||
// Draw background box to block out the center line and increase contrast
|
||||
DrawRectangle(screen_width / 2 - timeTextWidth / 2 - 15, 715, timeTextWidth + 30, 44, Color{ 15, 15, 15, 220 });
|
||||
DrawRectangleLines(screen_width / 2 - timeTextWidth / 2 - 15, 715, timeTextWidth + 30, 44, Color{ 100, 100, 100, 255 });
|
||||
DrawText(TextFormat("%02i:%02i", minutes, seconds), screen_width / 2 - timeTextWidth / 2, 721, 32, YELLOW);
|
||||
|
||||
ball.Draw();
|
||||
cpu.Draw();
|
||||
player.Draw();
|
||||
|
||||
// Draw Pause Overlay and Text
|
||||
if (isPaused) {
|
||||
// Semi-transparent overlay inside the borders
|
||||
DrawRectangle(20, 20, screen_width - 40, screen_height - 40, Color{ 0, 0, 0, 150 });
|
||||
|
||||
int pausedTextWidth = MeasureText("PAUSED", 60);
|
||||
DrawText("PAUSED", screen_width / 2 - pausedTextWidth / 2, screen_height / 2 - 30, 60, YELLOW);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GameState::GameOver:
|
||||
{
|
||||
if (IsKeyPressed(KEY_SPACE)) {
|
||||
currentState = GameState::MainMenu;
|
||||
}
|
||||
|
||||
// Draw a nice centered dark panel for game over info
|
||||
int panelWidth = 600;
|
||||
int panelHeight = 400;
|
||||
int panelX = screen_width / 2 - panelWidth / 2;
|
||||
int panelY = screen_height / 2 - panelHeight / 2;
|
||||
|
||||
// Semi-transparent background panel
|
||||
DrawRectangle(panelX, panelY, panelWidth, panelHeight, Color{ 15, 15, 15, 230 });
|
||||
DrawRectangleLines(panelX, panelY, panelWidth, panelHeight, Color{ 120, 120, 120, 255 });
|
||||
|
||||
// Draw "GAME OVER" header
|
||||
int gameOverWidth = MeasureText("GAME OVER", 60);
|
||||
DrawText("GAME OVER", screen_width / 2 - gameOverWidth / 2, panelY + 40, 60, RED);
|
||||
|
||||
// Determine winner text and color
|
||||
std::string winnerText = "GAME ENDED";
|
||||
Color winnerColor = YELLOW;
|
||||
if (score.player_score > score.cpu_score) {
|
||||
winnerText = "YOU WIN!";
|
||||
winnerColor = Green;
|
||||
}
|
||||
else if (score.cpu_score > score.player_score) {
|
||||
winnerText = "CPU WINS!";
|
||||
winnerColor = RED;
|
||||
}
|
||||
int winnerWidth = MeasureText(winnerText.c_str(), 40);
|
||||
DrawText(winnerText.c_str(), screen_width / 2 - winnerWidth / 2, panelY + 120, 40, winnerColor);
|
||||
|
||||
// Draw final scores
|
||||
std::string playerScoreStr = "Player Score: " + std::to_string(score.player_score);
|
||||
std::string cpuScoreStr = "CPU Score: " + std::to_string(score.cpu_score);
|
||||
|
||||
int playerTextWidth = MeasureText(playerScoreStr.c_str(), 30);
|
||||
int cpuTextWidth = MeasureText(cpuScoreStr.c_str(), 30);
|
||||
|
||||
DrawText(playerScoreStr.c_str(), screen_width / 2 - playerTextWidth / 2, panelY + 200, 30, WHITE);
|
||||
DrawText(cpuScoreStr.c_str(), screen_width / 2 - cpuTextWidth / 2, panelY + 250, 30, WHITE);
|
||||
|
||||
// Draw playtime
|
||||
int minutes = (int)sessionPlayTime / 60;
|
||||
int seconds = (int)sessionPlayTime % 60;
|
||||
std::string timeStr = "Playtime: " + std::to_string(minutes) + "m " + std::to_string(seconds) + "s";
|
||||
int timeTextWidth = MeasureText(timeStr.c_str(), 20);
|
||||
DrawText(timeStr.c_str(), screen_width / 2 - timeTextWidth / 2, panelY + 300, 20, LIGHTGRAY);
|
||||
|
||||
// Instruction to return
|
||||
int instWidth = MeasureText("Press SPACEBAR to return to Main Menu", 20);
|
||||
DrawText("Press SPACEBAR to return to Main Menu", screen_width / 2 - instWidth / 2, panelY + 340, 20, YELLOW);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -106,6 +338,10 @@ int main() {
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
UnloadTexture(courtBackground);
|
||||
UnloadTexture(wallsTexture);
|
||||
UnloadTexture(lineTexture);
|
||||
|
||||
CloseWindow();
|
||||
return 0;
|
||||
}
|
||||
+8
-10
@@ -1,25 +1,21 @@
|
||||
#include "menu.h"
|
||||
|
||||
void Menu::Update(GameState& currentState) {
|
||||
int Menu::Update() {
|
||||
// Handle Navigation
|
||||
if (IsKeyPressed(KEY_DOWN)) {
|
||||
selectedIndex++;
|
||||
if (selectedIndex >= options.size()) selectedIndex = 0;
|
||||
if (selectedIndex >= static_cast<int>(options.size())) selectedIndex = 0;
|
||||
}
|
||||
if (IsKeyPressed(KEY_UP)) {
|
||||
selectedIndex--;
|
||||
if (selectedIndex < 0) selectedIndex = options.size() - 1;
|
||||
if (selectedIndex < 0) selectedIndex = static_cast<int>(options.size()) - 1;
|
||||
}
|
||||
|
||||
// Handle Selection
|
||||
if (IsKeyPressed(KEY_ENTER)) {
|
||||
if (options[selectedIndex] == "Start Game") {
|
||||
currentState = GameState::Playing;
|
||||
}
|
||||
else if (options[selectedIndex] == "Quit") {
|
||||
currentState = GameState::GameOver;
|
||||
}
|
||||
return selectedIndex;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Menu::Draw() {
|
||||
@@ -30,8 +26,10 @@ void Menu::Draw() {
|
||||
DrawText(title.c_str(), screenWidth / 2 - MeasureText(title.c_str(), 60) / 2, screenHeight / 4, 60, WHITE);
|
||||
|
||||
// Draw Options
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
for (int i = 0; i < static_cast<int>(options.size()); i++) {
|
||||
Color textColor = (i == selectedIndex) ? YELLOW : WHITE;
|
||||
DrawText(options[i].c_str(), screenWidth / 2 - MeasureText(options[i].c_str(), 40) / 2, screenHeight / 2 + (i * 60), 40, textColor);
|
||||
}
|
||||
|
||||
// Draw Control keys
|
||||
}
|
||||
Reference in New Issue
Block a user