From 770d3e3c711f26639c028c4f0c97eaa936f9008b Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Wed, 11 Dec 2024 23:20:10 +0100 Subject: [PATCH 01/12] [Added] Pause and Game Over screens --- include/entities/Game.h | 192 +++++++++++++++++++++++++++++-- include/entities/Player.h | 27 +++-- include/entities/Text.h | 1 + include/screens/GameOverScreen.h | 33 ++++++ include/services/GameService.h | 30 +++++ include/services/StateService.h | 4 - src/screens/MainScreen.cpp | 6 +- src/services/AssetService.cpp | 2 +- src/services/AudioService.cpp | 1 + src/services/StateService.cpp | 4 + 10 files changed, 275 insertions(+), 25 deletions(-) create mode 100644 include/screens/GameOverScreen.h create mode 100644 include/services/GameService.h diff --git a/include/entities/Game.h b/include/entities/Game.h index 71491ce..326969d 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -7,19 +7,24 @@ #include "services/AudioService.h" #include "services/MapService.h" #include "services/InputService.h" +#include "services/GameService.h" #include "entities/Player.h" +#include "entities/Text.h" class Game { public: Game() { - TextureWrapper wrapper1 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME1), {GetScreenWidth()*2.0f,(float)GetScreenHeight()}, {0,0}); - wrapper1.setSourceRect({0,0,wrapper1.getSize().x, wrapper1.getSize().y}); - TextureWrapper wrapper2 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME2), {864.0f,(float)GetScreenHeight()}, {wrapper1.getSize().x,0.0}); - wrapper2.setSourceRect({0,200,wrapper2.getSize().x, 200+wrapper2.getSize().y}); - TextureWrapper wrapper3 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME3), {1792.0f,(float)GetScreenHeight()}, {wrapper1.getSize().x+wrapper2.getSize().x,0.0}); - wrapper3.setSourceRect({64,200,64+wrapper3.getSize().x, 200+wrapper3.getSize().y}); + GameService::getInstance().setGameOver(false); + GameService::getInstance().setPaused(false); + + TextureWrapper wrapper1 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME1), {GetScreenWidth() * 2.0f, (float)GetScreenHeight()}, {0, 0}); + wrapper1.setSourceRect({0, 0, wrapper1.getSize().x, wrapper1.getSize().y}); + TextureWrapper wrapper2 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME2), {864.0f, (float)GetScreenHeight()}, {wrapper1.getSize().x, 0.0}); + wrapper2.setSourceRect({0, 200, wrapper2.getSize().x, 200 + wrapper2.getSize().y}); + TextureWrapper wrapper3 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME3), {1792.0f, (float)GetScreenHeight()}, {wrapper1.getSize().x + wrapper2.getSize().x, 0.0}); + wrapper3.setSourceRect({64, 200, 64 + wrapper3.getSize().x, 200 + wrapper3.getSize().y}); std::vector wrappers; wrappers.push_back(wrapper1); @@ -38,10 +43,12 @@ public: this->camera = camera; this->player = Player(); - this->player.getTexture().setPosition({200,350}); + this->player.getTexture().setPosition({200, 350}); - InputService::getInstance().setKeysToWatch({KEY_SPACE, KEY_A, KEY_D}, {MOUSE_BUTTON_LEFT}); + initPauseScreen(); + initGameOverScreen(); + InputService::getInstance().setKeysToWatch({KEY_A, KEY_W, KEY_S, KEY_D, KEY_ENTER}, {MOUSE_BUTTON_LEFT}); AudioService::getInstance().setMusic(EMusic::GAME); AudioService::getInstance().playMusic(); } @@ -49,13 +56,133 @@ public: { } + void initPauseScreen() + { + const int TOP_GAP = 100; + const int BOTTOM_GAP = 50; + const int AMNT_OF_BUTTONS = 4; + const int TOTAL_SPACE = GetScreenHeight() - (TOP_GAP + BOTTOM_GAP); + const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; + + for (int i = 0; i < AMNT_OF_BUTTONS; i++) + { + Text txt = Text("Continue", EFont::SANSATION, 30, 0, WHITE); + pauseScreenButtons.push_back(txt); + } + pauseScreenButtons[0].setColor(RED); + pauseScreenButtons[1].setText("Save Game"); + pauseScreenButtons[2].setText("Quit to Main Menu"); + pauseScreenButtons[3].setText("Quit to Desktop"); + + for (int i = 0; i < AMNT_OF_BUTTONS; i++) + { + pauseScreenButtons[i].setPosition({(float)(GetScreenWidth() - pauseScreenButtons[i].getDimensions().x) / 2.0f, (float)TOP_GAP + SPACE_PER_ITEM * i}); + } + } + + void initGameOverScreen() + { + this->gameOverText = Text("game over", EFont::OXIN, 60, 0, WHITE); + this->gameOverText.setPosition({(float)(GetScreenWidth() - gameOverText.getDimensions().x) / 2.0f, (float)(GetScreenHeight() - gameOverText.getDimensions().y) / 3.0f}); + + const int TOP_POS = GetScreenHeight() / 1.8; + const int X_GAP = 90; + const int AMNT_OF_BUTTONS = 2; + const int TOTAL_SPACE = GetScreenWidth() - (X_GAP + X_GAP); + const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; + for (int i = 0; i < AMNT_OF_BUTTONS; i++) + { + Text txt = Text("play again", EFont::OXIN, 30, 0, WHITE); + gameOverScreenButtons.push_back(txt); + } + gameOverScreenButtons[0].setColor(BLACK); + gameOverScreenButtons[1].setText("quit to main menu"); + for (int i = 0; i < AMNT_OF_BUTTONS; i++) + { + gameOverScreenButtons[i].setPosition({(float)X_GAP + SPACE_PER_ITEM * i, (float)TOP_POS}); + } + } + void logic() { + if (GameService::getInstance().isPaused()) + { + pausedLogic(); + return; + } + else if (GameService::getInstance().isGameOver()) + { + gameOverLogic(); + return; + } + + if (InputService::getInstance().isKeyPressed(KEY_ENTER)) + { + GameService::getInstance().setPaused(true); + AudioService::getInstance().pauseMusic(); + return; + } // this->camera.offset.x -= 1.5; this->player.logic(); - if(this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) { - this->camera.offset = {(StateService::getInstance().getScreenSize().x / 2.0f) +((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; + if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) + { + this->camera.offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; + } + } + void pausedLogic() + { + if (InputService::getInstance().isKeyPressed(KEY_S) || InputService::getInstance().isKeyPressed(KEY_W)) + { + pauseScreenButtons[hoveredPauseScreenButton].setColor(WHITE); + /** Move up or down depending on the pressed button. +4 is used so that -1 becomes 3 (the last button) */ + hoveredPauseScreenButton = ((hoveredPauseScreenButton - (InputService::getInstance().isKeyPressed(KEY_S) ? -1 : 1)) + pauseScreenButtons.size()) % pauseScreenButtons.size(); + pauseScreenButtons[hoveredPauseScreenButton].setColor(RED); + } + + if (InputService::getInstance().isKeyPressed(KEY_ENTER)) + { + + std::string selectedButton = pauseScreenButtons[hoveredPauseScreenButton].getText(); + + if (selectedButton == "Continue") + { + GameService::getInstance().setPaused(false); + AudioService::getInstance().playMusic(); + } + else if (selectedButton == "Save Game") + { + } + else if (selectedButton == "Quit to Main Menu") + StateService::getInstance().setScreen(EScreen::MAIN); + else if (selectedButton == "Quit to Desktop") + StateService::getInstance().setScreen(EScreen::CLOSE); + } + } + + void gameOverLogic() + { + if (InputService::getInstance().isKeyPressed(KEY_D) || InputService::getInstance().isKeyPressed(KEY_A)) + { + gameOverScreenButtons[hoveredGameOverScreenButton].setColor(WHITE); + /** Move up or down depending on the pressed button. +4 is used so that -1 becomes 3 (the last button) */ + hoveredGameOverScreenButton = ((hoveredGameOverScreenButton - (InputService::getInstance().isKeyPressed(KEY_D) ? -1 : 1)) + gameOverScreenButtons.size()) % gameOverScreenButtons.size(); + gameOverScreenButtons[hoveredGameOverScreenButton].setColor(BLACK); + } + + if (InputService::getInstance().isKeyPressed(KEY_ENTER)) + { + GameService::getInstance().setGameOver(false); + std::string selectedButton = gameOverScreenButtons[hoveredGameOverScreenButton].getText(); + + if (selectedButton == "play again") + { + StateService::getInstance().setScreen(EScreen::GAME); + } + else if (selectedButton == "quit to main menu") + { + StateService::getInstance().setScreen(EScreen::MAIN); + } } } @@ -69,14 +196,59 @@ public: this->player.draw(); EndMode2D(); + + if (GameService::getInstance().isPaused()) + { + drawPaused(); + } + else if (GameService::getInstance().isGameOver()) + { + drawGameOver(); + } EndDrawing(); } + void drawPaused() + { + // 100, 100, 255, 60 + DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {0, 0, 255, 100}); + if (pauseScreenButtons.size() > 0) + { + for (int i = 0; i < pauseScreenButtons.size(); i++) + { + pauseScreenButtons[i].draw(); + } + } + } + + void drawGameOver() + { + DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {255, 0, 0, 100}); + + gameOverText.draw(); + + for (int i = 0; i < gameOverScreenButtons.size(); i++) + { + gameOverScreenButtons[i].draw(); + } + } + private: Camera2D camera; Background background; Map map; Player player; + + std::vector pauseScreenButtons; + int hoveredPauseScreenButton = 0; + + Text gameOverText; + std::vector gameOverScreenButtons; + int hoveredGameOverScreenButton = 0; + + // bool isPaused = false; + // bool isGameOver = false; + // std::vector enemies // std::vector crates // std::vector playerBullets diff --git a/include/entities/Player.h b/include/entities/Player.h index eea1a0b..bafc08c 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -6,6 +6,7 @@ #include "services/AssetService.h" #include "services/MapService.h" #include "services/InputService.h" +#include "services/GameService.h" #include "entities/TextureWrapper.h" #include "enums/ECorner.h" @@ -24,9 +25,10 @@ public: void logic() { - gravity(); + applyGravity(); movement(); handleCollision(); + outofboundsCheck(); } void draw() @@ -43,6 +45,9 @@ protected: private: TextureWrapper texture; Vector2 velocity = {0, 0}; + float jumpPower = 7.2f; + float moveSpeed = 5.0f; + float gravity = 0.6f; std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground @@ -52,11 +57,11 @@ private: * Is used for precise collision detection */ - void gravity() + void applyGravity() { if (!groundCollision) { - velocity.y += 0.6f; + velocity.y += gravity; } else { @@ -69,16 +74,16 @@ private: velocity.x = 0.0f; if (InputService::getInstance().isKeyDown(KEY_A)) { - velocity.x -= 5.0f; + velocity.x -= moveSpeed; } else if (InputService::getInstance().isKeyDown(KEY_D)) { - velocity.x += 5.0f; + velocity.x += moveSpeed; } - if (InputService::getInstance().isKeyPressed(KEY_SPACE) && groundCollision) + if (InputService::getInstance().isKeyPressed(KEY_W) && groundCollision) { - velocity.y -= 7.2f; + velocity.y -= jumpPower; } } @@ -118,6 +123,14 @@ private: this->texture.setPosition({floorf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); } } + + void outofboundsCheck() { + if(this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) { + GameService::getInstance().setGameOver(true); + AudioService::getInstance().setMusic(EMusic::GAME_OVER); + AudioService::getInstance().playMusic(); + } + } }; #endif diff --git a/include/entities/Text.h b/include/entities/Text.h index 21dd3b0..680a206 100644 --- a/include/entities/Text.h +++ b/include/entities/Text.h @@ -9,6 +9,7 @@ class Text { public: + Text(){} Text(std::string text, EFont font, int fontSize = 30, int spacing = 0, Color color = WHITE, Vector2 position = {100,100}); ~Text(){} diff --git a/include/screens/GameOverScreen.h b/include/screens/GameOverScreen.h new file mode 100644 index 0000000..4e2a222 --- /dev/null +++ b/include/screens/GameOverScreen.h @@ -0,0 +1,33 @@ +#ifndef GAME_OVER_SCREEN_H +#define GAME_OVER_SCREEN_H + +#include "screens/Screen.h" +#include "raylib.h" +#include "services/AudioService.h" + +class GameOverScreen : public Screen +{ + +public: + GameOverScreen() + { + AudioService::getInstance().setMusic(EMusic::GAME_OVER); + AudioService::getInstance().playMusic(); + } + + void logic() override + { + } + + void draw() override + { + BeginDrawing(); + // ClearBackground((Color){255,0,0,100}); + DrawRectangle(0, 0, 640, 544, (Color){255,0,0,65}); + EndDrawing(); + } + +private: +}; + +#endif \ No newline at end of file diff --git a/include/services/GameService.h b/include/services/GameService.h new file mode 100644 index 0000000..5e8e364 --- /dev/null +++ b/include/services/GameService.h @@ -0,0 +1,30 @@ +#ifndef GAME_SERVICE_H +#define GAME_SERVICE_H + +#include "Service.h" + +class GameService : public Service { +public: + bool isPaused() { + return this->paused; + } + + bool isGameOver() { + return this->gameOver; + } + + void setPaused(bool paused) { + this->paused = paused; + } + + void setGameOver(bool gameOver) { + this->gameOver = gameOver; + } + +private: + bool paused = false; + bool gameOver = false; +}; + + +#endif \ No newline at end of file diff --git a/include/services/StateService.h b/include/services/StateService.h index 01960b6..4e1ce49 100644 --- a/include/services/StateService.h +++ b/include/services/StateService.h @@ -32,7 +32,6 @@ public: * Start the game loop */ void startGame(); - static void staticStep() { if (IsKeyPressed(KEY_ENTER) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) @@ -45,16 +44,13 @@ public: private: std::unique_ptr currentScreen; - bool gameRunning = true; - Vector2 screenSize; /** * One step of the gameloop */ void step(); - void handleMusic(); }; diff --git a/src/screens/MainScreen.cpp b/src/screens/MainScreen.cpp index 77f6293..40738e3 100644 --- a/src/screens/MainScreen.cpp +++ b/src/screens/MainScreen.cpp @@ -35,7 +35,7 @@ MainScreen::MainScreen() AudioService::getInstance().playMusic(); } - InputService::getInstance().setKeysToWatch({KEY_ENTER, KEY_UP, KEY_DOWN}, {MOUSE_BUTTON_LEFT}); + InputService::getInstance().setKeysToWatch({KEY_ENTER, KEY_W, KEY_S}, {MOUSE_BUTTON_LEFT}); } void MainScreen::logic() @@ -81,11 +81,11 @@ void MainScreen::mouseLogic() void MainScreen::keyboardLogic() { - if (InputService::getInstance().isKeyPressed(KEY_DOWN) || InputService::getInstance().isKeyPressed(KEY_UP)) + if (InputService::getInstance().isKeyPressed(KEY_S) || InputService::getInstance().isKeyPressed(KEY_W)) { buttons[hoveredButton].setColor(WHITE); /** Move up or down depending on the pressed button. +4 is used so that -1 becomes 3 (the last button) */ - hoveredButton = ((hoveredButton - (InputService::getInstance().isKeyPressed(KEY_DOWN) ? -1 : 1)) + 4) % 4; + hoveredButton = ((hoveredButton - (InputService::getInstance().isKeyPressed(KEY_S) ? -1 : 1)) + 4) % 4; buttons[hoveredButton].setColor(RED); } } diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index 06039a9..b99907b 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -17,7 +17,7 @@ void AssetService::setAssets() music[EMusic::MAIN] = loadMusic("Audio/Music/Crash_WarpRoomTheme.ogg", true); music[EMusic::GAME] = loadMusic("Audio/Music/GameTheme_2.ogg", true); - music[EMusic::GAME_OVER] = loadMusic("Audio/Music/gameOverTheme.ogg", false); + music[EMusic::GAME_OVER] = loadMusic("Audio/Music/gameOverTheme_2.ogg", false); /** Todo: set volume */ backgrounds[EBackground::TUTORIAL] = loadTexture("Textures/TutorialBackground2.png"); diff --git a/src/services/AudioService.cpp b/src/services/AudioService.cpp index 64737c8..c93e800 100644 --- a/src/services/AudioService.cpp +++ b/src/services/AudioService.cpp @@ -13,6 +13,7 @@ void AudioService::setMusic(EMusic music) if (currentTrack == music) return; + stopMusic(); // Makes sure it doesn't continue where it left off this->currentTrack = music; this->music = AssetService::getInstance().getMusic(music); } diff --git a/src/services/StateService.cpp b/src/services/StateService.cpp index 9c56ebf..ce1a975 100644 --- a/src/services/StateService.cpp +++ b/src/services/StateService.cpp @@ -4,6 +4,7 @@ #include "screens/MainScreen.h" #include "screens/TutorialScreen.h" #include "screens/GameScreen.h" +#include "screens/GameOverScreen.h" #include #include "services/AudioService.h" #include "services/InputService.h" @@ -24,6 +25,9 @@ void StateService::setScreen(EScreen state) case EScreen::GAME: currentScreen = std::make_unique(); break; + // case EScreen::GAMEOVER: + // currentScreen = std::make_unique(); + // break; case EScreen::CLOSE: gameRunning = false; break; From dbeb15ee60a31b221d63c59d1bf9eb26c073e97b Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Thu, 12 Dec 2024 20:31:56 +0100 Subject: [PATCH 02/12] [Added] Sprite animation for player, crosshair mouse, [Updated] Collision --- .devcontainer/devcontainer.json | 3 +- include/entities/Game.h | 35 ++++-- include/entities/Map.h | 4 + include/entities/Player.h | 190 +++++++++++++++++++++++++++----- include/entities/Vector2i.h | 15 +++ include/enums/ESprite.h | 3 +- src/screens/MainScreen.cpp | 6 +- src/screens/TitleScreen.cpp | 4 +- src/screens/TutorialScreen.cpp | 4 +- src/services/AssetService.cpp | 1 + src/services/StateService.cpp | 7 +- 11 files changed, 220 insertions(+), 52 deletions(-) create mode 100644 include/entities/Vector2i.h diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a4fc976..0e4d1b2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -28,8 +28,7 @@ "settings": {}, "extensions": [ "esbenp.prettier-vscode", - "ms-vscode.cpptools-extension-pack", - "jeff-hykin.better-cpp-syntax" + "ms-vscode.cpptools-extension-pack" ] } }, diff --git a/include/entities/Game.h b/include/entities/Game.h index 326969d..d75d022 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -45,6 +45,9 @@ public: this->player = Player(); this->player.getTexture().setPosition({200, 350}); + + this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15,15}, {0,0}, {0,0,15,15}); + initPauseScreen(); initGameOverScreen(); @@ -82,7 +85,7 @@ public: void initGameOverScreen() { - this->gameOverText = Text("game over", EFont::OXIN, 60, 0, WHITE); + this->gameOverText = Text("Game Over", EFont::SANSATION, 60, 0, WHITE); this->gameOverText.setPosition({(float)(GetScreenWidth() - gameOverText.getDimensions().x) / 2.0f, (float)(GetScreenHeight() - gameOverText.getDimensions().y) / 3.0f}); const int TOP_POS = GetScreenHeight() / 1.8; @@ -92,11 +95,11 @@ public: const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; for (int i = 0; i < AMNT_OF_BUTTONS; i++) { - Text txt = Text("play again", EFont::OXIN, 30, 0, WHITE); + Text txt = Text("Play Again", EFont::SANSATION, 30, 0, GRAY); gameOverScreenButtons.push_back(txt); } - gameOverScreenButtons[0].setColor(BLACK); - gameOverScreenButtons[1].setText("quit to main menu"); + gameOverScreenButtons[0].setColor(WHITE); + gameOverScreenButtons[1].setText("Quit to Main Menu"); for (int i = 0; i < AMNT_OF_BUTTONS; i++) { gameOverScreenButtons[i].setPosition({(float)X_GAP + SPACE_PER_ITEM * i, (float)TOP_POS}); @@ -105,6 +108,8 @@ public: void logic() { + this->crosshair.setPosition(GetMousePosition()); + if (GameService::getInstance().isPaused()) { pausedLogic(); @@ -164,25 +169,25 @@ public: { if (InputService::getInstance().isKeyPressed(KEY_D) || InputService::getInstance().isKeyPressed(KEY_A)) { - gameOverScreenButtons[hoveredGameOverScreenButton].setColor(WHITE); + gameOverScreenButtons[hoveredGameOverScreenButton].setColor(GRAY); /** Move up or down depending on the pressed button. +4 is used so that -1 becomes 3 (the last button) */ hoveredGameOverScreenButton = ((hoveredGameOverScreenButton - (InputService::getInstance().isKeyPressed(KEY_D) ? -1 : 1)) + gameOverScreenButtons.size()) % gameOverScreenButtons.size(); - gameOverScreenButtons[hoveredGameOverScreenButton].setColor(BLACK); + gameOverScreenButtons[hoveredGameOverScreenButton].setColor(WHITE); } if (InputService::getInstance().isKeyPressed(KEY_ENTER)) { - GameService::getInstance().setGameOver(false); std::string selectedButton = gameOverScreenButtons[hoveredGameOverScreenButton].getText(); - if (selectedButton == "play again") + if (selectedButton == "Play Again") { StateService::getInstance().setScreen(EScreen::GAME); } - else if (selectedButton == "quit to main menu") + else if (selectedButton == "Quit to Main Menu") { StateService::getInstance().setScreen(EScreen::MAIN); } + GameService::getInstance().setGameOver(false); } } @@ -205,6 +210,7 @@ public: { drawGameOver(); } + drawCrosshair(); EndDrawing(); } @@ -233,11 +239,22 @@ public: } } + void drawCrosshair() { + DrawTextureEx(this->crosshair.getTexture(),GetMousePosition(),0,1.2f,RED); + // DrawTexturePro( + // this->crosshair.getTexture(), + // this->crosshair.getSourceRect(), + // {GetMousePosition().x, GetMousePosition().y, GetMousePosition().x+this->crosshair.getSize().x, GetMousePosition().y+this->crosshair.getSize().y}, + // {this->crosshair.getSize().x/2.0f, this->crosshair.getSize().y/2.0f}, + // 0, + // WHITE); + } private: Camera2D camera; Background background; Map map; Player player; + TextureWrapper crosshair; std::vector pauseScreenButtons; int hoveredPauseScreenButton = 0; diff --git a/include/entities/Map.h b/include/entities/Map.h index 7aeece0..c16fcd3 100644 --- a/include/entities/Map.h +++ b/include/entities/Map.h @@ -24,6 +24,10 @@ public: return this->tileSize; } + std::vector> getColMap() { + return this->colMap; + } + private: /** Holds the number for every single tile, which refers to a certain sprite in the tileset */ std::vector> map; diff --git a/include/entities/Player.h b/include/entities/Player.h index bafc08c..2cb07e5 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -9,6 +9,7 @@ #include "services/GameService.h" #include "entities/TextureWrapper.h" #include "enums/ECorner.h" +#include "entities/Vector2i.h" class Player { @@ -18,6 +19,8 @@ public: TextureWrapper wrapper = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER), {32, 32}, {200, 200}); wrapper.setSourceRect({0, 0, 32, 32}); this->texture = wrapper; + prevFrame = GetTime(); + spriteAnimation = {0,0}; } ~Player() { @@ -25,9 +28,9 @@ public: void logic() { - applyGravity(); + // applyGravity(); movement(); - handleCollision(); + // handleCollision(); outofboundsCheck(); } @@ -48,6 +51,11 @@ private: float jumpPower = 7.2f; float moveSpeed = 5.0f; float gravity = 0.6f; + float animationDelay = 0.2f; // seconds + double prevFrame; + Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction + enum Direction { Down, Left, Right, Up }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction + std::vector tiles; // Holds eight points of the player for accurate collision std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground @@ -71,20 +79,86 @@ private: void movement() { - velocity.x = 0.0f; if (InputService::getInstance().isKeyDown(KEY_A)) { - velocity.x -= moveSpeed; + spriteAnimation.y = Left; + velocity.x = -moveSpeed; + animate(); } else if (InputService::getInstance().isKeyDown(KEY_D)) { - velocity.x += moveSpeed; + spriteAnimation.y = Right; + velocity.x = moveSpeed; + animate(); + } else { + velocity.x = 0.0f; + } + positionUpdates(); + handleCollision(); + + if (!groundCollision) { + velocity.y += gravity; + } else { + velocity.y = 0.0f; } if (InputService::getInstance().isKeyPressed(KEY_W) && groundCollision) { velocity.y -= jumpPower; } + + this->texture.move(velocity); + } + + void animate() + { + if (GetTime() - prevFrame > animationDelay) + { + spriteAnimation.x = std::fmod(spriteAnimation.x + 1,3); + + Rectangle sourceRect = this->texture.getSourceRect(); + sourceRect.x = spriteAnimation.x * this->texture.getSize().x; + sourceRect.y = spriteAnimation.y * this->texture.getSize().y; + // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); + this->texture.setSourceRect(sourceRect); + prevFrame = GetTime(); + } + } + + void positionUpdates() { + const Vector2 pos = this->texture.getPosition(); + const Vector2 size = this->texture.getSize(); + const int tileSize = MapService::getInstance().getMap().getTileSize(); + + const float bottom = pos.y + size.y + velocity.y; + const float left = pos.x + velocity.x; + const float right = pos.x + size.x + velocity.x; + const float top = pos.y + velocity.y; + + Vector2i topLeft(Vector2i((int)(left + 5) / tileSize, (int)top / tileSize)); // +5 to separate top from side + Vector2i topRight(Vector2i((int)(right - 5) / tileSize, (int)top / tileSize)); // -5 to separate top from side + + Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); + Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); + Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); + + Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side + Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + + + tiles.clear(); + + tiles.push_back(topLeft); + tiles.push_back(topRight); + + tiles.push_back(leftTop); + tiles.push_back(leftBottom); + tiles.push_back(rightTop); + tiles.push_back(rightBottom); + + tiles.push_back(bottomLeft); + tiles.push_back(bottomRight); } /** @@ -95,37 +169,93 @@ private: */ void handleCollision() { - Vector2 tempPos = this->texture.getPosition(); - this->texture.move(velocity); - groundCollision = false; - std::unordered_map collisions = MapService::getInstance().getMap().checkForCollision(this->texture); + timesCollision.clear(); - if (collisions[ECorner::BOTTOM_LEFT] || collisions[ECorner::BOTTOM_RIGHT]) + for (int i = 0; i < tiles.size(); i++) + { + if (MapService::getInstance().getMap().getColMap()[tiles[i].y][tiles[i].x] == 1) { - velocity.y = 0.0f; - groundCollision = true; - this->texture.setPosition({this->texture.getPosition().x, floorf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize()}); - } - if (collisions[ECorner::LEFT_TOP] && collisions[ECorner::LEFT_BOTTOM]) - { - velocity.x = 0.0f; - this->texture.setPosition({ceilf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); - } - if (collisions[ECorner::TOP_LEFT] || collisions[ECorner::TOP_RIGHT]) - { - velocity.y = 0.0f; - this->texture.setPosition({this->texture.getPosition().x, (ceilf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize())) * MapService::getInstance().getMap().getTileSize()}); - } - if (collisions[ECorner::RIGHT_TOP] && collisions[ECorner::RIGHT_BOTTOM]) - { - velocity.x = 0.0f; - this->texture.setPosition({floorf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); + timesCollision.push_back(i); } } - void outofboundsCheck() { - if(this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) { + groundCollision = false; + + if (timesCollision.size() > 0) + { + for (int i = 0; i < timesCollision.size(); i++) + { + if (timesCollision[i] == 0 || timesCollision[i] == 1) //Topleft and Topright + { + velocity.y = 0; + } + else if (timesCollision[i] == 2 || timesCollision[i] == 3 || //Lefttop and Leftbottom + timesCollision[i] == 4 || timesCollision[i] == 5) //Righttop and Lefttop + { + velocity.x = 0; + } + else if (timesCollision[i] == 6 || timesCollision[i] == 7) //Bottomleft and Bottomright + { + groundCollision = true; + } + } + } + + // Set velocity.x based on going right or left + + // Take current position of all 4 corners + // Apply velocity + // Calculate the 8 points + // Add them to a list + + // Empty vector of amount of collisions + // Loop through the points + // Check the collisionmap for intersection + // If so, add item to the vector of collisions + + // Set groundcollision to false + // Loop through collisions + // Based on their location (TopLeft, RightTop, etc) change the velocity + + // Apply gravity if not on the floor + // If jumping and on the floor, go up + + // Move using velocity + + // Vector2 tempPos = this->texture.getPosition(); + // this->texture.move(velocity); + + // groundCollision = false; + // std::unordered_map collisions = MapService::getInstance().getMap().checkForCollision(this->texture); + + // if (collisions[ECorner::BOTTOM_LEFT] || collisions[ECorner::BOTTOM_RIGHT]) + // { + // velocity.y = 0.0f; + // groundCollision = true; + // this->texture.setPosition({this->texture.getPosition().x, floorf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize()}); + // } + // if (collisions[ECorner::LEFT_TOP] && collisions[ECorner::LEFT_BOTTOM]) + // { + // velocity.x = 0.0f; + // this->texture.setPosition({ceilf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); + // } + // if (collisions[ECorner::TOP_LEFT] || collisions[ECorner::TOP_RIGHT]) + // { + // velocity.y = 0.0f; + // this->texture.setPosition({this->texture.getPosition().x, (ceilf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize())) * MapService::getInstance().getMap().getTileSize()}); + // } + // if (collisions[ECorner::RIGHT_TOP] && collisions[ECorner::RIGHT_BOTTOM]) + // { + // velocity.x = 0.0f; + // this->texture.setPosition({floorf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); + // } + } + + void outofboundsCheck() + { + if (this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) + { GameService::getInstance().setGameOver(true); AudioService::getInstance().setMusic(EMusic::GAME_OVER); AudioService::getInstance().playMusic(); diff --git a/include/entities/Vector2i.h b/include/entities/Vector2i.h new file mode 100644 index 0000000..950533a --- /dev/null +++ b/include/entities/Vector2i.h @@ -0,0 +1,15 @@ +#ifndef VECTOR2I_H +#define VECTOR2I_H + +class Vector2i { + public: + Vector2i(){} + Vector2i (int x, int y) { + this->x = x; + this->y = y; + } + int x; + int y; +}; + +#endif \ No newline at end of file diff --git a/include/enums/ESprite.h b/include/enums/ESprite.h index 5e70281..932595c 100644 --- a/include/enums/ESprite.h +++ b/include/enums/ESprite.h @@ -15,7 +15,8 @@ enum class ESprite { POWERUP, HUD_HEART, HUD_HEART_EMPTY, - HUD_AMMO + HUD_AMMO, + CROSSHAIR }; #endif \ No newline at end of file diff --git a/src/screens/MainScreen.cpp b/src/screens/MainScreen.cpp index 40738e3..54c9d18 100644 --- a/src/screens/MainScreen.cpp +++ b/src/screens/MainScreen.cpp @@ -35,7 +35,7 @@ MainScreen::MainScreen() AudioService::getInstance().playMusic(); } - InputService::getInstance().setKeysToWatch({KEY_ENTER, KEY_W, KEY_S}, {MOUSE_BUTTON_LEFT}); + InputService::getInstance().setKeysToWatch({KEY_ENTER, KEY_W, KEY_S}, {}); } void MainScreen::logic() @@ -44,7 +44,7 @@ void MainScreen::logic() // std::cout << mouseD.y <background = Background(EBackground::TUTORIAL); - InputService::getInstance().setKeysToWatch({KEY_ENTER}, {MOUSE_BUTTON_LEFT}); + InputService::getInstance().setKeysToWatch({KEY_ENTER},{}); } void TutorialScreen::logic() { - if ( InputService::getInstance().isKeyPressed(KEY_ENTER) || InputService::getInstance().isMouseButtonPressed(MOUSE_BUTTON_LEFT)) + if ( InputService::getInstance().isKeyPressed(KEY_ENTER)) { StateService::getInstance().setScreen(EScreen::MAIN); } diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index b99907b..7a75245 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -43,6 +43,7 @@ void AssetService::setAssets() sprites[ESprite::HUD_HEART] = loadTexture("Textures/heart.png"); sprites[ESprite::HUD_HEART_EMPTY] = loadTexture("Textures/heart_empty.png"); sprites[ESprite::HUD_AMMO] = loadTexture("Textures/bullet_hud.png"); + sprites[ESprite::CROSSHAIR] = loadTexture("Textures/crosshair_3.png"); } Font AssetService::getFont(EFont font, int fontSize) diff --git a/src/services/StateService.cpp b/src/services/StateService.cpp index ce1a975..8115e18 100644 --- a/src/services/StateService.cpp +++ b/src/services/StateService.cpp @@ -45,10 +45,11 @@ void StateService::startGame() { // if(!instance) // return; - InitAudioDevice(); + // InitAudioDevice(); // InitWindow(640,544, "Jelly Rescue"); InitWindow(screenSize.x, screenSize.y, "Jelly Rescue"); - SetTargetFPS(60); + SetTargetFPS(45); + HideCursor(); // #if defined(PLATFORM_WEB) // emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_CANVAS, nullptr, 1, staticStep); @@ -74,7 +75,7 @@ void StateService::startGame() #endif CloseWindow(); - CloseAudioDevice(); + // CloseAudioDevice(); } void StateService::handleMusic() From 88e0b7cb0572290271367770ebab0dd35b3b595f Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Fri, 13 Dec 2024 00:24:51 +0100 Subject: [PATCH 03/12] [Added] Gun angling --- include/entities/Game.h | 175 +++++++++++++------------ include/entities/Map.h | 4 +- include/entities/Player.h | 209 +++++++++++++++++++----------- include/entities/TextureWrapper.h | 44 ++++--- include/services/GameService.h | 45 ++++++- src/entities/Map.cpp | 78 +++++------ src/screens/GameScreen.cpp | 2 + src/screens/TitleScreen.cpp | 4 +- 8 files changed, 333 insertions(+), 228 deletions(-) diff --git a/include/entities/Game.h b/include/entities/Game.h index d75d022..7d61180 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -40,13 +40,13 @@ public: camera.target = (Vector2){screenSize.x / 2.0f, screenSize.y / 2.0f}; // Initially at the center of the world camera.rotation = 0.0f; camera.zoom = 1.0f; - this->camera = camera; + // this->camera = camera; + GameService::getInstance().setCamera(camera); this->player = Player(); - this->player.getTexture().setPosition({200, 350}); + this->player.setPosition({200, 350}); - - this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15,15}, {0,0}, {0,0,15,15}); + this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15, 15}, {0, 0}, {0, 0, 15, 15}); initPauseScreen(); initGameOverScreen(); @@ -55,10 +55,89 @@ public: AudioService::getInstance().setMusic(EMusic::GAME); AudioService::getInstance().playMusic(); } + ~Game() { } + void logic() + { + this->crosshair.setPosition(GetMousePosition()); + + if (GameService::getInstance().isPaused()) + { + pausedLogic(); + return; + } + else if (GameService::getInstance().isGameOver()) + { + gameOverLogic(); + return; + } + + if (InputService::getInstance().isKeyPressed(KEY_ENTER)) + { + GameService::getInstance().setPaused(true); + AudioService::getInstance().pauseMusic(); + return; + } + // this->camera.offset.x -= 1.5; + this->player.logic(); + if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) + { + GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; + } + } + + void draw() + { + BeginDrawing(); + BeginMode2D(GameService::getInstance().getCamera()); + + this->background.draw(); + this->map.draw(); + this->player.draw(); + + EndMode2D(); + + if (GameService::getInstance().isPaused()) + { + drawPaused(); + } + else if (GameService::getInstance().isGameOver()) + { + drawGameOver(); + } + drawCrosshair(); + EndDrawing(); + } + +private: + // Camera2D camera; + Background background; + Map map; + Player player; + TextureWrapper crosshair; + + std::vector pauseScreenButtons; + int hoveredPauseScreenButton = 0; + + Text gameOverText; + std::vector gameOverScreenButtons; + int hoveredGameOverScreenButton = 0; + + // bool isPaused = false; + // bool isGameOver = false; + + // std::vector enemies + // std::vector crates + // std::vector playerBullets + // std::vector enemyBullets + // std::vector jellies + // std::vector ammo + // std::vector healthpacks + // Powerup powerup + void initPauseScreen() { const int TOP_GAP = 100; @@ -106,35 +185,6 @@ public: } } - void logic() - { - this->crosshair.setPosition(GetMousePosition()); - - if (GameService::getInstance().isPaused()) - { - pausedLogic(); - return; - } - else if (GameService::getInstance().isGameOver()) - { - gameOverLogic(); - return; - } - - if (InputService::getInstance().isKeyPressed(KEY_ENTER)) - { - GameService::getInstance().setPaused(true); - AudioService::getInstance().pauseMusic(); - return; - } - // this->camera.offset.x -= 1.5; - this->player.logic(); - if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) - { - this->camera.offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; - } - } - void pausedLogic() { if (InputService::getInstance().isKeyPressed(KEY_S) || InputService::getInstance().isKeyPressed(KEY_W)) @@ -191,29 +241,6 @@ public: } } - void draw() - { - BeginDrawing(); - BeginMode2D(this->camera); - - this->background.draw(); - this->map.draw(); - this->player.draw(); - - EndMode2D(); - - if (GameService::getInstance().isPaused()) - { - drawPaused(); - } - else if (GameService::getInstance().isGameOver()) - { - drawGameOver(); - } - drawCrosshair(); - EndDrawing(); - } - void drawPaused() { // 100, 100, 255, 60 @@ -239,41 +266,17 @@ public: } } - void drawCrosshair() { - DrawTextureEx(this->crosshair.getTexture(),GetMousePosition(),0,1.2f,RED); + void drawCrosshair() + { + DrawTextureEx(this->crosshair.getTexture(), GetMousePosition(), 0, 1.2f, RED); // DrawTexturePro( // this->crosshair.getTexture(), - // this->crosshair.getSourceRect(), - // {GetMousePosition().x, GetMousePosition().y, GetMousePosition().x+this->crosshair.getSize().x, GetMousePosition().y+this->crosshair.getSize().y}, + // this->crosshair.getSourceRect(), + // {GetMousePosition().x, GetMousePosition().y, GetMousePosition().x+this->crosshair.getSize().x, GetMousePosition().y+this->crosshair.getSize().y}, // {this->crosshair.getSize().x/2.0f, this->crosshair.getSize().y/2.0f}, - // 0, + // 0, // WHITE); } -private: - Camera2D camera; - Background background; - Map map; - Player player; - TextureWrapper crosshair; - - std::vector pauseScreenButtons; - int hoveredPauseScreenButton = 0; - - Text gameOverText; - std::vector gameOverScreenButtons; - int hoveredGameOverScreenButton = 0; - - // bool isPaused = false; - // bool isGameOver = false; - - // std::vector enemies - // std::vector crates - // std::vector playerBullets - // std::vector enemyBullets - // std::vector jellies - // std::vector ammo - // std::vector healthpacks - // Powerup powerup }; #endif \ No newline at end of file diff --git a/include/entities/Map.h b/include/entities/Map.h index c16fcd3..f93bf97 100644 --- a/include/entities/Map.h +++ b/include/entities/Map.h @@ -18,7 +18,7 @@ public: void draw(); /** Checks and returns which corners of the object have been collided with */ - std::unordered_map checkForCollision(TextureWrapper texture); + // std::unordered_map checkForCollision(TextureWrapper texture); int getTileSize() { return this->tileSize; @@ -45,7 +45,7 @@ private: /** * Converts the corners of the object to the matching tiles, so that they can be checked for collision */ - std::unordered_map cornersToTilePos(std::unordered_map corners); + // std::unordered_map cornersToTilePos(std::unordered_map corners); }; #endif \ No newline at end of file diff --git a/include/entities/Player.h b/include/entities/Player.h index 2cb07e5..e530e19 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -16,11 +16,12 @@ class Player public: Player() { - TextureWrapper wrapper = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER), {32, 32}, {200, 200}); - wrapper.setSourceRect({0, 0, 32, 32}); - this->texture = wrapper; + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER), {32, 32}, {200, 200}); + prevFrame = GetTime(); - spriteAnimation = {0,0}; + spriteAnimation = {0, 0}; + this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_RIGHT), {58, 29}, {0, 0}); + this->gunLeft = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_LEFT), {58, 29}, {0, 0}); } ~Player() { @@ -28,14 +29,14 @@ public: void logic() { - // applyGravity(); + rotateGun(); movement(); - // handleCollision(); outofboundsCheck(); } void draw() { + drawGun(); DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), WHITE); } @@ -44,26 +45,35 @@ public: return this->texture; } + void setPosition(Vector2 pos) + { + this->texture.setPosition(pos); + } + protected: private: TextureWrapper texture; + TextureWrapper gunLeft; + TextureWrapper gunRight; Vector2 velocity = {0, 0}; float jumpPower = 7.2f; float moveSpeed = 5.0f; float gravity = 0.6f; - float animationDelay = 0.2f; // seconds + float animationDelay = 0.2f; // In seconds double prevFrame; - Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction - enum Direction { Down, Left, Right, Up }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction - std::vector tiles; // Holds eight points of the player for accurate collision + Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction + enum Direction + { + Down, + Left, + Right, + Up + }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction + std::vector tiles; // Holds eight points of the player for accurate collision std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground - bool crateTopCollision; - /** - * Holds the corners of the player - * Is used for precise collision detection - */ + bool crateTopCollision; // Whether the player is standing on top of a crate void applyGravity() { @@ -90,15 +100,20 @@ private: spriteAnimation.y = Right; velocity.x = moveSpeed; animate(); - } else { + } + else + { velocity.x = 0.0f; } positionUpdates(); handleCollision(); - if (!groundCollision) { + if (!groundCollision) + { velocity.y += gravity; - } else { + } + else + { velocity.y = 0.0f; } @@ -106,7 +121,11 @@ private: { velocity.y -= jumpPower; } + move(velocity); + } + void move(Vector2 velocity) + { this->texture.move(velocity); } @@ -114,7 +133,7 @@ private: { if (GetTime() - prevFrame > animationDelay) { - spriteAnimation.x = std::fmod(spriteAnimation.x + 1,3); + spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); Rectangle sourceRect = this->texture.getSourceRect(); sourceRect.x = spriteAnimation.x * this->texture.getSize().x; @@ -125,82 +144,76 @@ private: } } - void positionUpdates() { - const Vector2 pos = this->texture.getPosition(); - const Vector2 size = this->texture.getSize(); - const int tileSize = MapService::getInstance().getMap().getTileSize(); + void positionUpdates() + { + const Vector2 pos = this->texture.getPosition(); + const Vector2 size = this->texture.getSize(); + const int tileSize = MapService::getInstance().getMap().getTileSize(); - const float bottom = pos.y + size.y + velocity.y; - const float left = pos.x + velocity.x; - const float right = pos.x + size.x + velocity.x; - const float top = pos.y + velocity.y; + const float bottom = pos.y + size.y + velocity.y; + const float left = pos.x + velocity.x; + const float right = pos.x + size.x + velocity.x; + const float top = pos.y + velocity.y; - Vector2i topLeft(Vector2i((int)(left + 5) / tileSize, (int)top / tileSize)); // +5 to separate top from side - Vector2i topRight(Vector2i((int)(right - 5) / tileSize, (int)top / tileSize)); // -5 to separate top from side + Vector2i topLeft(Vector2i((int)(left + 5) / tileSize, (int)top / tileSize)); // +5 to separate top from side + Vector2i topRight(Vector2i((int)(right - 5) / tileSize, (int)top / tileSize)); // -5 to separate top from side - Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); - Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); - Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); + Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); + Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side - Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side + Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + tiles.clear(); - tiles.clear(); + tiles.push_back(topLeft); + tiles.push_back(topRight); - tiles.push_back(topLeft); - tiles.push_back(topRight); + tiles.push_back(leftTop); + tiles.push_back(leftBottom); + tiles.push_back(rightTop); + tiles.push_back(rightBottom); - tiles.push_back(leftTop); - tiles.push_back(leftBottom); - tiles.push_back(rightTop); - tiles.push_back(rightBottom); - - tiles.push_back(bottomLeft); - tiles.push_back(bottomRight); + tiles.push_back(bottomLeft); + tiles.push_back(bottomRight); } - /** - * Stores the current position - * Applies the current velocity - * Checks for collision, and removes all velocities that are causing collisions - * Set the object exactly against the object it is colliding with position - */ void handleCollision() { timesCollision.clear(); - for (int i = 0; i < tiles.size(); i++) - { - if (MapService::getInstance().getMap().getColMap()[tiles[i].y][tiles[i].x] == 1) + for (int i = 0; i < tiles.size(); i++) { - timesCollision.push_back(i); - } - } - - groundCollision = false; - - if (timesCollision.size() > 0) - { - for (int i = 0; i < timesCollision.size(); i++) - { - if (timesCollision[i] == 0 || timesCollision[i] == 1) //Topleft and Topright + if (MapService::getInstance().getMap().getColMap()[tiles[i].y][tiles[i].x] == 1) { - velocity.y = 0; + timesCollision.push_back(i); } - else if (timesCollision[i] == 2 || timesCollision[i] == 3 || //Lefttop and Leftbottom - timesCollision[i] == 4 || timesCollision[i] == 5) //Righttop and Lefttop + } + + groundCollision = false; + + if (timesCollision.size() > 0) + { + for (int i = 0; i < timesCollision.size(); i++) { - velocity.x = 0; - } - else if (timesCollision[i] == 6 || timesCollision[i] == 7) //Bottomleft and Bottomright - { - groundCollision = true; + if (timesCollision[i] == 0 || timesCollision[i] == 1) // Topleft and Topright + { + velocity.y = 0; + } + else if (timesCollision[i] == 2 || timesCollision[i] == 3 || // Lefttop and Leftbottom + timesCollision[i] == 4 || timesCollision[i] == 5) // Righttop and Lefttop + { + velocity.x = 0; + } + else if (timesCollision[i] == 6 || timesCollision[i] == 7) // Bottomleft and Bottomright + { + groundCollision = true; + } } } - } // Set velocity.x based on going right or left @@ -217,7 +230,7 @@ private: // Set groundcollision to false // Loop through collisions // Based on their location (TopLeft, RightTop, etc) change the velocity - + // Apply gravity if not on the floor // If jumping and on the floor, go up @@ -261,6 +274,52 @@ private: AudioService::getInstance().playMusic(); } } + + void rotateGun() + { + if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right) + { + float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - this->texture.getPosition().y, + GameService::getInstance().getMouseWorldPos().x - this->texture.getPosition().x); + this->gunRight.setRotation(gunRotationRight * 90); + } + else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) + { + float gunRotationLeft = atan2(GameService::getInstance().getMouseWorldPos().y - this->texture.getPosition().y, + this->texture.getPosition().x - GameService::getInstance().getMouseWorldPos().x); + this->gunLeft.setRotation(-gunRotationLeft * 90); + } + } + + void drawGun() + { + if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) + { + DrawTexturePro( + this->gunRight.getTexture(), + {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().x / 2.0f, + this->gunRight.getSize().x, + this->gunRight.getSize().y}, + {this->gunRight.getSize().x / 2.0f, this->gunRight.getSize().y / 2.0f}, + this->gunRight.getRotation(), + WHITE); + } + else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) + { + DrawTexturePro( + this->gunLeft.getTexture(), + {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().x / 2.0f, + this->gunLeft.getSize().x, + this->gunLeft.getSize().y}, + {this->gunLeft.getSize().x / 2.0f, this->gunLeft.getSize().y / 2.0f}, + this->gunLeft.getRotation(), + WHITE); + } + } }; #endif diff --git a/include/entities/TextureWrapper.h b/include/entities/TextureWrapper.h index accd1c8..09cdf88 100644 --- a/include/entities/TextureWrapper.h +++ b/include/entities/TextureWrapper.h @@ -12,6 +12,7 @@ class TextureWrapper { this->texture = texture; this->size = size; this->position = position; + this->sourceRect = sourceRect; } ~TextureWrapper() { @@ -33,6 +34,10 @@ class TextureWrapper { return this->position; } + float getRotation() { + return this->rotation; + } + void setSourceRect(Rectangle rect) { this->sourceRect = rect; } @@ -41,35 +46,40 @@ class TextureWrapper { this->position = position; } + void setRotation(float rot) { + this->rotation = rot; + } + void move(Vector2 deltaPos) { this->position = {this->position.x + deltaPos.x, this->position.y + deltaPos.y}; } /** Returns the 4 corners of the texture */ - std::unordered_map getCorners() { - std::unordered_map corners; - float top = this->position.y; - float right = this->position.x+this->size.x; - float bottom = this->position.y + this->size.y; - float left = this->position.x; - float margin = 8; // Space to the corner - corners[ECorner::TOP_LEFT] = {left+margin, top}; - corners[ECorner::TOP_RIGHT] = {right-margin, top}; - corners[ECorner::RIGHT_TOP] = {right, top+margin}; - corners[ECorner::RIGHT_BOTTOM] = {right, bottom-margin}; - corners[ECorner::BOTTOM_RIGHT] = {right-margin, bottom}; - corners[ECorner::BOTTOM_LEFT] = {left+margin, bottom}; - corners[ECorner::LEFT_BOTTOM] = {left, bottom-margin}; - corners[ECorner::LEFT_TOP] = {left, top+margin}; + // std::unordered_map getCorners() { + // std::unordered_map corners; + // float top = this->position.y; + // float right = this->position.x+this->size.x; + // float bottom = this->position.y + this->size.y; + // float left = this->position.x; + // float margin = 8; // Space to the corner + // corners[ECorner::TOP_LEFT] = {left+margin, top}; + // corners[ECorner::TOP_RIGHT] = {right-margin, top}; + // corners[ECorner::RIGHT_TOP] = {right, top+margin}; + // corners[ECorner::RIGHT_BOTTOM] = {right, bottom-margin}; + // corners[ECorner::BOTTOM_RIGHT] = {right-margin, bottom}; + // corners[ECorner::BOTTOM_LEFT] = {left+margin, bottom}; + // corners[ECorner::LEFT_BOTTOM] = {left, bottom-margin}; + // corners[ECorner::LEFT_TOP] = {left, top+margin}; - return corners; - } + // return corners; + // } private: Texture2D texture; Rectangle sourceRect; Vector2 size; Vector2 position; + float rotation; }; #endif \ No newline at end of file diff --git a/include/services/GameService.h b/include/services/GameService.h index 5e8e364..d98fd3f 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -1,30 +1,61 @@ #ifndef GAME_SERVICE_H #define GAME_SERVICE_H -#include "Service.h" +#include "services/Service.h" +#include "entities/Game.h" -class GameService : public Service { +class GameService : public Service +{ public: - bool isPaused() { + bool isPaused() + { return this->paused; } - bool isGameOver() { + bool isGameOver() + { return this->gameOver; } - void setPaused(bool paused) { + void setPaused(bool paused) + { this->paused = paused; } - void setGameOver(bool gameOver) { + void setGameOver(bool gameOver) + { this->gameOver = gameOver; } + void setCamera(Camera2D camera) + { + this->camera = camera; + } + + Camera2D &getCamera() + { + return this->camera; + } + + /** + * Returns the position of the mouse in world coordinates + */ + Vector2 getMouseWorldPos() + { + Vector2 mouseWorldPos = {GetMousePosition().x + camera.target.x - camera.offset.x, GetMousePosition().y}; + return mouseWorldPos; + } + // void setGame(Game game) { + // this->game = game; + // } + + // Game& getGame() { + // return this->game; + // } private: bool paused = false; bool gameOver = false; + Camera2D camera; }; - #endif \ No newline at end of file diff --git a/src/entities/Map.cpp b/src/entities/Map.cpp index c1ddcc5..775e090 100644 --- a/src/entities/Map.cpp +++ b/src/entities/Map.cpp @@ -114,18 +114,18 @@ void Map::loadTileset() this->tileset = AssetService::getInstance().loadTexture("Textures/tileset.png"); } -std::unordered_map Map::cornersToTilePos(std::unordered_map corners) -{ - // std::cout << "Starting converting" << std::endl; - std::unordered_map cornersTilePos; - for (const auto &pair : corners) - { - // std::cout << "Foreach pair" << std::endl; - Vector2 original = corners[pair.first]; - cornersTilePos[pair.first] = {floorf(original.x / this->tileSize), floorf(original.y / this->tileSize)}; - } - return cornersTilePos; -} +// std::unordered_map Map::cornersToTilePos(std::unordered_map corners) +// { +// // std::cout << "Starting converting" << std::endl; +// std::unordered_map cornersTilePos; +// for (const auto &pair : corners) +// { +// // std::cout << "Foreach pair" << std::endl; +// Vector2 original = corners[pair.first]; +// cornersTilePos[pair.first] = {floorf(original.x / this->tileSize), floorf(original.y / this->tileSize)}; +// } +// return cornersTilePos; +// } void Map::draw() { @@ -146,35 +146,35 @@ void Map::draw() } } -std::unordered_map Map::checkForCollision(TextureWrapper texture) -{ - // std::cout << "Converting.." << std::endl; - std::unordered_map cornersTilePos = cornersToTilePos(texture.getCorners()); - // std::cout << "Done with converting" << std::endl; +// std::unordered_map Map::checkForCollision(TextureWrapper texture) +// { +// // std::cout << "Converting.." << std::endl; +// std::unordered_map cornersTilePos = cornersToTilePos(texture.getCorners()); +// // std::cout << "Done with converting" << std::endl; - std::unordered_map cornerCollisions; +// std::unordered_map cornerCollisions; - for (const auto &pair : cornersTilePos) - { - // std::cout << "foreach collision check" << std::endl; - Vector2 pos = cornersTilePos[pair.first]; - // std::cout << pos.x << " " << pos.y << std::endl; - if(colMap[pos.y][pos.x] == 1) { - cornerCollisions[pair.first] = true; - } else { - cornerCollisions[pair.first] = false; - } - } - // std::cout << "Finished collision checks" << std::endl; +// for (const auto &pair : cornersTilePos) +// { +// // std::cout << "foreach collision check" << std::endl; +// Vector2 pos = cornersTilePos[pair.first]; +// // std::cout << pos.x << " " << pos.y << std::endl; +// if(colMap[pos.y][pos.x] == 1) { +// cornerCollisions[pair.first] = true; +// } else { +// cornerCollisions[pair.first] = false; +// } +// } +// // std::cout << "Finished collision checks" << std::endl; - return cornerCollisions; +// return cornerCollisions; - // for (int i = 0; i < tiles.size(); i++) - // { - // if (colMap[tiles[i].y][tiles[i].x] == 1) - // { - // timesCollision.push_back(i); - // } - // } +// // for (int i = 0; i < tiles.size(); i++) +// // { +// // if (colMap[tiles[i].y][tiles[i].x] == 1) +// // { +// // timesCollision.push_back(i); +// // } +// // } -} +// } diff --git a/src/screens/GameScreen.cpp b/src/screens/GameScreen.cpp index d3d445a..3b01485 100644 --- a/src/screens/GameScreen.cpp +++ b/src/screens/GameScreen.cpp @@ -1,6 +1,7 @@ #include "screens/GameScreen.h" #include "entities/Map.h" #include "services/StateService.h" +#include "services/GameService.h" GameScreen::GameScreen() { @@ -12,6 +13,7 @@ GameScreen::GameScreen() // this->map = Map(); this->game = Game(); + // GameService::getInstance().setGame(this->game); // this->camera = camera; } diff --git a/src/screens/TitleScreen.cpp b/src/screens/TitleScreen.cpp index b3bf266..98e7f35 100644 --- a/src/screens/TitleScreen.cpp +++ b/src/screens/TitleScreen.cpp @@ -29,13 +29,13 @@ TitleScreen::TitleScreen() AudioService::getInstance().playMusic(); } - InputService::getInstance().setKeysToWatch({KEY_ENTER}, {}); + InputService::getInstance().setKeysToWatch({KEY_ENTER}, {MOUSE_BUTTON_LEFT}); } void TitleScreen::logic() { // std::cout << "Next screen!" << std::endl; - if (InputService::getInstance().isKeyPressed(KEY_ENTER)) + if (InputService::getInstance().isKeyPressed(KEY_ENTER) || InputService::getInstance().isMouseButtonPressed(MOUSE_BUTTON_LEFT)) { StateService::getInstance().setScreen(EScreen::MAIN); } From 55792bb9548c0e39c38f1440ae10d783dfd3167b Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Fri, 13 Dec 2024 22:04:33 +0100 Subject: [PATCH 04/12] [Added] The ability to fire bullets to the player --- include/entities/Bullet.h | 83 +++++++++++++++++++++++++++++++++ include/entities/EBullet.h | 20 ++++++++ include/entities/Game.h | 25 +++++++--- include/entities/PBullet.h | 23 +++++++++ include/entities/Player.h | 55 ++++++++++++++++++++-- include/services/GameService.h | 57 ++++++++++++++++++++++ include/services/InputService.h | 12 +++++ 7 files changed, 266 insertions(+), 9 deletions(-) create mode 100644 include/entities/Bullet.h create mode 100644 include/entities/EBullet.h create mode 100644 include/entities/PBullet.h diff --git a/include/entities/Bullet.h b/include/entities/Bullet.h new file mode 100644 index 0000000..b623fb7 --- /dev/null +++ b/include/entities/Bullet.h @@ -0,0 +1,83 @@ +#ifndef BULLET_H +#define BULLET_H + +#include "raylib.h" +#include +#include "entities/TextureWrapper.h" +#include "entities/Vector2i.h" +#include "services/MapService.h" +#include + +class Bullet +{ +public: + Bullet() {} + Bullet(Vector2 pos, float direction) + { + this->position = pos; + this->direction = direction; + std::cout << direction << std::endl; + } + + // ~Bullet() {} + + void logic() + { + move(); + hasCollided = false; + envCollision(); + objCollision(); + // // detectCollision(); + } + + void draw() + { + DrawCircle(position.x, position.y, size, color); + } + + bool getHasCollided() + { + return this->hasCollided; + } + +protected: + Color color = RED; + +private: + // TextureWrapper object; + Vector2 position; + float direction; + float velocity = 15.0f; + int size = 7; + bool hasCollided = false; + + void move() + { + // float directionInRadians = this->direction * M_PI / 180.0f; + position.x += velocity * cos(direction); + position.y += velocity * sin(direction); + } + void envCollision() + { + if (position.y < 0 || position.y > GetScreenHeight()) + { + hasCollided = true; + return; + } + + const int tileSize = MapService::getInstance().getMap().getTileSize(); + Vector2i tilePos = Vector2i((int)this->position.x / tileSize, (int)this->position.y / tileSize); + if (MapService::getInstance().getMap().getColMap()[tilePos.y][tilePos.x] == 1) + { + hasCollided = true; + return; + } + } + virtual void objCollision() = 0; + + // void handleCollision() { + + // } +}; + +#endif \ No newline at end of file diff --git a/include/entities/EBullet.h b/include/entities/EBullet.h new file mode 100644 index 0000000..3118b9b --- /dev/null +++ b/include/entities/EBullet.h @@ -0,0 +1,20 @@ +// #ifndef EBULLET_H +// #define EBULLET_H + +// #include "Bullet.h" +// #include + +// class EBullet : public Bullet { +// public: + +// EBullet() {} +// ~EBullet() {} + +// void detectCollision() override { +// std::cout << "Enemy bullets" << std::endl; +// } +// private: + +// }; + +// #endif \ No newline at end of file diff --git a/include/entities/Game.h b/include/entities/Game.h index 7d61180..2639fe0 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -10,14 +10,15 @@ #include "services/GameService.h" #include "entities/Player.h" #include "entities/Text.h" +#include "entities/Bullet.h" +#include "entities/PBullet.h" class Game { public: Game() { - GameService::getInstance().setGameOver(false); - GameService::getInstance().setPaused(false); + GameService::getInstance().resetGame(); TextureWrapper wrapper1 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME1), {GetScreenWidth() * 2.0f, (float)GetScreenHeight()}, {0, 0}); wrapper1.setSourceRect({0, 0, wrapper1.getSize().x, wrapper1.getSize().y}); @@ -42,10 +43,8 @@ public: camera.zoom = 1.0f; // this->camera = camera; GameService::getInstance().setCamera(camera); - this->player = Player(); this->player.setPosition({200, 350}); - this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15, 15}, {0, 0}, {0, 0, 15, 15}); initPauseScreen(); @@ -81,7 +80,9 @@ public: AudioService::getInstance().pauseMusic(); return; } - // this->camera.offset.x -= 1.5; + + GameService::getInstance().bulletLogic(); + this->player.logic(); if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) { @@ -95,6 +96,13 @@ public: BeginMode2D(GameService::getInstance().getCamera()); this->background.draw(); + + // std::vector pbs = GameService::getInstance().getPlayerBullets(); + // for (int i = 0; i < GameService::getInstance().getPlayerBullets().size(); i++) + // { + // GameService::getInstance().getPlayerBullets()[i].draw(); + // } + GameService::getInstance().drawBullets(); this->map.draw(); this->player.draw(); @@ -126,12 +134,13 @@ private: std::vector gameOverScreenButtons; int hoveredGameOverScreenButton = 0; + // std::vector playerBullets; + // bool isPaused = false; // bool isGameOver = false; // std::vector enemies // std::vector crates - // std::vector playerBullets // std::vector enemyBullets // std::vector jellies // std::vector ammo @@ -241,6 +250,10 @@ private: } } + void shootLogic() + { + } + void drawPaused() { // 100, 100, 255, 60 diff --git a/include/entities/PBullet.h b/include/entities/PBullet.h new file mode 100644 index 0000000..f440ba7 --- /dev/null +++ b/include/entities/PBullet.h @@ -0,0 +1,23 @@ +#ifndef PBULLET_H +#define PBULLET_H + +#include "entities/Bullet.h" +#include + +class PBullet : public Bullet { +public: + + PBullet() {} + PBullet(Vector2 pos, float angle) : Bullet(pos, angle) { + this->color = YELLOW; + } + + +private: + void objCollision() override { + + } + +}; + +#endif \ No newline at end of file diff --git a/include/entities/Player.h b/include/entities/Player.h index e530e19..c260ce1 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -10,6 +10,7 @@ #include "entities/TextureWrapper.h" #include "enums/ECorner.h" #include "entities/Vector2i.h" +#include "entities/PBullet.h" class Player { @@ -22,6 +23,8 @@ public: spriteAnimation = {0, 0}; this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_RIGHT), {58, 29}, {0, 0}); this->gunLeft = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_LEFT), {58, 29}, {0, 0}); + this->ammo = max_ammo; + this->prevShot = GetTime(); } ~Player() { @@ -30,6 +33,7 @@ public: void logic() { rotateGun(); + shoot(); movement(); outofboundsCheck(); } @@ -59,9 +63,9 @@ private: float jumpPower = 7.2f; float moveSpeed = 5.0f; float gravity = 0.6f; - float animationDelay = 0.2f; // In seconds + float animationDelay = 0.2f; // In seconds double prevFrame; - Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction + Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction enum Direction { Down, @@ -70,11 +74,16 @@ private: Up }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction - std::vector tiles; // Holds eight points of the player for accurate collision + std::vector tiles; // Holds eight points of the player for accurate collision std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground bool crateTopCollision; // Whether the player is standing on top of a crate + int ammo; + int max_ammo = 100; + float firingRate= 0.2f; // Delay in seconds + double prevShot; // The last time the player fired a bullet + void applyGravity() { if (!groundCollision) @@ -320,6 +329,46 @@ private: WHITE); } } + + void shoot() + { + if (InputService::getInstance().isMouseButtonDown(MOUSE_BUTTON_LEFT) && GetTime() - prevShot > firingRate && groundCollision) + { + prevShot = GetTime(); + if (ammo == 0) { + // Play empty gun sound + return; + } + // Play shooting sound + ammo--; + Vector2 bulletStartLocation = {this->texture.getPosition().x + 10, this->texture.getPosition().y + 10}; + + Vector2 mouseWorldPos = GameService::getInstance().getMouseWorldPos(); + + if (spriteAnimation.y == Left) + { + bulletStartLocation.y = this->texture.getPosition().y + 10; + bulletStartLocation.x = this->texture.getPosition().x - 5; + } + if (spriteAnimation.y == Right) + { + bulletStartLocation.y = this->texture.getPosition().y + 10; + bulletStartLocation.x = this->texture.getPosition().x + 37; + } + + if (mouseWorldPos.x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right || + mouseWorldPos.x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) + { + // PlayGunSound(); + // currentAmmo--; + // .push_back(bulletStartLocation); + float angleShot = atan2(mouseWorldPos.y - bulletStartLocation.y, + mouseWorldPos.x - bulletStartLocation.x); + GameService::getInstance().addPlayerBullet(PBullet(bulletStartLocation, angleShot)); + // bulletDirections.push_back(angleShot); + } + } + } }; #endif diff --git a/include/services/GameService.h b/include/services/GameService.h index d98fd3f..dcf1ebd 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -3,6 +3,9 @@ #include "services/Service.h" #include "entities/Game.h" +#include "entities/PBullet.h" +#include +#include class GameService : public Service { @@ -52,10 +55,64 @@ public: // return this->game; // } + void addPlayerBullet(PBullet bullet) + { + this->playerBullets.push_back(bullet); + } + + // std::list& getPlayerBullets() { + // return this->playerBullets; + // } + + // void removeCollidedBullets() + // { + // for (auto it = playerBullets.begin(); it != playerBullets.end(); /* no increment here */) + // { + // if (it->getHasCollided()) + // { + // it = playerBullets.erase(it); // Remove element and get the next valid iterator + // } + // else + // { + // ++it; // Only increment if no removal happens + // } + // } + // } + + void bulletLogic() + { + for (auto it = this->playerBullets.begin(); it != this->playerBullets.end();) + { + it->logic(); + if (it->getHasCollided()) + it = this->playerBullets.erase(it); + else + ++it; + } + } + + void drawBullets() + { + for (auto it = this->playerBullets.begin(); it != this->playerBullets.end();) + { + it->draw(); + ++it; + } + } + + void resetGame() + { + paused = false; + gameOver = false; + // playerBullets.clear(); + } + private: bool paused = false; bool gameOver = false; Camera2D camera; + + std::list playerBullets; }; #endif \ No newline at end of file diff --git a/include/services/InputService.h b/include/services/InputService.h index e7f537b..84b06bc 100644 --- a/include/services/InputService.h +++ b/include/services/InputService.h @@ -57,10 +57,15 @@ public: for (auto button : watchedMouseButtons) { mouseButtonsPressed[button] = false; + mouseButtonsDown[button] = false; if (IsMouseButtonPressed(button)) { mouseButtonsPressed[button] = true; } + if (IsMouseButtonDown(button)) + { + mouseButtonsDown[button] = true; + } } } @@ -78,22 +83,29 @@ public: return mouseButtonsPressed[button]; } + bool isMouseButtonDown(MouseButton button) { + return mouseButtonsDown[button]; + } + private: std::unordered_set watchedKeys; std::unordered_map keysPressed; std::unordered_map keysDown; std::unordered_set watchedMouseButtons; std::unordered_map mouseButtonsPressed; + std::unordered_map mouseButtonsDown; void setMouseButtonsToWatch(std::vector mouseButtonsToWatch) { this->watchedMouseButtons.clear(); this->mouseButtonsPressed.clear(); + this->mouseButtonsDown.clear(); for (int i = 0; i < mouseButtonsToWatch.size(); i++) { this->watchedMouseButtons.insert(mouseButtonsToWatch[i]); this->mouseButtonsPressed[mouseButtonsToWatch[i]] = false; + this->mouseButtonsDown[mouseButtonsToWatch[i]] = false; } } }; From de958b02e60f41c4582985ed82fbfc52bcee2c1e Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Sun, 15 Dec 2024 00:12:16 +0100 Subject: [PATCH 05/12] [Added] Crates, Hud for bullets and health --- .devcontainer/devcontainer.json | 3 +- .vscode/launch.json | 23 ++++ .vscode/tasks.json | 20 +++ Dockerfile | 2 +- include/entities/Bullet.h | 11 +- include/entities/Crate.h | 48 +++++++ include/entities/Game.h | 71 +++++++++- include/entities/Map.h | 7 +- include/entities/PBullet.h | 26 +++- include/entities/Player.h | 39 ++++-- include/enums/ESound.h | 17 +++ include/services/AssetService.h | 7 +- include/services/CollectableService.h | 184 ++++++++++++++++++++++++++ include/services/GameService.h | 5 + scripts/build_and_run_project.sh | 6 +- src/services/AssetService.cpp | 7 + src/services/StateService.cpp | 10 +- 17 files changed, 448 insertions(+), 38 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 include/entities/Crate.h create mode 100644 include/enums/ESound.h create mode 100644 include/services/CollectableService.h diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0e4d1b2..462a8c6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -28,7 +28,8 @@ "settings": {}, "extensions": [ "esbenp.prettier-vscode", - "ms-vscode.cpptools-extension-pack" + "ms-vscode.cpptools-extension-pack", + "vadimcn.vscode-lldb" ] } }, diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e6964e4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug with LLDB", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/dist/jelly_rescue", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "miDebuggerPath": "/usr/bin/lldb-mi", + "preLaunchTask": "build jelly_rescue", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..403f3c9 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,20 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build jelly_rescue", + "type": "shell", + "command": "bash", + "args": [ + "-c", + "g++ -std=c++20 -g -O0 $(find src/ -name \"*.cpp\") -o dist/jelly_rescue -I./include -L$LD_LIBRARY_PATH -lraylib -lGL -lm -lpthread -ldl -lrt -lX11" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": ["$gcc"], + "detail": "Compiling the jelly_rescue project" + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 25c0935..1eab433 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apt-get update \ cmake \ libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev \ libglu1-mesa-dev libxcursor-dev libxinerama-dev libwayland-dev libxkbcommon-dev \ - sudo \ + sudo lldb \ alsa-utils \ && rm -rf /var/cache/apt/* diff --git a/include/entities/Bullet.h b/include/entities/Bullet.h index b623fb7..d65ff65 100644 --- a/include/entities/Bullet.h +++ b/include/entities/Bullet.h @@ -16,7 +16,6 @@ public: { this->position = pos; this->direction = direction; - std::cout << direction << std::endl; } // ~Bullet() {} @@ -25,8 +24,8 @@ public: { move(); hasCollided = false; - envCollision(); objCollision(); + envCollision(); // // detectCollision(); } @@ -42,14 +41,14 @@ public: protected: Color color = RED; + Vector2 position; + int size = 7; + bool hasCollided = false; private: // TextureWrapper object; - Vector2 position; float direction; float velocity = 15.0f; - int size = 7; - bool hasCollided = false; void move() { @@ -59,7 +58,7 @@ private: } void envCollision() { - if (position.y < 0 || position.y > GetScreenHeight()) + if (position.x < 0 || position.y < 0 || position.y > GetScreenHeight()) { hasCollided = true; return; diff --git a/include/entities/Crate.h b/include/entities/Crate.h new file mode 100644 index 0000000..3f31bce --- /dev/null +++ b/include/entities/Crate.h @@ -0,0 +1,48 @@ +#ifndef CRATE_H +#define CRATE_H + +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" +#include "entities/PBullet.h" + +class Crate { +public: + Crate() { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CRATE),{32,32}); + } + + Crate(Vector2 pos) { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CRATE),{32,32}, pos); + } + + void logic() { + + } + + bool hasBeenHit() { + return this->hit; + } + + void setHit(bool hit) { + this->hit = hit; + } + + void draw() { + DrawTexture(this->texture.getTexture(), this->texture.getPosition().x, this->texture.getPosition().y, WHITE); + } + + TextureWrapper& getObject() { + return this->texture; + } + +private: + TextureWrapper texture; + bool hit; // Whether a PBullet has hit the create +}; + + + + + + +#endif \ No newline at end of file diff --git a/include/entities/Game.h b/include/entities/Game.h index 2639fe0..3c797ab 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -8,6 +8,7 @@ #include "services/MapService.h" #include "services/InputService.h" #include "services/GameService.h" +#include "services/CollectableService.h" #include "entities/Player.h" #include "entities/Text.h" #include "entities/Bullet.h" @@ -47,6 +48,8 @@ public: this->player.setPosition({200, 350}); this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15, 15}, {0, 0}, {0, 0, 15, 15}); + CollectableService::getInstance().initCollectables(); + initPauseScreen(); initGameOverScreen(); @@ -80,8 +83,9 @@ public: AudioService::getInstance().pauseMusic(); return; } - + GameService::getInstance().bulletLogic(); + CollectableService::getInstance().collectableLogic(); this->player.logic(); if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) @@ -104,6 +108,7 @@ public: // } GameService::getInstance().drawBullets(); this->map.draw(); + CollectableService::getInstance().drawCollectables(); this->player.draw(); EndMode2D(); @@ -116,6 +121,7 @@ public: { drawGameOver(); } + drawHud(); drawCrosshair(); EndDrawing(); } @@ -250,10 +256,6 @@ private: } } - void shootLogic() - { - } - void drawPaused() { // 100, 100, 255, 60 @@ -290,6 +292,65 @@ private: // 0, // WHITE); } + + void drawHud() + { + drawHudHealth(); + drawHudBullets(); + } + + void drawHudBullets() + { + Vector2i pixel_pos = Vector2i(5, 40); + Texture2D ammoSprite = AssetService::getInstance().getSprite(ESprite::HUD_AMMO); + Vector2i display_pos; + for (int i = 0; i < this->player.getAmmo(); i++) + { + if (i <= 49) + { + display_pos = Vector2i(pixel_pos.x + ammoSprite.width * i, pixel_pos.y); + } + if (i > 49 && i <= 99) + { + display_pos = Vector2i(pixel_pos.x + ammoSprite.width * (i - (50)), pixel_pos.y + ammoSprite.height); + } + else if (i > 99 && i <= 149) + { + display_pos = Vector2i(pixel_pos.x + ammoSprite.width * (i - (100)), pixel_pos.y + ammoSprite.height * 2); + } + else if (i > 149 && i <= 199) + { + display_pos = Vector2i(pixel_pos.x + ammoSprite.width * (i - (150)), pixel_pos.y + ammoSprite.height * 3); + } + else if (i > 199 && i <= 249) + { + display_pos = Vector2i(pixel_pos.x + ammoSprite.width * (i - (200)), pixel_pos.y + ammoSprite.height * 4); + } + DrawTexture(ammoSprite, display_pos.x, display_pos.y, WHITE); + } + } + + void drawHudHealth() + { + Vector2i pixel_pos = Vector2i(5, 5); + Texture2D noHealthSprite = AssetService::getInstance().getSprite(ESprite::HUD_HEART_EMPTY); + Texture2D healthSprite = AssetService::getInstance().getSprite(ESprite::HUD_HEART); + // sVector2f coord_pos = GetWindow().mapPixelToCoords(pixel_pos); + for (int i = 0; i < this->player.getMaxHealth(); i++) + { + Vector2i pos = Vector2i(pixel_pos.x + noHealthSprite.width * i, pixel_pos.y); + // noHealthSprite[i].setPosition(coord_pos.x + noHealthTexture.getSize().x * i, coord_pos.y); + DrawTexture(noHealthSprite, pos.x, pos.y, WHITE); + } + for (int i = 0; i < this->player.getHealth(); i++) + { + // healthSprite[i].setPosition(coord_pos.x + healthTexture.getSize().x * i, coord_pos.y); + // DrawTexture(ammoSprite, display_pos.x, display_pos.y, WHITE); + Vector2i pos = Vector2i(pixel_pos.x + healthSprite.width * i, pixel_pos.y); + // noHealthSprite[i].setPosition(coord_pos.x + noHealthTexture.getSize().x * i, coord_pos.y); + DrawTexture(healthSprite, pos.x, pos.y, WHITE); + } + } }; #endif \ No newline at end of file diff --git a/include/entities/Map.h b/include/entities/Map.h index f93bf97..acdbf7b 100644 --- a/include/entities/Map.h +++ b/include/entities/Map.h @@ -1,9 +1,10 @@ #ifndef MAP_H #define MAP_H -#include #include "raylib.h" #include "entities/TextureWrapper.h" +#include "entities/Vector2i.h" +#include #include /** @@ -28,6 +29,10 @@ public: return this->colMap; } + void updateColmapTile(Vector2i tile, bool collision) { + this->colMap[tile.y][tile.x] = collision ? 1 : 0; + } + private: /** Holds the number for every single tile, which refers to a certain sprite in the tileset */ std::vector> map; diff --git a/include/entities/PBullet.h b/include/entities/PBullet.h index f440ba7..4febe31 100644 --- a/include/entities/PBullet.h +++ b/include/entities/PBullet.h @@ -1,23 +1,35 @@ #ifndef PBULLET_H #define PBULLET_H +#include "services/CollectableService.h" +#include "services/AssetService.h" #include "entities/Bullet.h" #include -class PBullet : public Bullet { +class PBullet : public Bullet +{ public: - PBullet() {} - PBullet(Vector2 pos, float angle) : Bullet(pos, angle) { + PBullet(Vector2 pos, float angle) : Bullet(pos, angle) + { this->color = YELLOW; } - private: - void objCollision() override { - + void objCollision() override + { + /** Check for collisions with creates */ + for (auto &crate : CollectableService::getInstance().getCrates()) + { + if(this->position.x + this->size > crate.getObject().getPosition().x && this->position.x - this->size < crate.getObject().getPosition().x && + this->position.y + this->size > crate.getObject().getPosition().y && this->position.y - this->size < crate.getObject().getPosition().y ) { + crate.setHit(true); + AssetService::getInstance().playSound(ESound::CRATE_BREAK); + // AssetService::getInstance().playSound(ESound::JELLY_FREED); + this->hasCollided = true; + } } - + } }; #endif \ No newline at end of file diff --git a/include/entities/Player.h b/include/entities/Player.h index c260ce1..9840767 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -9,6 +9,7 @@ #include "services/GameService.h" #include "entities/TextureWrapper.h" #include "enums/ECorner.h" +#include "enums/ESound.h" #include "entities/Vector2i.h" #include "entities/PBullet.h" @@ -20,11 +21,12 @@ public: this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER), {32, 32}, {200, 200}); prevFrame = GetTime(); - spriteAnimation = {0, 0}; + spriteAnimation = {0, Right}; this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_RIGHT), {58, 29}, {0, 0}); this->gunLeft = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_LEFT), {58, 29}, {0, 0}); this->ammo = max_ammo; this->prevShot = GetTime(); + this->health = max_health; } ~Player() { @@ -54,6 +56,18 @@ public: this->texture.setPosition(pos); } + int getAmmo() { + return this->ammo; + } + + uint8_t getHealth() { + return this->health; + } + + uint8_t getMaxHealth() { + return this->max_health; + } + protected: private: TextureWrapper texture; @@ -63,9 +77,9 @@ private: float jumpPower = 7.2f; float moveSpeed = 5.0f; float gravity = 0.6f; - float animationDelay = 0.2f; // In seconds + float animationDelay = 0.2f; // In seconds double prevFrame; - Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction + Vector2i spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction enum Direction { Down, @@ -79,10 +93,12 @@ private: bool groundCollision; // Keeps track of whether or not the player has collided with the ground bool crateTopCollision; // Whether the player is standing on top of a crate - int ammo; - int max_ammo = 100; - float firingRate= 0.2f; // Delay in seconds + uint8_t ammo; // Current ammo of the player + uint8_t max_ammo = 100; // Max ammo of the player + float firingRate= 0.2f; // Delay in seconds double prevShot; // The last time the player fired a bullet + uint8_t health; // Current health of the player + uint8_t max_health = 10; // Max health of the player void applyGravity() { @@ -278,6 +294,7 @@ private: { if (this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) { + this->health = 0; GameService::getInstance().setGameOver(true); AudioService::getInstance().setMusic(EMusic::GAME_OVER); AudioService::getInstance().playMusic(); @@ -335,14 +352,18 @@ private: if (InputService::getInstance().isMouseButtonDown(MOUSE_BUTTON_LEFT) && GetTime() - prevShot > firingRate && groundCollision) { prevShot = GetTime(); - if (ammo == 0) { + if (ammo == 0 || (GameService::getInstance().getMouseWorldPos().x > (this->texture.getPosition().x - this->gunLeft.getSize().x/2.0f) && spriteAnimation.y == Left) || + (GameService::getInstance().getMouseWorldPos().x < (this->texture.getPosition().x + this->texture.getSize().x + gunLeft.getSize().x/2.0f) && spriteAnimation.y == Right)) + { // Play empty gun sound + AssetService::getInstance().playSound(ESound::NO_AMMO); return; } // Play shooting sound + AssetService::getInstance().playSound(ESound::SHOOT); ammo--; - Vector2 bulletStartLocation = {this->texture.getPosition().x + 10, this->texture.getPosition().y + 10}; + Vector2 bulletStartLocation = {this->texture.getPosition().x + 10, this->texture.getPosition().y + 10}; Vector2 mouseWorldPos = GameService::getInstance().getMouseWorldPos(); if (spriteAnimation.y == Left) @@ -353,7 +374,7 @@ private: if (spriteAnimation.y == Right) { bulletStartLocation.y = this->texture.getPosition().y + 10; - bulletStartLocation.x = this->texture.getPosition().x + 37; + bulletStartLocation.x = this->texture.getPosition().x + 22; } if (mouseWorldPos.x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right || diff --git a/include/enums/ESound.h b/include/enums/ESound.h new file mode 100644 index 0000000..0f0aa6a --- /dev/null +++ b/include/enums/ESound.h @@ -0,0 +1,17 @@ +#ifndef ESOUND_H +#define ESOUND_H + +enum class ESound { + SHOOT, + NO_AMMO, + CRATE_BREAK, + JELLY_FREED, + HEAL_PICKUP, + AMMO_PICKUP, + POWERUP, + PLAYER_DAMAGE, + ENEMY_DEFEATED, + VICTORY +}; + +#endif \ No newline at end of file diff --git a/include/services/AssetService.h b/include/services/AssetService.h index 5eec69e..42080cf 100644 --- a/include/services/AssetService.h +++ b/include/services/AssetService.h @@ -10,6 +10,7 @@ #include "enums/EBackground.h" #include "enums/EMap.h" #include "enums/ESprite.h" +#include "enums/ESound.h" #include "services/Service.h" class AssetService: public Service @@ -32,6 +33,10 @@ public: return this->sprites[sprite]; } + void playSound(ESound sound) { + PlaySound(sounds[sound]); + } + protected: AssetService(); @@ -41,7 +46,7 @@ private: std::string assetDir; std::unordered_map fonts; - std::vector sounds; + std::unordered_map sounds; std::unordered_map music; std::unordered_map backgrounds; std::unordered_map maps; diff --git a/include/services/CollectableService.h b/include/services/CollectableService.h new file mode 100644 index 0000000..b391848 --- /dev/null +++ b/include/services/CollectableService.h @@ -0,0 +1,184 @@ +#ifndef COLLECTABLE_SERVICE_H +#define COLLECTABLE_SERVICE_H + +#include "raylib.h" +#include "services/Service.h" +#include "services/MapService.h" +#include "entities/Vector2i.h" +#include "entities/Crate.h" +#include "entities/PBullet.h" +#include + +class CollectableService : public Service +{ +public: + void initCollectables() + { + initCrates(); + } + + void collectableLogic() + { + crateLogic(); + } + + void drawCollectables() + { + drawCrates(); + } + + // void checkCratesForCollision(PBullet bullet) + // { + // for (auto it = crates.begin(); it != crates.end();) + // { + // auto &crate = *it; + // // if (*it % 2 == 0) { + // if (crate.bulletCollision(bullet)) + // { + // Vector2 pos = crate.getObject().getPosition(); + // Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); + // MapService::getInstance().getMap().updateColmapTile(tilePos, false); + // it = crates.erase(it); // erase returns the next iterator + // } + // else + // { + // ++it; + // } + // } + // } + + std::vector& getCrates() { + return this->crates; + } + +private: + void initCrates() + { + crates.clear(); + Vector2 location; + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < 3; i++) + { + crates.push_back(Crate()); + // crateLocation.push_back(location); + } + crates[0].getObject().setPosition({38.0f * tileSize, 9.0f * tileSize}); + crates[1].getObject().setPosition({41.0f * tileSize, 5.0f * tileSize}); + crates[2].getObject().setPosition({68.0f * tileSize, 4.0f * tileSize}); + + for (int i = 0; i < crates.size(); i++) + { + Vector2i crateTilePos = Vector2i(crates[i].getObject().getPosition().x / tileSize, crates[i].getObject().getPosition().y / tileSize); + MapService::getInstance().getMap().updateColmapTile(crateTilePos, true); + } + // crates[0].y = 9 * tileSize; + + // crateLocation[1].x = 41 * tileSize; + // crateLocation[1].y = 5 * tileSize; + + // crateLocation[2].x = 68 * tileSize; + // crateLocation[2].y = 4 * tileSize; + } + + void initJellies() + { + } + + void crateLogic() + { + for (auto it = crates.begin(); it != crates.end();) + { + auto &crate = *it; + /** + * If the crate has been hit: + * 1. Remove the collision tile from the collision map + * + */ + if (crate.hasBeenHit()) + { + Vector2 pos = crate.getObject().getPosition(); + Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); + MapService::getInstance().getMap().updateColmapTile(tilePos, false); + it = crates.erase(it); + } + else + { + ++it; + } + } + } + + // void destroyCrate(int index) { + // Vector2 pos = crates[index].getObject().getPosition(); + // Vector2i tilePos = Vector2i(pos.x/MapService::getInstance().getMap().getTileSize(), pos.y/MapService::getInstance().getMap().getTileSize()); + // MapService::getInstance().getMap().updateColmapTile(tilePos, false); + // crates.erase(crates.begin() + index); + // } + + // void checkCratesForCollision(PBullet bullet) + // { + // for (auto it = crates.begin(); it != crates.end();) + // { + // auto &crate = *it; + // // if (*it % 2 == 0) { + // if (crate.bulletCollision(bullet)) + // { + // Vector2 pos = crate.getObject().getPosition(); + // Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); + // MapService::getInstance().getMap().updateColmapTile(tilePos, false); + // it = crates.erase(it); // erase returns the next iterator + // } + // else + // { + // ++it; + // } + // } + // } + + void jellyLogic() + { + } + + void drawCrates() + { + for (int i = 0; i < crates.size(); i++) + { + crates[i].draw(); + } + } + + void drawJellies() + { + } + + void drawHudJellies() + { + } + + void drawAmmoPacks() + { + } + + void drawHealthpacks() + { + } + + // sf::Text jelliesFreed; // Holds the text that shows how many jellies are freed + // sf::Font jelliesFreedFont; // Holds the font used for the text of the display + + // sf::Sprite jelliesFreedSprite; // Holds the sprite of the jelly in the top right corner + + // Vector2i jellyDisplayLocation; // Holds the location where the amount of jellies freed are displayed + std::vector crates; + // std::vector crateSprite; // Holds the sprites of the crates + // std::vector jellySprite; // Holds the sprites of the jellies inside the crates + // std::vector crateLocation; // Holds the locations of the crates/jellies + // std::vector velocity; // Makes sure the jellies jump when the crate is destroyed + // std::vector collision; // Keeps track of whether or not the player has collided with a crate + // std::vector crateDestroyed; // Keeps track of whether or not a crate has been destroyed + + // unsigned int totalAmntCrates; // Holds the total amount of crates + // unsigned int amntCratesDestroyed; // Holds the amount of crates that have been destroyed +}; + +#endif \ No newline at end of file diff --git a/include/services/GameService.h b/include/services/GameService.h index dcf1ebd..75e3190 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -47,6 +47,11 @@ public: Vector2 mouseWorldPos = {GetMousePosition().x + camera.target.x - camera.offset.x, GetMousePosition().y}; return mouseWorldPos; } + + Vector2 toWorldPos(Vector2i pixelPos) { + Vector2 worldPos = {(float)pixelPos.x + camera.target.x - camera.offset.x, (float)pixelPos.y}; + return worldPos; + } // void setGame(Game game) { // this->game = game; // } diff --git a/scripts/build_and_run_project.sh b/scripts/build_and_run_project.sh index 96d0c67..4a2e7ed 100755 --- a/scripts/build_and_run_project.sh +++ b/scripts/build_and_run_project.sh @@ -1,6 +1,8 @@ #!/bin/bash clear # g++ src/main.cpp -o dist/jelly_rescue -L$LD_LIBRARY_PATH -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 -g++ $(find src/ -name "*.cpp") -o dist/jelly_rescue -I./include -L$LD_LIBRARY_PATH -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 +# g++ -std=c++20 $(find src/ -name "*.cpp") -o dist/jelly_rescue -I./include -L$LD_LIBRARY_PATH -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 +g++ -std=c++20 -g -O0 $(find src/ -name "*.cpp") -o dist/jelly_rescue -I./include -L$LD_LIBRARY_PATH -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 + # g++ src/main.cpp -o dist/jelly_rescue -L$LD_LIBRARY_PATH -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 -./dist/jelly_rescue +gdb dist/jelly_rescue diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index 7a75245..80e75d6 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -20,6 +20,13 @@ void AssetService::setAssets() music[EMusic::GAME_OVER] = loadMusic("Audio/Music/gameOverTheme_2.ogg", false); /** Todo: set volume */ + sounds[ESound::SHOOT] = loadSound("Audio/Sounds/gunShot.wav"); + SetSoundVolume(sounds[ESound::SHOOT], 0.4f); + sounds[ESound::NO_AMMO] = loadSound("Audio/Sounds/outOfAmmo.wav"); + sounds[ESound::CRATE_BREAK] = loadSound("Audio/Sounds/crateBreak.wav"); + SetSoundVolume(sounds[ESound::CRATE_BREAK], 1.0f); + sounds[ESound::JELLY_FREED] = loadSound("Audio/Sounds/jellyFreed.wav"); + backgrounds[EBackground::TUTORIAL] = loadTexture("Textures/TutorialBackground2.png"); backgrounds[EBackground::GAME1] = loadTexture("Textures/GameBackground_1.png"); backgrounds[EBackground::GAME2] = loadTexture("Textures/GameBackground_2.jpg"); diff --git a/src/services/StateService.cpp b/src/services/StateService.cpp index 8115e18..40e3ead 100644 --- a/src/services/StateService.cpp +++ b/src/services/StateService.cpp @@ -1,13 +1,13 @@ -#include "services/StateService.h" #include "raylib.h" +#include "services/StateService.h" +#include "services/AudioService.h" +#include "services/InputService.h" #include "screens/TitleScreen.h" #include "screens/MainScreen.h" #include "screens/TutorialScreen.h" #include "screens/GameScreen.h" #include "screens/GameOverScreen.h" #include -#include "services/AudioService.h" -#include "services/InputService.h" void StateService::setScreen(EScreen state) { @@ -45,7 +45,7 @@ void StateService::startGame() { // if(!instance) // return; - // InitAudioDevice(); + InitAudioDevice(); // InitWindow(640,544, "Jelly Rescue"); InitWindow(screenSize.x, screenSize.y, "Jelly Rescue"); SetTargetFPS(45); @@ -75,7 +75,7 @@ void StateService::startGame() #endif CloseWindow(); - // CloseAudioDevice(); + CloseAudioDevice(); } void StateService::handleMusic() From 5af531a08b4230a3732b4fcbfafc0fc9e3fb281d Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Sun, 15 Dec 2024 20:14:57 +0100 Subject: [PATCH 06/12] [Added] Jellies, healthpacks, ammopacks, powerups, [Updated] Gun rotation --- .devcontainer/devcontainer.json | 3 +- .vscode/settings.json | 58 ++++++ include/entities/AmmoPack.h | 43 +++++ include/entities/Background.h | 6 + include/entities/Bullet.h | 2 +- include/entities/Crate.h | 7 +- include/entities/Game.h | 46 +++-- include/entities/HealthPack.h | 43 +++++ include/entities/Jelly.h | 50 +++++ include/entities/PBullet.h | 4 +- include/entities/Player.h | 137 +++++++++----- include/entities/Powerup.h | 58 ++++++ include/services/CollectableService.h | 251 ++++++++++++++++++-------- include/services/GameService.h | 7 - src/services/AssetService.cpp | 4 +- 15 files changed, 564 insertions(+), 155 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 include/entities/AmmoPack.h create mode 100644 include/entities/HealthPack.h create mode 100644 include/entities/Jelly.h create mode 100644 include/entities/Powerup.h diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 462a8c6..367e543 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,7 +20,8 @@ "source=./scripts,target=/app/scripts,type=bind", "source=./dist,target=/app/dist,type=bind", "source=./assets,target=/app/assets,type=bind", - "source=./include,target=/app/include,type=bind" + "source=./include,target=/app/include,type=bind", + "source=./.vscode,target=/app/.vscode,type=bind" ], "customizations": { diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5a12027 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,58 @@ +{ + "files.associations": { + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "format": "cpp", + "text_encoding": "cpp" + } +} \ No newline at end of file diff --git a/include/entities/AmmoPack.h b/include/entities/AmmoPack.h new file mode 100644 index 0000000..3c631ea --- /dev/null +++ b/include/entities/AmmoPack.h @@ -0,0 +1,43 @@ +#ifndef AMMO_PACK_H +#define AMMO_PACK_H + +#include "raylib.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" + +class AmmoPack { + public: + AmmoPack() { + this->object = TextureWrapper(AssetService::getInstance().getSprite(ESprite::AMMOPACK),{32,32}); + } + void logic(){ + } + + void draw() { + DrawTexture(object.getTexture(), object.getPosition().x, object.getPosition().y, WHITE); + } + + int getAmmoAmount() { + return this->ammoAmount; + } + + void setPickedUp(bool pickedUp) { + this->pickedUp = pickedUp; + } + + bool hasBeenPickedUp() { + return this->pickedUp; + } + + TextureWrapper& getObject() { + return this->object; + } + + private: + TextureWrapper object; + unsigned int ammoAmount = 10; // Amount of ammo replenished by the pack + bool pickedUp; // When the player has picked up the pack +}; + + +#endif \ No newline at end of file diff --git a/include/entities/Background.h b/include/entities/Background.h index dc9ad8b..a5cd0ed 100644 --- a/include/entities/Background.h +++ b/include/entities/Background.h @@ -35,6 +35,12 @@ class Background { DrawTextureRec(texture.getTexture(), texture.getSourceRect(), texture.getPosition(),WHITE); } } + + Vector2 getBackgroundSize() { + TextureWrapper lastbg = this->textures[this->textures.size()-1]; + return {lastbg.getPosition().x + lastbg.getSize().x, lastbg.getPosition().y + lastbg.getSize().y}; + } + private: std::vector textures; // Vector2 position; diff --git a/include/entities/Bullet.h b/include/entities/Bullet.h index d65ff65..2986310 100644 --- a/include/entities/Bullet.h +++ b/include/entities/Bullet.h @@ -31,7 +31,7 @@ public: void draw() { - DrawCircle(position.x, position.y, size, color); + DrawCircleGradient(position.x, position.y, size, color, BLUE); } bool getHasCollided() diff --git a/include/entities/Crate.h b/include/entities/Crate.h index 3f31bce..9e81e63 100644 --- a/include/entities/Crate.h +++ b/include/entities/Crate.h @@ -37,12 +37,7 @@ public: private: TextureWrapper texture; - bool hit; // Whether a PBullet has hit the create + bool hit; // Whether a PBullet has hit the crate }; - - - - - #endif \ No newline at end of file diff --git a/include/entities/Game.h b/include/entities/Game.h index 3c797ab..630b2ea 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -13,6 +13,7 @@ #include "entities/Text.h" #include "entities/Bullet.h" #include "entities/PBullet.h" +#include class Game { @@ -25,7 +26,7 @@ public: wrapper1.setSourceRect({0, 0, wrapper1.getSize().x, wrapper1.getSize().y}); TextureWrapper wrapper2 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME2), {864.0f, (float)GetScreenHeight()}, {wrapper1.getSize().x, 0.0}); wrapper2.setSourceRect({0, 200, wrapper2.getSize().x, 200 + wrapper2.getSize().y}); - TextureWrapper wrapper3 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME3), {1792.0f, (float)GetScreenHeight()}, {wrapper1.getSize().x + wrapper2.getSize().x, 0.0}); + TextureWrapper wrapper3 = TextureWrapper(AssetService::getInstance().getBackground(EBackground::GAME3), {1728.0f, (float)GetScreenHeight()}, {wrapper1.getSize().x + wrapper2.getSize().x, 0.0}); wrapper3.setSourceRect({64, 200, 64 + wrapper3.getSize().x, 200 + wrapper3.getSize().y}); std::vector wrappers; @@ -50,6 +51,7 @@ public: CollectableService::getInstance().initCollectables(); + initHud(); initPauseScreen(); initGameOverScreen(); @@ -88,7 +90,8 @@ public: CollectableService::getInstance().collectableLogic(); this->player.logic(); - if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f)) + if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f) && + this->player.getTexture().getPosition().x + (StateService::getInstance().getScreenSize().x / 2.0f) < background.getBackgroundSize().x) { GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; } @@ -140,18 +143,14 @@ private: std::vector gameOverScreenButtons; int hoveredGameOverScreenButton = 0; - // std::vector playerBullets; + // Font hudJellyFont; + Text hudJellyText; + TextureWrapper hudJellyTexture; - // bool isPaused = false; - // bool isGameOver = false; - - // std::vector enemies - // std::vector crates - // std::vector enemyBullets - // std::vector jellies - // std::vector ammo - // std::vector healthpacks - // Powerup powerup + void initHud() { + hudJellyTexture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY),{32,32},{500,5}); + hudJellyText = Text("0/3",EFont::SANSATION,30,0,RED,{hudJellyTexture.getPosition().x + hudJellyTexture.getSize().x*1.5f, hudJellyTexture.getPosition().y+5}); + } void initPauseScreen() { @@ -283,7 +282,12 @@ private: void drawCrosshair() { - DrawTextureEx(this->crosshair.getTexture(), GetMousePosition(), 0, 1.2f, RED); + DrawTextureEx( + this->crosshair.getTexture(), + {GetMousePosition().x - this->crosshair.getSize().x / 2.0f, GetMousePosition().y - this->crosshair.getSize().y / 2.0f}, + 0, + 1.2f, + RED); // DrawTexturePro( // this->crosshair.getTexture(), // this->crosshair.getSourceRect(), @@ -297,6 +301,7 @@ private: { drawHudHealth(); drawHudBullets(); + drawHudJellies(); } void drawHudBullets() @@ -351,6 +356,19 @@ private: DrawTexture(healthSprite, pos.x, pos.y, WHITE); } } + + void drawHudJellies() + { + std::stringstream stream; + stream.clear(); + stream << CollectableService::getInstance().getAmountOfJelliesFreed(); + stream << "/"; + stream << CollectableService::getInstance().getMaxAmountOfJellies(); + hudJellyText.setText(stream.str()); + + DrawTexture(hudJellyTexture.getTexture(), hudJellyTexture.getPosition().x, hudJellyTexture.getPosition().y, WHITE); + hudJellyText.draw(); + } }; #endif \ No newline at end of file diff --git a/include/entities/HealthPack.h b/include/entities/HealthPack.h new file mode 100644 index 0000000..dad0083 --- /dev/null +++ b/include/entities/HealthPack.h @@ -0,0 +1,43 @@ +#ifndef HEALTH_PACK_H +#define HEALTH_PACK_H + +#include "raylib.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" + +class HealthPack { + public: + HealthPack() { + this->object = TextureWrapper(AssetService::getInstance().getSprite(ESprite::HEALTHPACK),{32,32}); + } + void logic(){ + } + + void draw() { + DrawTexture(object.getTexture(), object.getPosition().x, object.getPosition().y, WHITE); + } + + int getHealAmount() { + return this->healAmount; + } + + void setPickedUp(bool pickedUp) { + this->pickedUp = pickedUp; + } + + bool hasBeenPickedUp() { + return this->pickedUp; + } + + TextureWrapper& getObject() { + return this->object; + } + + private: + TextureWrapper object; + unsigned int healAmount = 2; // Amount of health replenished by the pack + bool pickedUp; // When the player has picked up the pack +}; + + +#endif \ No newline at end of file diff --git a/include/entities/Jelly.h b/include/entities/Jelly.h new file mode 100644 index 0000000..a04ee5b --- /dev/null +++ b/include/entities/Jelly.h @@ -0,0 +1,50 @@ +#ifndef JELLY_H +#define JELLY_H + +#include "raylib.h" +#include "services/MapService.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" + +class Jelly +{ +public: + Jelly(){ + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY),{32,32}); + AssetService::getInstance().playSound(ESound::JELLY_FREED); + } + Jelly(Vector2 startPos) + { + this->startPosition = startPos; + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY),{32,32},startPos); + AssetService::getInstance().playSound(ESound::JELLY_FREED); + } + + void logic() + { + if (texture.getPosition().y >= startPosition.y) + { + velocity.y = -jumpSpeed; + } + else + { + velocity.y += gravity; + } + + texture.move(velocity); + } + + void draw() + { + DrawTexture(texture.getTexture(), texture.getPosition().x, texture.getPosition().y, WHITE); + } + +private: + TextureWrapper texture; + float jumpSpeed = 7.0f; + float gravity = 0.6f; + Vector2 velocity = {0, 0}; + Vector2 startPosition = {0,0}; +}; + +#endif \ No newline at end of file diff --git a/include/entities/PBullet.h b/include/entities/PBullet.h index 4febe31..5dd3377 100644 --- a/include/entities/PBullet.h +++ b/include/entities/PBullet.h @@ -21,8 +21,8 @@ private: /** Check for collisions with creates */ for (auto &crate : CollectableService::getInstance().getCrates()) { - if(this->position.x + this->size > crate.getObject().getPosition().x && this->position.x - this->size < crate.getObject().getPosition().x && - this->position.y + this->size > crate.getObject().getPosition().y && this->position.y - this->size < crate.getObject().getPosition().y ) { + if(this->position.x + this->size > crate.getObject().getPosition().x && this->position.x-this->size < crate.getObject().getPosition().x + crate.getObject().getSize().x && + this->position.y + this->size > crate.getObject().getPosition().y && this->position.y-this->size < crate.getObject().getPosition().y + crate.getObject().getSize().y) { crate.setHit(true); AssetService::getInstance().playSound(ESound::CRATE_BREAK); // AssetService::getInstance().playSound(ESound::JELLY_FREED); diff --git a/include/entities/Player.h b/include/entities/Player.h index 9840767..1a97d9a 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -2,7 +2,6 @@ #define PLAYER_H #include "raylib.h" -#include #include "services/AssetService.h" #include "services/MapService.h" #include "services/InputService.h" @@ -12,6 +11,8 @@ #include "enums/ESound.h" #include "entities/Vector2i.h" #include "entities/PBullet.h" +#include +#include class Player { @@ -19,7 +20,6 @@ public: Player() { this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER), {32, 32}, {200, 200}); - prevFrame = GetTime(); spriteAnimation = {0, Right}; this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_RIGHT), {58, 29}, {0, 0}); @@ -28,6 +28,7 @@ public: this->prevShot = GetTime(); this->health = max_health; } + ~Player() { } @@ -38,12 +39,13 @@ public: shoot(); movement(); outofboundsCheck(); + consumableCollision(); } void draw() { drawGun(); - DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), WHITE); + DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), gainedPowerup ? RED : WHITE); } TextureWrapper &getTexture() @@ -56,15 +58,18 @@ public: this->texture.setPosition(pos); } - int getAmmo() { + int getAmmo() + { return this->ammo; } - uint8_t getHealth() { + uint8_t getHealth() + { return this->health; } - uint8_t getMaxHealth() { + uint8_t getMaxHealth() + { return this->max_health; } @@ -78,7 +83,7 @@ private: float moveSpeed = 5.0f; float gravity = 0.6f; float animationDelay = 0.2f; // In seconds - double prevFrame; + double prevFrame; // Time since the last frame Vector2i spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction enum Direction { @@ -86,19 +91,20 @@ private: Left, Right, Up - }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction + }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction std::vector tiles; // Holds eight points of the player for accurate collision std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground bool crateTopCollision; // Whether the player is standing on top of a crate - uint8_t ammo; // Current ammo of the player - uint8_t max_ammo = 100; // Max ammo of the player - float firingRate= 0.2f; // Delay in seconds - double prevShot; // The last time the player fired a bullet - uint8_t health; // Current health of the player - uint8_t max_health = 10; // Max health of the player + uint8_t ammo; // Current ammo of the player + uint8_t max_ammo = 100; // Max ammo of the player + float firingRate = 0.2f; // Delay in seconds + double prevShot; // The last time the player fired a bullet + uint8_t health; // Current health of the player + uint8_t max_health = 10; // Max health of the player + bool gainedPowerup = false; void applyGravity() { @@ -290,6 +296,56 @@ private: // } } + void consumableCollision() + { + /** Check for collisions with ammo packs */ + for (auto &ammoPack : CollectableService::getInstance().getAmmoPacks()) + { + if (this->texture.getPosition().x + this->texture.getSize().x > ammoPack.getObject().getPosition().x && + this->texture.getPosition().x < ammoPack.getObject().getPosition().x + ammoPack.getObject().getSize().x && + this->texture.getPosition().y + this->texture.getSize().y> ammoPack.getObject().getPosition().y && + this->texture.getPosition().y < ammoPack.getObject().getPosition().y + ammoPack.getObject().getSize().y) + { + ammoPack.setPickedUp(true); + AssetService::getInstance().playSound(ESound::AMMO_PICKUP); + ammo += ammoPack.getAmmoAmount(); + } + } + + /** Check for collisions with health packs */ + for (auto &healthPack : CollectableService::getInstance().getHealthPacks()) + { + if (this->texture.getPosition().x + this->texture.getSize().x > healthPack.getObject().getPosition().x && + this->texture.getPosition().x < healthPack.getObject().getPosition().x + healthPack.getObject().getSize().x && + this->texture.getPosition().y + this->texture.getSize().y> healthPack.getObject().getPosition().y && + this->texture.getPosition().y < healthPack.getObject().getPosition().y + healthPack.getObject().getSize().y) + { + if(health < max_health) { + healthPack.setPickedUp(true); + AssetService::getInstance().playSound(ESound::HEAL_PICKUP); + health = std::max(health+healthPack.getHealAmount(),(int)max_health); + } + } + } + + for (auto &powerup : CollectableService::getInstance().getPowerups()) + { + if (this->texture.getPosition().x + this->texture.getSize().x > powerup.getObject().getPosition().x && + this->texture.getPosition().x < powerup.getObject().getPosition().x + powerup.getObject().getSize().x && + this->texture.getPosition().y + this->texture.getSize().y> powerup.getObject().getPosition().y && + this->texture.getPosition().y < powerup.getObject().getPosition().y + powerup.getObject().getSize().y) + { + // if(health < max_health) { + powerup.setPickedUp(true); + AssetService::getInstance().playSound(ESound::POWERUP); + jumpPower += powerup.getExtraJumpPower(); + gainedPowerup = true; + // health = std::max(health+powerup.getHealAmount(),(int)max_health); + // } + } + } + } + void outofboundsCheck() { if (this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) @@ -305,15 +361,15 @@ private: { if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right) { - float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - this->texture.getPosition().y, - GameService::getInstance().getMouseWorldPos().x - this->texture.getPosition().x); - this->gunRight.setRotation(gunRotationRight * 90); + float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + GameService::getInstance().getMouseWorldPos().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + this->gunRight.setRotation(gunRotationRight * 180 / M_PI); } else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) { - float gunRotationLeft = atan2(GameService::getInstance().getMouseWorldPos().y - this->texture.getPosition().y, - this->texture.getPosition().x - GameService::getInstance().getMouseWorldPos().x); - this->gunLeft.setRotation(-gunRotationLeft * 90); + float gunRotationLeft = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + (this->texture.getPosition().x + this->texture.getSize().x / 2.0) - GameService::getInstance().getMouseWorldPos().x); + this->gunLeft.setRotation(-gunRotationLeft * 180 / M_PI); } } @@ -321,11 +377,13 @@ private: { if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) { + // + this->texture.getSize().x / 2.0f + // + this->texture.getSize().y / 2.0f DrawTexturePro( this->gunRight.getTexture(), {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, - this->texture.getPosition().y + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, this->gunRight.getSize().x, this->gunRight.getSize().y}, {this->gunRight.getSize().x / 2.0f, this->gunRight.getSize().y / 2.0f}, @@ -338,7 +396,7 @@ private: this->gunLeft.getTexture(), {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, - this->texture.getPosition().y + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, {this->gunLeft.getSize().x / 2.0f, this->gunLeft.getSize().y / 2.0f}, @@ -352,9 +410,9 @@ private: if (InputService::getInstance().isMouseButtonDown(MOUSE_BUTTON_LEFT) && GetTime() - prevShot > firingRate && groundCollision) { prevShot = GetTime(); - if (ammo == 0 || (GameService::getInstance().getMouseWorldPos().x > (this->texture.getPosition().x - this->gunLeft.getSize().x/2.0f) && spriteAnimation.y == Left) || - (GameService::getInstance().getMouseWorldPos().x < (this->texture.getPosition().x + this->texture.getSize().x + gunLeft.getSize().x/2.0f) && spriteAnimation.y == Right)) - { + if (ammo == 0 || (GameService::getInstance().getMouseWorldPos().x > (this->texture.getPosition().x - this->gunLeft.getSize().x / 2.0f) && spriteAnimation.y == Left) || + (GameService::getInstance().getMouseWorldPos().x < (this->texture.getPosition().x + this->texture.getSize().x + gunLeft.getSize().x / 2.0f) && spriteAnimation.y == Right)) + { // Play empty gun sound AssetService::getInstance().playSound(ESound::NO_AMMO); return; @@ -363,30 +421,23 @@ private: AssetService::getInstance().playSound(ESound::SHOOT); ammo--; - Vector2 bulletStartLocation = {this->texture.getPosition().x + 10, this->texture.getPosition().y + 10}; Vector2 mouseWorldPos = GameService::getInstance().getMouseWorldPos(); - if (spriteAnimation.y == Left) - { - bulletStartLocation.y = this->texture.getPosition().y + 10; - bulletStartLocation.x = this->texture.getPosition().x - 5; - } - if (spriteAnimation.y == Right) - { - bulletStartLocation.y = this->texture.getPosition().y + 10; - bulletStartLocation.x = this->texture.getPosition().x + 22; - } - if (mouseWorldPos.x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right || mouseWorldPos.x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) { - // PlayGunSound(); - // currentAmmo--; - // .push_back(bulletStartLocation); - float angleShot = atan2(mouseWorldPos.y - bulletStartLocation.y, - mouseWorldPos.x - bulletStartLocation.x); + + float angleShot = atan2(mouseWorldPos.y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + mouseWorldPos.x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + + /** Making the bullet originate from the gun's mouth */ + float offsetDistance = this->gunLeft.getSize().x / 2; + float offsetX = cos(angleShot) * offsetDistance; + float offsetY = sin(angleShot) * offsetDistance; + Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; + GameService::getInstance().addPlayerBullet(PBullet(bulletStartLocation, angleShot)); - // bulletDirections.push_back(angleShot); } } } diff --git a/include/entities/Powerup.h b/include/entities/Powerup.h new file mode 100644 index 0000000..7375531 --- /dev/null +++ b/include/entities/Powerup.h @@ -0,0 +1,58 @@ +#ifndef POWERUP_H +#define POWERUP_H + +#include "raylib.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" + +class Powerup { + public: + Powerup() { + this->object = TextureWrapper(AssetService::getInstance().getSprite(ESprite::POWERUP),{50,55}); + } + void logic(){ + animate(); + } + + void draw() { + DrawTextureRec(object.getTexture(), object.getSourceRect(), object.getPosition(), WHITE); + } + + float getExtraJumpPower() { + return this->extraJumpPower; + } + + void setPickedUp(bool pickedUp) { + this->pickedUp = pickedUp; + } + + bool hasBeenPickedUp() { + return this->pickedUp; + } + + TextureWrapper& getObject() { + return this->object; + } + + private: + TextureWrapper object; + float extraJumpPower = 2.0f; // Jump power gained by grabbing the powerup + bool pickedUp = false; // When the player has picked up the pack + float animationDelay = 0.08f; // How long to wait in between animation frames (in seconds) + double lastFrame; // Time since the last frame + u_int8_t currentFrame = 0; // Current frame of the animation + u_int8_t amountOfFrames = 8; // Total amount of animation frames + + void animate() { + if(GetTime() - lastFrame > animationDelay) { + currentFrame ++; + currentFrame %= amountOfFrames; + + object.setSourceRect({(float)currentFrame * 81 + 16, 10,object.getSize().x, object.getSize().y}); + lastFrame = GetTime(); + } + } +}; + + +#endif \ No newline at end of file diff --git a/include/services/CollectableService.h b/include/services/CollectableService.h index b391848..e5c44a2 100644 --- a/include/services/CollectableService.h +++ b/include/services/CollectableService.h @@ -7,6 +7,10 @@ #include "entities/Vector2i.h" #include "entities/Crate.h" #include "entities/PBullet.h" +#include "entities/Jelly.h" +#include "entities/AmmoPack.h" +#include "entities/HealthPack.h" +#include "entities/Powerup.h" #include class CollectableService : public Service @@ -15,52 +19,77 @@ public: void initCollectables() { initCrates(); + initJellies(); + initAmmoPacks(); + initHealthPacks(); + initPowerups(); } void collectableLogic() { crateLogic(); + jellyLogic(); + ammoPackLogic(); + healthPackLogic(); + powerupLogic(); } void drawCollectables() { drawCrates(); + drawJellies(); + drawAmmoPacks(); + drawHealthpacks(); + drawPowerups(); } - // void checkCratesForCollision(PBullet bullet) - // { - // for (auto it = crates.begin(); it != crates.end();) - // { - // auto &crate = *it; - // // if (*it % 2 == 0) { - // if (crate.bulletCollision(bullet)) - // { - // Vector2 pos = crate.getObject().getPosition(); - // Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); - // MapService::getInstance().getMap().updateColmapTile(tilePos, false); - // it = crates.erase(it); // erase returns the next iterator - // } - // else - // { - // ++it; - // } - // } - // } - - std::vector& getCrates() { + std::vector &getCrates() + { return this->crates; } + std::vector &getAmmoPacks() + { + return this->ammoPacks; + } + + std::vector &getHealthPacks() + { + return this->healthPacks; + } + + std::vector &getPowerups() { + return this->powerUps; + } + + int getMaxAmountOfJellies() + { + return this->amountOfCrates; + } + + int getAmountOfJelliesFreed() + { + return this->jellies.size(); + } + private: + int amountOfCrates = 3; + int amountOfAmmoPacks = 4; + int amountOfHealthPacks = 4; + int amountOfPowerups = 1; + std::vector crates; + std::vector ammoPacks; + std::vector healthPacks; + std::vector jellies; + std::vector powerUps; + void initCrates() { crates.clear(); - Vector2 location; int tileSize = MapService::getInstance().getMap().getTileSize(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < amountOfCrates; i++) { crates.push_back(Crate()); - // crateLocation.push_back(location); } crates[0].getObject().setPosition({38.0f * tileSize, 9.0f * tileSize}); crates[1].getObject().setPosition({41.0f * tileSize, 5.0f * tileSize}); @@ -71,17 +100,50 @@ private: Vector2i crateTilePos = Vector2i(crates[i].getObject().getPosition().x / tileSize, crates[i].getObject().getPosition().y / tileSize); MapService::getInstance().getMap().updateColmapTile(crateTilePos, true); } - // crates[0].y = 9 * tileSize; - - // crateLocation[1].x = 41 * tileSize; - // crateLocation[1].y = 5 * tileSize; - - // crateLocation[2].x = 68 * tileSize; - // crateLocation[2].y = 4 * tileSize; } void initJellies() { + jellies.clear(); + } + + void initAmmoPacks() + { + ammoPacks.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfAmmoPacks; i++) + { + ammoPacks.push_back(AmmoPack()); + } + ammoPacks[0].getObject().setPosition({32.0f * tileSize, 9.0f * tileSize}); + ammoPacks[1].getObject().setPosition({48.0f * tileSize, 5.0f * tileSize}); + ammoPacks[2].getObject().setPosition({88.0f * tileSize, 9.0f * tileSize}); + ammoPacks[3].getObject().setPosition({105.0f * tileSize, 15.0f * tileSize}); + } + + void initHealthPacks() + { + healthPacks.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfHealthPacks; i++) + { + healthPacks.push_back(HealthPack()); + } + healthPacks[0].getObject().setPosition({28.0f * tileSize, 10.0f * tileSize}); + healthPacks[1].getObject().setPosition({64.0f * tileSize, 6.0f * tileSize}); + healthPacks[2].getObject().setPosition({118.0f * tileSize, 11.0f * tileSize}); + healthPacks[3].getObject().setPosition({74.0f * tileSize, 8.0f * tileSize}); + } + + void initPowerups() + { + powerUps.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfPowerups; i++) + { + powerUps.push_back(Powerup()); + } + powerUps[0].getObject().setPosition({41.0f * tileSize+8, 10.0f * tileSize-25}); } void crateLogic() @@ -91,12 +153,16 @@ private: auto &crate = *it; /** * If the crate has been hit: - * 1. Remove the collision tile from the collision map - * + * 1. Create a Jelly on the location of the crate + * 2. Remove the collision tile from the collision map + * 3. Remove the crate */ if (crate.hasBeenHit()) { Vector2 pos = crate.getObject().getPosition(); + + jellies.push_back(Jelly(pos)); + Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); MapService::getInstance().getMap().updateColmapTile(tilePos, false); it = crates.erase(it); @@ -108,35 +174,61 @@ private: } } - // void destroyCrate(int index) { - // Vector2 pos = crates[index].getObject().getPosition(); - // Vector2i tilePos = Vector2i(pos.x/MapService::getInstance().getMap().getTileSize(), pos.y/MapService::getInstance().getMap().getTileSize()); - // MapService::getInstance().getMap().updateColmapTile(tilePos, false); - // crates.erase(crates.begin() + index); - // } - - // void checkCratesForCollision(PBullet bullet) - // { - // for (auto it = crates.begin(); it != crates.end();) - // { - // auto &crate = *it; - // // if (*it % 2 == 0) { - // if (crate.bulletCollision(bullet)) - // { - // Vector2 pos = crate.getObject().getPosition(); - // Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); - // MapService::getInstance().getMap().updateColmapTile(tilePos, false); - // it = crates.erase(it); // erase returns the next iterator - // } - // else - // { - // ++it; - // } - // } - // } - void jellyLogic() { + for (int i = 0; i < jellies.size(); i++) + { + jellies[i].logic(); + } + } + + void ammoPackLogic() + { + for (auto it = ammoPacks.begin(); it != ammoPacks.end();) + { + auto &ammoPack = *it; + if (ammoPack.hasBeenPickedUp()) + { + it = ammoPacks.erase(it); + } + else + { + ++it; + } + } + } + + void healthPackLogic() + { + for (auto it = healthPacks.begin(); it != healthPacks.end();) + { + auto &healthPack = *it; + if (healthPack.hasBeenPickedUp()) + { + it = healthPacks.erase(it); + } + else + { + ++it; + } + } + } + + void powerupLogic() + { + for (auto it = powerUps.begin(); it != powerUps.end();) + { + auto &powerUp = *it; + if (powerUp.hasBeenPickedUp()) + { + it = powerUps.erase(it); + } + else + { + powerUp.logic(); + ++it; + } + } } void drawCrates() @@ -149,36 +241,35 @@ private: void drawJellies() { - } - - void drawHudJellies() - { + for (int i = 0; i < jellies.size(); i++) + { + jellies[i].draw(); + } } void drawAmmoPacks() { + for (int i = 0; i < ammoPacks.size(); i++) + { + ammoPacks[i].draw(); + } } void drawHealthpacks() { + for (int i = 0; i < healthPacks.size(); i++) + { + healthPacks[i].draw(); + } } - // sf::Text jelliesFreed; // Holds the text that shows how many jellies are freed - // sf::Font jelliesFreedFont; // Holds the font used for the text of the display - - // sf::Sprite jelliesFreedSprite; // Holds the sprite of the jelly in the top right corner - - // Vector2i jellyDisplayLocation; // Holds the location where the amount of jellies freed are displayed - std::vector crates; - // std::vector crateSprite; // Holds the sprites of the crates - // std::vector jellySprite; // Holds the sprites of the jellies inside the crates - // std::vector crateLocation; // Holds the locations of the crates/jellies - // std::vector velocity; // Makes sure the jellies jump when the crate is destroyed - // std::vector collision; // Keeps track of whether or not the player has collided with a crate - // std::vector crateDestroyed; // Keeps track of whether or not a crate has been destroyed - - // unsigned int totalAmntCrates; // Holds the total amount of crates - // unsigned int amntCratesDestroyed; // Holds the amount of crates that have been destroyed + void drawPowerups() + { + for (int i = 0; i < powerUps.size(); i++) + { + powerUps[i].draw(); + } + } }; #endif \ No newline at end of file diff --git a/include/services/GameService.h b/include/services/GameService.h index 75e3190..fb00af9 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -52,13 +52,6 @@ public: Vector2 worldPos = {(float)pixelPos.x + camera.target.x - camera.offset.x, (float)pixelPos.y}; return worldPos; } - // void setGame(Game game) { - // this->game = game; - // } - - // Game& getGame() { - // return this->game; - // } void addPlayerBullet(PBullet bullet) { diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index 80e75d6..7b78e07 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -24,8 +24,10 @@ void AssetService::setAssets() SetSoundVolume(sounds[ESound::SHOOT], 0.4f); sounds[ESound::NO_AMMO] = loadSound("Audio/Sounds/outOfAmmo.wav"); sounds[ESound::CRATE_BREAK] = loadSound("Audio/Sounds/crateBreak.wav"); - SetSoundVolume(sounds[ESound::CRATE_BREAK], 1.0f); sounds[ESound::JELLY_FREED] = loadSound("Audio/Sounds/jellyFreed.wav"); + sounds[ESound::HEAL_PICKUP] = loadSound("Audio/Sounds/healmag.wav"); + sounds[ESound::AMMO_PICKUP] = loadSound("Audio/Sounds/shotgunReload.wav"); + sounds[ESound::POWERUP] = loadSound("Audio/Sounds/powerUpSound.wav"); backgrounds[EBackground::TUTORIAL] = loadTexture("Textures/TutorialBackground2.png"); backgrounds[EBackground::GAME1] = loadTexture("Textures/GameBackground_1.png"); From e8c78770b52c2fafe80f43321ef422e5eaea0505 Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Mon, 16 Dec 2024 22:27:06 +0100 Subject: [PATCH 07/12] [Added] Start of enemies --- include/entities/Crate.h | 1 - include/entities/Enemy.h | 223 ++++++++++++++++++++++++++ include/entities/Flag.h | 48 ++++++ include/entities/Game.h | 17 +- include/entities/PBullet.h | 17 ++ include/entities/Player.h | 68 ++++---- include/entities/TextureWrapper.h | 1 - include/enums/ESprite.h | 3 +- include/screens/GameOverScreen.h | 33 ---- include/screens/VictoryScreen.h | 28 ++++ include/services/CollectableService.h | 18 ++- include/services/EnemyService.h | 67 ++++++++ include/services/GameService.h | 19 --- src/screens/MainScreen.cpp | 7 - src/screens/VictoryScreen.cpp | 119 ++++++++++++++ src/services/AssetService.cpp | 4 + src/services/StateService.cpp | 8 +- 17 files changed, 580 insertions(+), 101 deletions(-) create mode 100644 include/entities/Enemy.h create mode 100644 include/entities/Flag.h delete mode 100644 include/screens/GameOverScreen.h create mode 100644 include/screens/VictoryScreen.h create mode 100644 include/services/EnemyService.h create mode 100644 src/screens/VictoryScreen.cpp diff --git a/include/entities/Crate.h b/include/entities/Crate.h index 9e81e63..51d0444 100644 --- a/include/entities/Crate.h +++ b/include/entities/Crate.h @@ -3,7 +3,6 @@ #include "services/AssetService.h" #include "entities/TextureWrapper.h" -#include "entities/PBullet.h" class Crate { public: diff --git a/include/entities/Enemy.h b/include/entities/Enemy.h new file mode 100644 index 0000000..dc5ca45 --- /dev/null +++ b/include/entities/Enemy.h @@ -0,0 +1,223 @@ +#ifndef ENEMY_H +#define ENEMY_H + +#include "raylib.h" +#include "services/MapService.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" +#include + +class Enemy +{ +public: + Enemy() + { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::ENEMY), {32, 32}); + this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::ENEMY_GUN_RIGHT), {97, 27}); + this->gunLeft = TextureWrapper(AssetService::getInstance().getSprite(ESprite::ENEMY_GUN_LEFT), {97, 27}); + prevFrame = GetTime(); + + /** Randomizes between 0 and 1 (+1 to get Left and Right in directions), so they dont all move in the same direction */ + int randDir = (int)((int)(GetTime() * 1000000) % 2); + spriteAnimation = {0, randDir + 1}; + health = maxHealth; + } + Enemy(Vector2 startPos) + { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::ENEMY), {32, 32}, startPos); + prevFrame = GetTime(); + + int randDir = (int)((int)(GetTime() * 1000000) % 2); + spriteAnimation = {0, randDir + 1}; + health = maxHealth; + } + + void logic() + { + if (health == 0) + { + fall(); + } + else + { + walk(); + checkForCollision(); + animate(); + // if (!hasCollided) + // { + // } + } + texture.move(velocity); + } + + void draw() + { + drawGun(); + drawHealthBar(); + DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), WHITE); + } + + TextureWrapper &getObject() + { + return this->texture; + } + + u_int8_t getHealth() + { + return this->health; + } + + void takeDamage() + { + this->health = std::max(health - 1, 0); + } + +private: + TextureWrapper texture; + TextureWrapper gunLeft; + TextureWrapper gunRight; + u_int8_t health; + u_int8_t maxHealth = 5; + Vector2 velocity = {0, 0}; + float spottingRange = 300; // From how far away enemies can spot the player + float gravity = 0.6f; + float moveSpeed = 1.5f; + float animationDelay = 0.2f; // In seconds + double prevFrame; // Time since the last frame + bool hasCollided = false; + Vector2i spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Enemy direction + enum Direction + { + Down, + Left, + Right, + Up + }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with enemy direction + + void walk() + { + if (spriteAnimation.y == Left) + { + velocity.x = -moveSpeed; + } + else + { + velocity.x = moveSpeed; + } + } + + void checkForCollision() + { + std::vector corners = getCorners(); + std::vector> colMap = MapService::getInstance().getMap().getColMap(); + hasCollided = false; + + if (colMap[corners[2].y][corners[2].x] == 0 || colMap[corners[3].y][corners[3].x] == 0) // No ground below enemy + { + hasCollided = true; + } + else if (colMap[corners[0].y][corners[0].x] == 1 || colMap[corners[1].y][corners[1].x] == 1) + { + hasCollided = true; + } + + if (hasCollided) + { + if (spriteAnimation.y == Left) + spriteAnimation.y = Right; + else + spriteAnimation.y = Left; + + velocity.x *= -1; + } + } + + std::vector getCorners() + { + float bottom = texture.getPosition().y + texture.getSize().y + velocity.y; + float left = texture.getPosition().x + velocity.x; + float right = texture.getPosition().x + texture.getSize().x + velocity.x; + float top = texture.getPosition().y + velocity.y; + + int tileSize = MapService::getInstance().getMap().getTileSize(); + // Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); + // Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i leftCenter = Vector2i((int)left / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + // Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); + // Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i rightCenter = Vector2i((int)right / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + + Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side + Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + + std::vector corners; + + corners.push_back(leftCenter); + corners.push_back(rightCenter); + // corners.push_back(leftBottom); + // corners.push_back(rightTop); + // corners.push_back(rightBottom); + + corners.push_back(bottomLeft); + corners.push_back(bottomRight); + return corners; + } + + void animate() + { + if (GetTime() - prevFrame > animationDelay || hasCollided) + { + spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); + + Rectangle sourceRect = this->texture.getSourceRect(); + sourceRect.x = spriteAnimation.x * this->texture.getSize().x; + sourceRect.y = spriteAnimation.y * this->texture.getSize().y; + // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); + this->texture.setSourceRect(sourceRect); + prevFrame = GetTime(); + } + } + + void drawHealthBar() + { + float barLength = (this->texture.getSize().x / maxHealth) * health; + DrawRectangle(this->texture.getPosition().x, this->texture.getPosition().y - 12.0f, barLength, 3, RED); + } + + void drawGun() + { + if (this->spriteAnimation.y == Right) + { + DrawTexturePro( + this->gunRight.getTexture(), + {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, + this->gunRight.getSize().x, + this->gunRight.getSize().y}, + {this->gunRight.getSize().x / 3.0f, this->gunRight.getSize().y / 2.0f}, + this->gunRight.getRotation(), + WHITE); + } + else if (this->spriteAnimation.y == Left) + { + DrawTexturePro( + this->gunLeft.getTexture(), + {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, + this->gunLeft.getSize().x, + this->gunLeft.getSize().y}, + {this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3.0f, this->gunLeft.getSize().y / 2.0f}, + this->gunLeft.getRotation(), + WHITE); + } + } + + void fall() + { + velocity.y += gravity; + } +}; + +#endif \ No newline at end of file diff --git a/include/entities/Flag.h b/include/entities/Flag.h new file mode 100644 index 0000000..fbb4287 --- /dev/null +++ b/include/entities/Flag.h @@ -0,0 +1,48 @@ +#ifndef FLAG_H +#define FLAG_H + +#include "raylib.h" +#include "services/AssetService.h" +#include "services/StateService.h" +#include "entities/TextureWrapper.h" + +class Flag +{ +public: + Flag() + { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::FLAG), {40, 40}); + } + Flag(Vector2 startPos) + { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::FLAG), {40, 40}, startPos); + } + + void logic() + { + if (touched) + { + StateService::getInstance().setScreen(EScreen::VICTORY); + } + } + + void draw() + { + DrawTextureEx(texture.getTexture(), texture.getPosition(), 0.0f, texture.getSize().x/texture.getTexture().width, WHITE); + } + + TextureWrapper &getObject() + { + return this->texture; + } + + void setTouched(bool touched) { + this->touched = touched; + } + +private: + TextureWrapper texture; + bool touched = false; // Whether the player has touched the flag +}; + +#endif \ No newline at end of file diff --git a/include/entities/Game.h b/include/entities/Game.h index 630b2ea..e7a1d54 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -9,6 +9,7 @@ #include "services/InputService.h" #include "services/GameService.h" #include "services/CollectableService.h" +#include "services/EnemyService.h" #include "entities/Player.h" #include "entities/Text.h" #include "entities/Bullet.h" @@ -54,6 +55,7 @@ public: initHud(); initPauseScreen(); initGameOverScreen(); + EnemyService::getInstance().initEnemies(); InputService::getInstance().setKeysToWatch({KEY_A, KEY_W, KEY_S, KEY_D, KEY_ENTER}, {MOUSE_BUTTON_LEFT}); AudioService::getInstance().setMusic(EMusic::GAME); @@ -89,8 +91,10 @@ public: GameService::getInstance().bulletLogic(); CollectableService::getInstance().collectableLogic(); + EnemyService::getInstance().enemyLogic(); + this->player.logic(); - if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f) && + if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f) && this->player.getTexture().getPosition().x + (StateService::getInstance().getScreenSize().x / 2.0f) < background.getBackgroundSize().x) { GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; @@ -112,6 +116,7 @@ public: GameService::getInstance().drawBullets(); this->map.draw(); CollectableService::getInstance().drawCollectables(); + EnemyService::getInstance().drawEnemies(); this->player.draw(); EndMode2D(); @@ -130,11 +135,11 @@ public: } private: - // Camera2D camera; Background background; Map map; Player player; TextureWrapper crosshair; + std::vector enemies; std::vector pauseScreenButtons; int hoveredPauseScreenButton = 0; @@ -143,13 +148,13 @@ private: std::vector gameOverScreenButtons; int hoveredGameOverScreenButton = 0; - // Font hudJellyFont; Text hudJellyText; TextureWrapper hudJellyTexture; - void initHud() { - hudJellyTexture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY),{32,32},{500,5}); - hudJellyText = Text("0/3",EFont::SANSATION,30,0,RED,{hudJellyTexture.getPosition().x + hudJellyTexture.getSize().x*1.5f, hudJellyTexture.getPosition().y+5}); + void initHud() + { + hudJellyTexture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY), {32, 32}, {500, 5}); + hudJellyText = Text("0/3", EFont::SANSATION, 30, 0, RED, {hudJellyTexture.getPosition().x + hudJellyTexture.getSize().x * 1.5f, hudJellyTexture.getPosition().y + 5}); } void initPauseScreen() diff --git a/include/entities/PBullet.h b/include/entities/PBullet.h index 5dd3377..54bd789 100644 --- a/include/entities/PBullet.h +++ b/include/entities/PBullet.h @@ -3,6 +3,7 @@ #include "services/CollectableService.h" #include "services/AssetService.h" +#include "services/EnemyService.h" #include "entities/Bullet.h" #include @@ -29,6 +30,22 @@ private: this->hasCollided = true; } } + + for (auto &enemy : EnemyService::getInstance().getEnemies()) + { + /** If the enemy is already falling of the map, dont allow bullets to hit it anymore */ + if(enemy.getHealth() == 0) + continue; + + if(this->position.x + this->size > enemy.getObject().getPosition().x && this->position.x-this->size < enemy.getObject().getPosition().x + enemy.getObject().getSize().x && + this->position.y + this->size > enemy.getObject().getPosition().y && this->position.y-this->size < enemy.getObject().getPosition().y + enemy.getObject().getSize().y) { + enemy.takeDamage(); + if(enemy.getHealth() == 0) { + AssetService::getInstance().playSound(ESound::ENEMY_DEFEATED); + } + this->hasCollided = true; + } + } } }; diff --git a/include/entities/Player.h b/include/entities/Player.h index 1a97d9a..98ba5c0 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -28,7 +28,7 @@ public: this->prevShot = GetTime(); this->health = max_health; } - + ~Player() { } @@ -82,28 +82,28 @@ private: float jumpPower = 7.2f; float moveSpeed = 5.0f; float gravity = 0.6f; - float animationDelay = 0.2f; // In seconds - double prevFrame; // Time since the last frame - Vector2i spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction + float animationDelay = 0.2f; // In seconds + double prevFrame; // Time since the last frame + Vector2i spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction enum Direction { Down, Left, Right, Up - }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction + }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction std::vector tiles; // Holds eight points of the player for accurate collision std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground bool crateTopCollision; // Whether the player is standing on top of a crate - uint8_t ammo; // Current ammo of the player - uint8_t max_ammo = 100; // Max ammo of the player - float firingRate = 0.2f; // Delay in seconds - double prevShot; // The last time the player fired a bullet - uint8_t health; // Current health of the player - uint8_t max_health = 10; // Max health of the player + uint8_t ammo; // Current ammo of the player + uint8_t max_ammo = 100; // Max ammo of the player + float firingRate = 0.2f; // Delay in seconds + double prevShot; // The last time the player fired a bullet + uint8_t health; // Current health of the player + uint8_t max_health = 10; // Max health of the player bool gainedPowerup = false; void applyGravity() @@ -301,9 +301,9 @@ private: /** Check for collisions with ammo packs */ for (auto &ammoPack : CollectableService::getInstance().getAmmoPacks()) { - if (this->texture.getPosition().x + this->texture.getSize().x > ammoPack.getObject().getPosition().x && - this->texture.getPosition().x < ammoPack.getObject().getPosition().x + ammoPack.getObject().getSize().x && - this->texture.getPosition().y + this->texture.getSize().y> ammoPack.getObject().getPosition().y && + if (this->texture.getPosition().x + this->texture.getSize().x > ammoPack.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> ammoPack.getObject().getPosition().y && this->texture.getPosition().y < ammoPack.getObject().getPosition().y + ammoPack.getObject().getSize().y) { ammoPack.setPickedUp(true); @@ -315,35 +315,47 @@ private: /** Check for collisions with health packs */ for (auto &healthPack : CollectableService::getInstance().getHealthPacks()) { - if (this->texture.getPosition().x + this->texture.getSize().x > healthPack.getObject().getPosition().x && - this->texture.getPosition().x < healthPack.getObject().getPosition().x + healthPack.getObject().getSize().x && - this->texture.getPosition().y + this->texture.getSize().y> healthPack.getObject().getPosition().y && + if (this->texture.getPosition().x + this->texture.getSize().x > healthPack.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> healthPack.getObject().getPosition().y && this->texture.getPosition().y < healthPack.getObject().getPosition().y + healthPack.getObject().getSize().y) { - if(health < max_health) { + if (health < max_health) + { healthPack.setPickedUp(true); AssetService::getInstance().playSound(ESound::HEAL_PICKUP); - health = std::max(health+healthPack.getHealAmount(),(int)max_health); + health = std::max(health + healthPack.getHealAmount(), (int)max_health); } } } - for (auto &powerup : CollectableService::getInstance().getPowerups()) + /** Checks for collisions with powerups */ + for (auto &powerup : CollectableService::getInstance().getPowerups()) { - if (this->texture.getPosition().x + this->texture.getSize().x > powerup.getObject().getPosition().x && - this->texture.getPosition().x < powerup.getObject().getPosition().x + powerup.getObject().getSize().x && - this->texture.getPosition().y + this->texture.getSize().y> powerup.getObject().getPosition().y && + if (this->texture.getPosition().x + this->texture.getSize().x > powerup.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> powerup.getObject().getPosition().y && this->texture.getPosition().y < powerup.getObject().getPosition().y + powerup.getObject().getSize().y) { // if(health < max_health) { - powerup.setPickedUp(true); - AssetService::getInstance().playSound(ESound::POWERUP); - jumpPower += powerup.getExtraJumpPower(); - gainedPowerup = true; - // health = std::max(health+powerup.getHealAmount(),(int)max_health); + powerup.setPickedUp(true); + AssetService::getInstance().playSound(ESound::POWERUP); + jumpPower += powerup.getExtraJumpPower(); + gainedPowerup = true; + // health = std::max(health+powerup.getHealAmount(),(int)max_health); // } } } + + /** Checks for collisions with the finish flag */ + auto &flag = CollectableService::getInstance().getFlag(); + if (this->texture.getPosition().x + this->texture.getSize().x > flag.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> flag.getObject().getPosition().y && + this->texture.getPosition().y < flag.getObject().getPosition().y + flag.getObject().getSize().y) + { + flag.setTouched(true); + } } void outofboundsCheck() diff --git a/include/entities/TextureWrapper.h b/include/entities/TextureWrapper.h index 09cdf88..dd5d928 100644 --- a/include/entities/TextureWrapper.h +++ b/include/entities/TextureWrapper.h @@ -15,7 +15,6 @@ class TextureWrapper { this->sourceRect = sourceRect; } ~TextureWrapper() { - } Texture2D getTexture() { diff --git a/include/enums/ESprite.h b/include/enums/ESprite.h index 932595c..77f4950 100644 --- a/include/enums/ESprite.h +++ b/include/enums/ESprite.h @@ -16,7 +16,8 @@ enum class ESprite { HUD_HEART, HUD_HEART_EMPTY, HUD_AMMO, - CROSSHAIR + CROSSHAIR, + FLAG, }; #endif \ No newline at end of file diff --git a/include/screens/GameOverScreen.h b/include/screens/GameOverScreen.h deleted file mode 100644 index 4e2a222..0000000 --- a/include/screens/GameOverScreen.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef GAME_OVER_SCREEN_H -#define GAME_OVER_SCREEN_H - -#include "screens/Screen.h" -#include "raylib.h" -#include "services/AudioService.h" - -class GameOverScreen : public Screen -{ - -public: - GameOverScreen() - { - AudioService::getInstance().setMusic(EMusic::GAME_OVER); - AudioService::getInstance().playMusic(); - } - - void logic() override - { - } - - void draw() override - { - BeginDrawing(); - // ClearBackground((Color){255,0,0,100}); - DrawRectangle(0, 0, 640, 544, (Color){255,0,0,65}); - EndDrawing(); - } - -private: -}; - -#endif \ No newline at end of file diff --git a/include/screens/VictoryScreen.h b/include/screens/VictoryScreen.h new file mode 100644 index 0000000..a0e71fa --- /dev/null +++ b/include/screens/VictoryScreen.h @@ -0,0 +1,28 @@ +#ifndef VICTORY_SCREEN_H +#define VICTORY_SCREEN_H + +#include "raylib.h" +#include "screens/Screen.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" +#include "entities/Text.h" +#include "entities/Background.h" +#include + +class VictoryScreen : public Screen +{ + +public: + VictoryScreen(); + void logic() override; + void draw() override; + +private: + std::vector text; + std::vector textures; + std::vector buttons; + int hoveredButton = 0; + Background background; +}; + +#endif \ No newline at end of file diff --git a/include/services/CollectableService.h b/include/services/CollectableService.h index e5c44a2..be118ae 100644 --- a/include/services/CollectableService.h +++ b/include/services/CollectableService.h @@ -6,13 +6,16 @@ #include "services/MapService.h" #include "entities/Vector2i.h" #include "entities/Crate.h" -#include "entities/PBullet.h" #include "entities/Jelly.h" #include "entities/AmmoPack.h" #include "entities/HealthPack.h" #include "entities/Powerup.h" +#include "entities/Flag.h" #include +/** + * Service that manages the collectables (jellies), consumables (Ammo, health, powerups), and the finish flag + */ class CollectableService : public Service { public: @@ -23,6 +26,7 @@ public: initAmmoPacks(); initHealthPacks(); initPowerups(); + initFlag(); } void collectableLogic() @@ -32,6 +36,7 @@ public: ammoPackLogic(); healthPackLogic(); powerupLogic(); + flag.logic(); } void drawCollectables() @@ -41,6 +46,7 @@ public: drawAmmoPacks(); drawHealthpacks(); drawPowerups(); + flag.draw(); } std::vector &getCrates() @@ -62,6 +68,10 @@ public: return this->powerUps; } + Flag& getFlag() { + return this->flag; + } + int getMaxAmountOfJellies() { return this->amountOfCrates; @@ -82,6 +92,7 @@ private: std::vector healthPacks; std::vector jellies; std::vector powerUps; + Flag flag; void initCrates() { @@ -146,6 +157,11 @@ private: powerUps[0].getObject().setPosition({41.0f * tileSize+8, 10.0f * tileSize-25}); } + void initFlag() { + int tileSize = MapService::getInstance().getMap().getTileSize(); + this->flag = Flag({118.0f*tileSize-tileSize/2.0f, 3.0f * tileSize-8}); + } + void crateLogic() { for (auto it = crates.begin(); it != crates.end();) diff --git a/include/services/EnemyService.h b/include/services/EnemyService.h new file mode 100644 index 0000000..3a8a1eb --- /dev/null +++ b/include/services/EnemyService.h @@ -0,0 +1,67 @@ +#ifndef ENEMY_SERVICE_H +#define ENEMY_SERVICE_H + +#include "raylib.h" +#include "services/Service.h" +#include "services/AssetService.h" +#include "entities/Enemy.h" +#include + +class EnemyService : public Service +{ +public: + void initEnemies() + { + this->enemies.clear(); + const int AMNT_OF_ENEMIES = 8; + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < AMNT_OF_ENEMIES; i++) + { + enemies.push_back(Enemy()); + } + this->enemies[0].getObject().setPosition({31.0f * tileSize, 15.0f * tileSize}); + this->enemies[1].getObject().setPosition({51.0f * tileSize, 10.0f * tileSize}); + this->enemies[2].getObject().setPosition({56.0f * tileSize, 5.0f * tileSize}); + this->enemies[3].getObject().setPosition({78.0f * tileSize, 10.0f * tileSize}); + this->enemies[4].getObject().setPosition({82.0f * tileSize, 15.0f * tileSize}); + this->enemies[5].getObject().setPosition({88.0f * tileSize, 4.0f * tileSize}); + this->enemies[6].getObject().setPosition({98.0f * tileSize, 11.0f * tileSize}); + this->enemies[7].getObject().setPosition({103.0f * tileSize, 15.0f * tileSize}); + } + + void enemyLogic() + { + for (auto it = enemies.begin(); it != enemies.end();) + { + auto &enemy = *it; + if (enemy.getObject().getPosition().y < 0) + { + it = enemies.erase(it); + } + else + { + enemy.logic(); + ++it; + } + } + } + + void drawEnemies() + { + for (int i = 0; i < enemies.size(); i++) + { + enemies[i].draw(); + } + } + + std::vector &getEnemies() + { + return this->enemies; + } + +protected: +private: + std::vector enemies; +}; + +#endif \ No newline at end of file diff --git a/include/services/GameService.h b/include/services/GameService.h index fb00af9..7c59182 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -58,25 +58,6 @@ public: this->playerBullets.push_back(bullet); } - // std::list& getPlayerBullets() { - // return this->playerBullets; - // } - - // void removeCollidedBullets() - // { - // for (auto it = playerBullets.begin(); it != playerBullets.end(); /* no increment here */) - // { - // if (it->getHasCollided()) - // { - // it = playerBullets.erase(it); // Remove element and get the next valid iterator - // } - // else - // { - // ++it; // Only increment if no removal happens - // } - // } - // } - void bulletLogic() { for (auto it = this->playerBullets.begin(); it != this->playerBullets.end();) diff --git a/src/screens/MainScreen.cpp b/src/screens/MainScreen.cpp index 54c9d18..0eaf9b4 100644 --- a/src/screens/MainScreen.cpp +++ b/src/screens/MainScreen.cpp @@ -40,13 +40,6 @@ MainScreen::MainScreen() void MainScreen::logic() { - // Vector2 mouseD = GetMouseDelta(); - // std::cout << mouseD.y < +#include + +VictoryScreen::VictoryScreen() +{ + AudioService::getInstance().stopMusic(); + AssetService::getInstance().playSound(ESound::VICTORY); + + Text victory = Text("victory", EFont::GTEK, 50, 0, {255, 0, 0, 255}); + victory.setPosition({(float)(GetScreenWidth() - victory.getDimensions().x) / 2.0f, + (float)(GetScreenHeight() - victory.getDimensions().y) / 2.5f}); + + int amntOfJelliesRescued = CollectableService::getInstance().getAmountOfJelliesFreed(); + int maxAmntOfJellies = CollectableService::getInstance().getMaxAmountOfJellies(); + std::string rescuedString; + if (amntOfJelliesRescued == maxAmntOfJellies) + { + rescuedString = "You have rescued all of the jellies!"; + } + else + { + std::stringstream stream; + stream << "You have rescued "; + stream << amntOfJelliesRescued; + stream << " of the "; + stream << maxAmntOfJellies; + stream << " jellies"; + rescuedString = stream.str(); + } + + Text rescued = Text(rescuedString, EFont::SANSATION, 30, 0, WHITE); + rescued.setPosition({(float)(GetScreenWidth() - rescued.getDimensions().x) / 2.0f, + (float)(GetScreenHeight() - rescued.getDimensions().y) / 1.5f}); + + this->text.push_back(victory); + this->text.push_back(rescued); + + + const int TOP_POS = GetScreenHeight()/1.2; + const int X_GAP = 70; + const int AMNT_OF_BUTTONS = 2; + const int TOTAL_SPACE = GetScreenWidth() - X_GAP * 2; + const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; + + for (int i = 0; i < AMNT_OF_BUTTONS; i++) + { + Text txt = Text("Play Again", EFont::SANSATION, 30, 0, WHITE); + buttons.push_back(txt); + } + buttons[0].setColor(RED); + buttons[1].setText("Return to Main Menu"); + buttons[0].setPosition({(float)X_GAP, (float)TOP_POS}); + buttons[1].setPosition({(float)GetScreenWidth() - X_GAP - buttons[1].getDimensions().x, (float)TOP_POS}); + + const int x_gap = 50; + const int available_space = GetScreenWidth() - x_gap * 2; + const int space_per_jelly = available_space / maxAmntOfJellies; + for (int i = 0; i < amntOfJelliesRescued; i++) + { + TextureWrapper wrapper = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY), {32, 32}); + wrapper.setPosition({(float)x_gap + space_per_jelly * i + space_per_jelly / 2.0f - wrapper.getSize().x / 2.0f, GetScreenHeight() / 2.0f}); + textures.push_back(wrapper); + } + + this->background = Background(EBackground::VICTORY); + + InputService::getInstance().setKeysToWatch({KEY_ENTER, KEY_A, KEY_D}, {}); +} + +void VictoryScreen::logic() +{ + if (InputService::getInstance().isKeyPressed(KEY_D) || InputService::getInstance().isKeyPressed(KEY_A)) + { + buttons[hoveredButton].setColor(WHITE); + hoveredButton = ((hoveredButton - (InputService::getInstance().isKeyPressed(KEY_D) ? -1 : 1)) + buttons.size()) % buttons.size(); + buttons[hoveredButton].setColor(RED); + } + + if (InputService::getInstance().isKeyPressed(KEY_ENTER)) + { + std::string selectedButton = buttons[hoveredButton].getText(); + + if (selectedButton == "Play Again") + { + StateService::getInstance().setScreen(EScreen::GAME); + } + else if (selectedButton == "Return to Main Menu") + { + StateService::getInstance().setScreen(EScreen::MAIN); + } + } +} + +void VictoryScreen::draw() +{ + BeginDrawing(); + this->background.draw(); + + for (int t = 0; t < text.size(); t++) + { + text[t].draw(); + } + + for (int t = 0; t < buttons.size(); t++) + { + buttons[t].draw(); + } + + for (int t = 0; t < textures.size(); t++) + { + DrawTexture(textures[t].getTexture(), textures[t].getPosition().x, textures[t].getPosition().y, WHITE); + } + + EndDrawing(); +} diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index 7b78e07..fb25cf3 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -28,6 +28,9 @@ void AssetService::setAssets() sounds[ESound::HEAL_PICKUP] = loadSound("Audio/Sounds/healmag.wav"); sounds[ESound::AMMO_PICKUP] = loadSound("Audio/Sounds/shotgunReload.wav"); sounds[ESound::POWERUP] = loadSound("Audio/Sounds/powerUpSound.wav"); + sounds[ESound::VICTORY] = loadSound("Audio/Sounds/victorySound.wav"); + sounds[ESound::PLAYER_DAMAGE] = loadSound("Audio/Sounds/WaterBalloon.wav"); + sounds[ESound::ENEMY_DEFEATED] = loadSound("Audio/Sounds/pain.wav"); backgrounds[EBackground::TUTORIAL] = loadTexture("Textures/TutorialBackground2.png"); backgrounds[EBackground::GAME1] = loadTexture("Textures/GameBackground_1.png"); @@ -53,6 +56,7 @@ void AssetService::setAssets() sprites[ESprite::HUD_HEART_EMPTY] = loadTexture("Textures/heart_empty.png"); sprites[ESprite::HUD_AMMO] = loadTexture("Textures/bullet_hud.png"); sprites[ESprite::CROSSHAIR] = loadTexture("Textures/crosshair_3.png"); + sprites[ESprite::FLAG] = loadTexture("Textures/flag.png"); } Font AssetService::getFont(EFont font, int fontSize) diff --git a/src/services/StateService.cpp b/src/services/StateService.cpp index 40e3ead..c477d51 100644 --- a/src/services/StateService.cpp +++ b/src/services/StateService.cpp @@ -6,7 +6,7 @@ #include "screens/MainScreen.h" #include "screens/TutorialScreen.h" #include "screens/GameScreen.h" -#include "screens/GameOverScreen.h" +#include "screens/VictoryScreen.h" #include void StateService::setScreen(EScreen state) @@ -25,9 +25,9 @@ void StateService::setScreen(EScreen state) case EScreen::GAME: currentScreen = std::make_unique(); break; - // case EScreen::GAMEOVER: - // currentScreen = std::make_unique(); - // break; + case EScreen::VICTORY: + currentScreen = std::make_unique(); + break; case EScreen::CLOSE: gameRunning = false; break; From 2400b0500a476ac8f91ea1fc643a53fed2e753f1 Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Tue, 17 Dec 2024 20:08:35 +0100 Subject: [PATCH 08/12] [Added] Enemy player detection and shooting, [Moved] Lots of entity code to cpp files to avoid circular dependencies --- .vscode/settings.json | 4 +- include/entities/Background.h | 67 +++--- include/entities/Bullet.h | 31 +-- include/entities/Crate.h | 31 ++- include/entities/EBullet.h | 34 +-- include/entities/Enemy.h | 157 ++----------- include/entities/Game.h | 39 ++- include/entities/Jelly.h | 1 - include/entities/PBullet.h | 34 +-- include/entities/Player.h | 379 ++---------------------------- include/entities/TextureWrapper.h | 21 -- include/enums/ESound.h | 3 +- include/services/BulletService.h | 56 +++++ include/services/GameService.h | 48 +--- include/services/PlayerService.h | 27 +++ src/entities/Bullet.cpp | 19 ++ src/entities/EBullet.cpp | 18 ++ src/entities/Enemy.cpp | 213 +++++++++++++++++ src/entities/PBullet.cpp | 33 +++ src/entities/Player.cpp | 305 ++++++++++++++++++++++++ src/services/AssetService.cpp | 1 + 21 files changed, 820 insertions(+), 701 deletions(-) create mode 100644 include/services/BulletService.h create mode 100644 include/services/PlayerService.h create mode 100644 src/entities/Bullet.cpp create mode 100644 src/entities/EBullet.cpp create mode 100644 src/entities/Enemy.cpp create mode 100644 src/entities/PBullet.cpp create mode 100644 src/entities/Player.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 5a12027..035ee7a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,6 +53,8 @@ "typeinfo": "cpp", "variant": "cpp", "format": "cpp", - "text_encoding": "cpp" + "text_encoding": "cpp", + "ratio": "cpp", + "cinttypes": "cpp" } } \ No newline at end of file diff --git a/include/entities/Background.h b/include/entities/Background.h index a5cd0ed..d8d892a 100644 --- a/include/entities/Background.h +++ b/include/entities/Background.h @@ -8,44 +8,43 @@ #include "services/AssetService.h" #include "services/StateService.h" -class Background { - public: - Background() { - +class Background +{ +public: + Background() + { } - Background(EBackground background){ - // Texture2D texture = AssetService::getInstance().getBackground(background); - TextureWrapper wrapper = TextureWrapper(AssetService::getInstance().getBackground(background), StateService::getInstance().getScreenSize(), {0,0}); - wrapper.setSourceRect({0,0,wrapper.getSize().x, wrapper.getSize().y}); - textures.push_back(wrapper); + Background(EBackground background) + { + TextureWrapper wrapper = TextureWrapper(AssetService::getInstance().getBackground(background), StateService::getInstance().getScreenSize(), {0, 0}); + wrapper.setSourceRect({0, 0, wrapper.getSize().x, wrapper.getSize().y}); + textures.push_back(wrapper); + } + Background(std::vector backgrounds) + { + for (int i = 0; i < backgrounds.size(); i++) + { + this->textures.push_back(backgrounds[i]); } - Background(std::vector backgrounds){ - for(int i = 0; i < backgrounds.size(); i++) { - this->textures.push_back(backgrounds[i]); - } - } - ~Background(){} - // Vector2 getPosition(); - // void updatePosition(Vector2 deltaPos); - // void setPosition(Vector2 position); - void draw(){ - for(int i = 0; i < textures.size(); i++) { - TextureWrapper texture = textures[i]; - // Rectangle sourceRect = {0, 0, texture.getSize().x, texture.getSize().y}; - DrawTextureRec(texture.getTexture(), texture.getSourceRect(), texture.getPosition(),WHITE); - } + } + ~Background() {} + void draw() + { + for (int i = 0; i < textures.size(); i++) + { + TextureWrapper texture = textures[i]; + DrawTextureRec(texture.getTexture(), texture.getSourceRect(), texture.getPosition(), WHITE); } + } - Vector2 getBackgroundSize() { - TextureWrapper lastbg = this->textures[this->textures.size()-1]; - return {lastbg.getPosition().x + lastbg.getSize().x, lastbg.getPosition().y + lastbg.getSize().y}; - } - - private: - std::vector textures; - // Vector2 position; - // Vector2 size; + Vector2 getBackgroundSize() + { + TextureWrapper lastbg = this->textures[this->textures.size() - 1]; + return {lastbg.getPosition().x + lastbg.getSize().x, lastbg.getPosition().y + lastbg.getSize().y}; + } + +private: + std::vector textures; }; - #endif \ No newline at end of file diff --git a/include/entities/Bullet.h b/include/entities/Bullet.h index 2986310..db9f976 100644 --- a/include/entities/Bullet.h +++ b/include/entities/Bullet.h @@ -2,11 +2,9 @@ #define BULLET_H #include "raylib.h" -#include #include "entities/TextureWrapper.h" #include "entities/Vector2i.h" -#include "services/MapService.h" -#include +#include class Bullet { @@ -18,8 +16,6 @@ public: this->direction = direction; } - // ~Bullet() {} - void logic() { move(); @@ -31,7 +27,7 @@ public: void draw() { - DrawCircleGradient(position.x, position.y, size, color, BLUE); + DrawCircleGradient(position.x, position.y, size, color, BLACK); } bool getHasCollided() @@ -46,37 +42,16 @@ protected: bool hasCollided = false; private: - // TextureWrapper object; float direction; float velocity = 15.0f; void move() { - // float directionInRadians = this->direction * M_PI / 180.0f; position.x += velocity * cos(direction); position.y += velocity * sin(direction); } - void envCollision() - { - if (position.x < 0 || position.y < 0 || position.y > GetScreenHeight()) - { - hasCollided = true; - return; - } - - const int tileSize = MapService::getInstance().getMap().getTileSize(); - Vector2i tilePos = Vector2i((int)this->position.x / tileSize, (int)this->position.y / tileSize); - if (MapService::getInstance().getMap().getColMap()[tilePos.y][tilePos.x] == 1) - { - hasCollided = true; - return; - } - } + void envCollision(); virtual void objCollision() = 0; - - // void handleCollision() { - - // } }; #endif \ No newline at end of file diff --git a/include/entities/Crate.h b/include/entities/Crate.h index 51d0444..c9f86d5 100644 --- a/include/entities/Crate.h +++ b/include/entities/Crate.h @@ -4,39 +4,46 @@ #include "services/AssetService.h" #include "entities/TextureWrapper.h" -class Crate { +class Crate +{ public: - Crate() { - this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CRATE),{32,32}); + Crate() + { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CRATE), {32, 32}); } - Crate(Vector2 pos) { - this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CRATE),{32,32}, pos); + Crate(Vector2 pos) + { + this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CRATE), {32, 32}, pos); } - void logic() { - + void logic() + { } - bool hasBeenHit() { + bool hasBeenHit() + { return this->hit; } - void setHit(bool hit) { + void setHit(bool hit) + { this->hit = hit; } - void draw() { + void draw() + { DrawTexture(this->texture.getTexture(), this->texture.getPosition().x, this->texture.getPosition().y, WHITE); } - TextureWrapper& getObject() { + TextureWrapper &getObject() + { return this->texture; } private: TextureWrapper texture; - bool hit; // Whether a PBullet has hit the crate + bool hit; // Whether a PBullet has hit the crate }; #endif \ No newline at end of file diff --git a/include/entities/EBullet.h b/include/entities/EBullet.h index 3118b9b..9d76b9b 100644 --- a/include/entities/EBullet.h +++ b/include/entities/EBullet.h @@ -1,20 +1,22 @@ -// #ifndef EBULLET_H -// #define EBULLET_H +#ifndef EBULLET_H +#define EBULLET_H -// #include "Bullet.h" -// #include +#include "services/AssetService.h" +#include "entities/Bullet.h" +#include -// class EBullet : public Bullet { -// public: +class EBullet : public Bullet +{ +public: + EBullet() {} + EBullet(Vector2 pos, float angle) : Bullet(pos, angle) + { + this->color = RED; + } -// EBullet() {} -// ~EBullet() {} +private: + uint8_t damage = 1; + void objCollision() override; +}; -// void detectCollision() override { -// std::cout << "Enemy bullets" << std::endl; -// } -// private: - -// }; - -// #endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/entities/Enemy.h b/include/entities/Enemy.h index dc5ca45..5d811ba 100644 --- a/include/entities/Enemy.h +++ b/include/entities/Enemy.h @@ -2,9 +2,9 @@ #define ENEMY_H #include "raylib.h" -#include "services/MapService.h" #include "services/AssetService.h" #include "entities/TextureWrapper.h" +#include "entities/Vector2i.h" #include class Enemy @@ -16,6 +16,7 @@ public: this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::ENEMY_GUN_RIGHT), {97, 27}); this->gunLeft = TextureWrapper(AssetService::getInstance().getSprite(ESprite::ENEMY_GUN_LEFT), {97, 27}); prevFrame = GetTime(); + lastShot = GetTime(); /** Randomizes between 0 and 1 (+1 to get Left and Right in directions), so they dont all move in the same direction */ int randDir = (int)((int)(GetTime() * 1000000) % 2); @@ -32,23 +33,7 @@ public: health = maxHealth; } - void logic() - { - if (health == 0) - { - fall(); - } - else - { - walk(); - checkForCollision(); - animate(); - // if (!hasCollided) - // { - // } - } - texture.move(velocity); - } + void logic(); void draw() { @@ -93,131 +78,19 @@ private: Right, Up }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with enemy direction + float shotsPerSecond = 8.0f; + double lastShot; + bool playerSpotted = false; - void walk() - { - if (spriteAnimation.y == Left) - { - velocity.x = -moveSpeed; - } - else - { - velocity.x = moveSpeed; - } - } - - void checkForCollision() - { - std::vector corners = getCorners(); - std::vector> colMap = MapService::getInstance().getMap().getColMap(); - hasCollided = false; - - if (colMap[corners[2].y][corners[2].x] == 0 || colMap[corners[3].y][corners[3].x] == 0) // No ground below enemy - { - hasCollided = true; - } - else if (colMap[corners[0].y][corners[0].x] == 1 || colMap[corners[1].y][corners[1].x] == 1) - { - hasCollided = true; - } - - if (hasCollided) - { - if (spriteAnimation.y == Left) - spriteAnimation.y = Right; - else - spriteAnimation.y = Left; - - velocity.x *= -1; - } - } - - std::vector getCorners() - { - float bottom = texture.getPosition().y + texture.getSize().y + velocity.y; - float left = texture.getPosition().x + velocity.x; - float right = texture.getPosition().x + texture.getSize().x + velocity.x; - float top = texture.getPosition().y + velocity.y; - - int tileSize = MapService::getInstance().getMap().getTileSize(); - // Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); - // Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i leftCenter = Vector2i((int)left / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); - // Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); - // Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i rightCenter = Vector2i((int)right / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); - - Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side - Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side - - std::vector corners; - - corners.push_back(leftCenter); - corners.push_back(rightCenter); - // corners.push_back(leftBottom); - // corners.push_back(rightTop); - // corners.push_back(rightBottom); - - corners.push_back(bottomLeft); - corners.push_back(bottomRight); - return corners; - } - - void animate() - { - if (GetTime() - prevFrame > animationDelay || hasCollided) - { - spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); - - Rectangle sourceRect = this->texture.getSourceRect(); - sourceRect.x = spriteAnimation.x * this->texture.getSize().x; - sourceRect.y = spriteAnimation.y * this->texture.getSize().y; - // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); - this->texture.setSourceRect(sourceRect); - prevFrame = GetTime(); - } - } - - void drawHealthBar() - { - float barLength = (this->texture.getSize().x / maxHealth) * health; - DrawRectangle(this->texture.getPosition().x, this->texture.getPosition().y - 12.0f, barLength, 3, RED); - } - - void drawGun() - { - if (this->spriteAnimation.y == Right) - { - DrawTexturePro( - this->gunRight.getTexture(), - {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, - {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, - this->texture.getPosition().y + this->texture.getSize().y / 2.0f, - this->gunRight.getSize().x, - this->gunRight.getSize().y}, - {this->gunRight.getSize().x / 3.0f, this->gunRight.getSize().y / 2.0f}, - this->gunRight.getRotation(), - WHITE); - } - else if (this->spriteAnimation.y == Left) - { - DrawTexturePro( - this->gunLeft.getTexture(), - {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, - {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, - this->texture.getPosition().y + this->texture.getSize().y / 2.0f, - this->gunLeft.getSize().x, - this->gunLeft.getSize().y}, - {this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3.0f, this->gunLeft.getSize().y / 2.0f}, - this->gunLeft.getRotation(), - WHITE); - } - } - - void fall() - { - velocity.y += gravity; - } + void walk(); + void checkForCollision(); + std::vector getCorners(); + void animate(); + void drawHealthBar(); + void drawGun(); + void fall(); + void detectPlayer(); + void shoot(); }; #endif \ No newline at end of file diff --git a/include/entities/Game.h b/include/entities/Game.h index e7a1d54..df4df1b 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -10,10 +10,9 @@ #include "services/GameService.h" #include "services/CollectableService.h" #include "services/EnemyService.h" -#include "entities/Player.h" +#include "services/PlayerService.h" +#include "services/BulletService.h" #include "entities/Text.h" -#include "entities/Bullet.h" -#include "entities/PBullet.h" #include class Game @@ -44,10 +43,9 @@ public: camera.target = (Vector2){screenSize.x / 2.0f, screenSize.y / 2.0f}; // Initially at the center of the world camera.rotation = 0.0f; camera.zoom = 1.0f; - // this->camera = camera; GameService::getInstance().setCamera(camera); - this->player = Player(); - this->player.setPosition({200, 350}); + BulletService::getInstance().initBullets(); + PlayerService::getInstance().initPlayer(); this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15, 15}, {0, 0}, {0, 0, 15, 15}); CollectableService::getInstance().initCollectables(); @@ -84,20 +82,19 @@ public: if (InputService::getInstance().isKeyPressed(KEY_ENTER)) { GameService::getInstance().setPaused(true); - AudioService::getInstance().pauseMusic(); return; } - GameService::getInstance().bulletLogic(); + BulletService::getInstance().bulletLogic(); CollectableService::getInstance().collectableLogic(); - EnemyService::getInstance().enemyLogic(); - this->player.logic(); - if (this->player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f) && - this->player.getTexture().getPosition().x + (StateService::getInstance().getScreenSize().x / 2.0f) < background.getBackgroundSize().x) + auto& player = PlayerService::getInstance().getPlayer(); + player.logic(); + if (player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f) && + player.getTexture().getPosition().x + (StateService::getInstance().getScreenSize().x / 2.0f) < background.getBackgroundSize().x) { - GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - this->player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; + GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; } } @@ -108,16 +105,11 @@ public: this->background.draw(); - // std::vector pbs = GameService::getInstance().getPlayerBullets(); - // for (int i = 0; i < GameService::getInstance().getPlayerBullets().size(); i++) - // { - // GameService::getInstance().getPlayerBullets()[i].draw(); - // } - GameService::getInstance().drawBullets(); + BulletService::getInstance().drawBullets(); this->map.draw(); CollectableService::getInstance().drawCollectables(); EnemyService::getInstance().drawEnemies(); - this->player.draw(); + PlayerService::getInstance().getPlayer().draw(); EndMode2D(); @@ -137,7 +129,6 @@ public: private: Background background; Map map; - Player player; TextureWrapper crosshair; std::vector enemies; @@ -314,7 +305,7 @@ private: Vector2i pixel_pos = Vector2i(5, 40); Texture2D ammoSprite = AssetService::getInstance().getSprite(ESprite::HUD_AMMO); Vector2i display_pos; - for (int i = 0; i < this->player.getAmmo(); i++) + for (int i = 0; i < PlayerService::getInstance().getPlayer().getAmmo(); i++) { if (i <= 49) { @@ -346,13 +337,13 @@ private: Texture2D noHealthSprite = AssetService::getInstance().getSprite(ESprite::HUD_HEART_EMPTY); Texture2D healthSprite = AssetService::getInstance().getSprite(ESprite::HUD_HEART); // sVector2f coord_pos = GetWindow().mapPixelToCoords(pixel_pos); - for (int i = 0; i < this->player.getMaxHealth(); i++) + for (int i = 0; i < PlayerService::getInstance().getPlayer().getMaxHealth(); i++) { Vector2i pos = Vector2i(pixel_pos.x + noHealthSprite.width * i, pixel_pos.y); // noHealthSprite[i].setPosition(coord_pos.x + noHealthTexture.getSize().x * i, coord_pos.y); DrawTexture(noHealthSprite, pos.x, pos.y, WHITE); } - for (int i = 0; i < this->player.getHealth(); i++) + for (int i = 0; i < PlayerService::getInstance().getPlayer().getHealth(); i++) { // healthSprite[i].setPosition(coord_pos.x + healthTexture.getSize().x * i, coord_pos.y); // DrawTexture(ammoSprite, display_pos.x, display_pos.y, WHITE); diff --git a/include/entities/Jelly.h b/include/entities/Jelly.h index a04ee5b..7d42aa0 100644 --- a/include/entities/Jelly.h +++ b/include/entities/Jelly.h @@ -2,7 +2,6 @@ #define JELLY_H #include "raylib.h" -#include "services/MapService.h" #include "services/AssetService.h" #include "entities/TextureWrapper.h" diff --git a/include/entities/PBullet.h b/include/entities/PBullet.h index 54bd789..4dfa76f 100644 --- a/include/entities/PBullet.h +++ b/include/entities/PBullet.h @@ -1,11 +1,8 @@ #ifndef PBULLET_H #define PBULLET_H -#include "services/CollectableService.h" #include "services/AssetService.h" -#include "services/EnemyService.h" #include "entities/Bullet.h" -#include class PBullet : public Bullet { @@ -17,36 +14,7 @@ public: } private: - void objCollision() override - { - /** Check for collisions with creates */ - for (auto &crate : CollectableService::getInstance().getCrates()) - { - if(this->position.x + this->size > crate.getObject().getPosition().x && this->position.x-this->size < crate.getObject().getPosition().x + crate.getObject().getSize().x && - this->position.y + this->size > crate.getObject().getPosition().y && this->position.y-this->size < crate.getObject().getPosition().y + crate.getObject().getSize().y) { - crate.setHit(true); - AssetService::getInstance().playSound(ESound::CRATE_BREAK); - // AssetService::getInstance().playSound(ESound::JELLY_FREED); - this->hasCollided = true; - } - } - - for (auto &enemy : EnemyService::getInstance().getEnemies()) - { - /** If the enemy is already falling of the map, dont allow bullets to hit it anymore */ - if(enemy.getHealth() == 0) - continue; - - if(this->position.x + this->size > enemy.getObject().getPosition().x && this->position.x-this->size < enemy.getObject().getPosition().x + enemy.getObject().getSize().x && - this->position.y + this->size > enemy.getObject().getPosition().y && this->position.y-this->size < enemy.getObject().getPosition().y + enemy.getObject().getSize().y) { - enemy.takeDamage(); - if(enemy.getHealth() == 0) { - AssetService::getInstance().playSound(ESound::ENEMY_DEFEATED); - } - this->hasCollided = true; - } - } - } + void objCollision() override; }; #endif \ No newline at end of file diff --git a/include/entities/Player.h b/include/entities/Player.h index 98ba5c0..a8c44a5 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -2,17 +2,16 @@ #define PLAYER_H #include "raylib.h" -#include "services/AssetService.h" -#include "services/MapService.h" -#include "services/InputService.h" -#include "services/GameService.h" -#include "entities/TextureWrapper.h" #include "enums/ECorner.h" #include "enums/ESound.h" +#include "services/AssetService.h" +#include "entities/TextureWrapper.h" #include "entities/Vector2i.h" -#include "entities/PBullet.h" #include #include +#include + +class PBullet; class Player { @@ -35,6 +34,11 @@ public: void logic() { + /** Fall out of the map when the jelly has been killed */ + if(this->health == 0) { + fall(); + return; + } rotateGun(); shoot(); movement(); @@ -73,6 +77,10 @@ public: return this->max_health; } + void takeDamage(uint8_t damage) { + this->health = std::max(health - damage, 0); + } + protected: private: TextureWrapper texture; @@ -106,353 +114,18 @@ private: uint8_t max_health = 10; // Max health of the player bool gainedPowerup = false; - void applyGravity() - { - if (!groundCollision) - { - velocity.y += gravity; - } - else - { - // velocity.y = 0.0f; - } - } - - void movement() - { - if (InputService::getInstance().isKeyDown(KEY_A)) - { - spriteAnimation.y = Left; - velocity.x = -moveSpeed; - animate(); - } - else if (InputService::getInstance().isKeyDown(KEY_D)) - { - spriteAnimation.y = Right; - velocity.x = moveSpeed; - animate(); - } - else - { - velocity.x = 0.0f; - } - positionUpdates(); - handleCollision(); - - if (!groundCollision) - { - velocity.y += gravity; - } - else - { - velocity.y = 0.0f; - } - - if (InputService::getInstance().isKeyPressed(KEY_W) && groundCollision) - { - velocity.y -= jumpPower; - } - move(velocity); - } - - void move(Vector2 velocity) - { - this->texture.move(velocity); - } - - void animate() - { - if (GetTime() - prevFrame > animationDelay) - { - spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); - - Rectangle sourceRect = this->texture.getSourceRect(); - sourceRect.x = spriteAnimation.x * this->texture.getSize().x; - sourceRect.y = spriteAnimation.y * this->texture.getSize().y; - // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); - this->texture.setSourceRect(sourceRect); - prevFrame = GetTime(); - } - } - - void positionUpdates() - { - const Vector2 pos = this->texture.getPosition(); - const Vector2 size = this->texture.getSize(); - const int tileSize = MapService::getInstance().getMap().getTileSize(); - - const float bottom = pos.y + size.y + velocity.y; - const float left = pos.x + velocity.x; - const float right = pos.x + size.x + velocity.x; - const float top = pos.y + velocity.y; - - Vector2i topLeft(Vector2i((int)(left + 5) / tileSize, (int)top / tileSize)); // +5 to separate top from side - Vector2i topRight(Vector2i((int)(right - 5) / tileSize, (int)top / tileSize)); // -5 to separate top from side - - Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); - Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); - Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); - - Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side - Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side - - tiles.clear(); - - tiles.push_back(topLeft); - tiles.push_back(topRight); - - tiles.push_back(leftTop); - tiles.push_back(leftBottom); - tiles.push_back(rightTop); - tiles.push_back(rightBottom); - - tiles.push_back(bottomLeft); - tiles.push_back(bottomRight); - } - - void handleCollision() - { - - timesCollision.clear(); - - for (int i = 0; i < tiles.size(); i++) - { - if (MapService::getInstance().getMap().getColMap()[tiles[i].y][tiles[i].x] == 1) - { - timesCollision.push_back(i); - } - } - - groundCollision = false; - - if (timesCollision.size() > 0) - { - for (int i = 0; i < timesCollision.size(); i++) - { - if (timesCollision[i] == 0 || timesCollision[i] == 1) // Topleft and Topright - { - velocity.y = 0; - } - else if (timesCollision[i] == 2 || timesCollision[i] == 3 || // Lefttop and Leftbottom - timesCollision[i] == 4 || timesCollision[i] == 5) // Righttop and Lefttop - { - velocity.x = 0; - } - else if (timesCollision[i] == 6 || timesCollision[i] == 7) // Bottomleft and Bottomright - { - groundCollision = true; - } - } - } - - // Set velocity.x based on going right or left - - // Take current position of all 4 corners - // Apply velocity - // Calculate the 8 points - // Add them to a list - - // Empty vector of amount of collisions - // Loop through the points - // Check the collisionmap for intersection - // If so, add item to the vector of collisions - - // Set groundcollision to false - // Loop through collisions - // Based on their location (TopLeft, RightTop, etc) change the velocity - - // Apply gravity if not on the floor - // If jumping and on the floor, go up - - // Move using velocity - - // Vector2 tempPos = this->texture.getPosition(); - // this->texture.move(velocity); - - // groundCollision = false; - // std::unordered_map collisions = MapService::getInstance().getMap().checkForCollision(this->texture); - - // if (collisions[ECorner::BOTTOM_LEFT] || collisions[ECorner::BOTTOM_RIGHT]) - // { - // velocity.y = 0.0f; - // groundCollision = true; - // this->texture.setPosition({this->texture.getPosition().x, floorf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize()}); - // } - // if (collisions[ECorner::LEFT_TOP] && collisions[ECorner::LEFT_BOTTOM]) - // { - // velocity.x = 0.0f; - // this->texture.setPosition({ceilf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); - // } - // if (collisions[ECorner::TOP_LEFT] || collisions[ECorner::TOP_RIGHT]) - // { - // velocity.y = 0.0f; - // this->texture.setPosition({this->texture.getPosition().x, (ceilf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize())) * MapService::getInstance().getMap().getTileSize()}); - // } - // if (collisions[ECorner::RIGHT_TOP] && collisions[ECorner::RIGHT_BOTTOM]) - // { - // velocity.x = 0.0f; - // this->texture.setPosition({floorf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); - // } - } - - void consumableCollision() - { - /** Check for collisions with ammo packs */ - for (auto &ammoPack : CollectableService::getInstance().getAmmoPacks()) - { - if (this->texture.getPosition().x + this->texture.getSize().x > ammoPack.getObject().getPosition().x && - this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> ammoPack.getObject().getPosition().y && - this->texture.getPosition().y < ammoPack.getObject().getPosition().y + ammoPack.getObject().getSize().y) - { - ammoPack.setPickedUp(true); - AssetService::getInstance().playSound(ESound::AMMO_PICKUP); - ammo += ammoPack.getAmmoAmount(); - } - } - - /** Check for collisions with health packs */ - for (auto &healthPack : CollectableService::getInstance().getHealthPacks()) - { - if (this->texture.getPosition().x + this->texture.getSize().x > healthPack.getObject().getPosition().x && - this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> healthPack.getObject().getPosition().y && - this->texture.getPosition().y < healthPack.getObject().getPosition().y + healthPack.getObject().getSize().y) - { - if (health < max_health) - { - healthPack.setPickedUp(true); - AssetService::getInstance().playSound(ESound::HEAL_PICKUP); - health = std::max(health + healthPack.getHealAmount(), (int)max_health); - } - } - } - - /** Checks for collisions with powerups */ - for (auto &powerup : CollectableService::getInstance().getPowerups()) - { - if (this->texture.getPosition().x + this->texture.getSize().x > powerup.getObject().getPosition().x && - this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> powerup.getObject().getPosition().y && - this->texture.getPosition().y < powerup.getObject().getPosition().y + powerup.getObject().getSize().y) - { - // if(health < max_health) { - powerup.setPickedUp(true); - AssetService::getInstance().playSound(ESound::POWERUP); - jumpPower += powerup.getExtraJumpPower(); - gainedPowerup = true; - // health = std::max(health+powerup.getHealAmount(),(int)max_health); - // } - } - } - - /** Checks for collisions with the finish flag */ - auto &flag = CollectableService::getInstance().getFlag(); - if (this->texture.getPosition().x + this->texture.getSize().x > flag.getObject().getPosition().x && - this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> flag.getObject().getPosition().y && - this->texture.getPosition().y < flag.getObject().getPosition().y + flag.getObject().getSize().y) - { - flag.setTouched(true); - } - } - - void outofboundsCheck() - { - if (this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) - { - this->health = 0; - GameService::getInstance().setGameOver(true); - AudioService::getInstance().setMusic(EMusic::GAME_OVER); - AudioService::getInstance().playMusic(); - } - } - - void rotateGun() - { - if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right) - { - float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), - GameService::getInstance().getMouseWorldPos().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); - this->gunRight.setRotation(gunRotationRight * 180 / M_PI); - } - else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) - { - float gunRotationLeft = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), - (this->texture.getPosition().x + this->texture.getSize().x / 2.0) - GameService::getInstance().getMouseWorldPos().x); - this->gunLeft.setRotation(-gunRotationLeft * 180 / M_PI); - } - } - - void drawGun() - { - if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) - { - // + this->texture.getSize().x / 2.0f - // + this->texture.getSize().y / 2.0f - DrawTexturePro( - this->gunRight.getTexture(), - {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, - {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, - this->texture.getPosition().y + this->texture.getSize().y / 2.0f, - this->gunRight.getSize().x, - this->gunRight.getSize().y}, - {this->gunRight.getSize().x / 2.0f, this->gunRight.getSize().y / 2.0f}, - this->gunRight.getRotation(), - WHITE); - } - else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) - { - DrawTexturePro( - this->gunLeft.getTexture(), - {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, - {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, - this->texture.getPosition().y + this->texture.getSize().y / 2.0f, - this->gunLeft.getSize().x, - this->gunLeft.getSize().y}, - {this->gunLeft.getSize().x / 2.0f, this->gunLeft.getSize().y / 2.0f}, - this->gunLeft.getRotation(), - WHITE); - } - } - - void shoot() - { - if (InputService::getInstance().isMouseButtonDown(MOUSE_BUTTON_LEFT) && GetTime() - prevShot > firingRate && groundCollision) - { - prevShot = GetTime(); - if (ammo == 0 || (GameService::getInstance().getMouseWorldPos().x > (this->texture.getPosition().x - this->gunLeft.getSize().x / 2.0f) && spriteAnimation.y == Left) || - (GameService::getInstance().getMouseWorldPos().x < (this->texture.getPosition().x + this->texture.getSize().x + gunLeft.getSize().x / 2.0f) && spriteAnimation.y == Right)) - { - // Play empty gun sound - AssetService::getInstance().playSound(ESound::NO_AMMO); - return; - } - // Play shooting sound - AssetService::getInstance().playSound(ESound::SHOOT); - ammo--; - - Vector2 mouseWorldPos = GameService::getInstance().getMouseWorldPos(); - - if (mouseWorldPos.x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right || - mouseWorldPos.x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) - { - - float angleShot = atan2(mouseWorldPos.y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), - mouseWorldPos.x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); - - /** Making the bullet originate from the gun's mouth */ - float offsetDistance = this->gunLeft.getSize().x / 2; - float offsetX = cos(angleShot) * offsetDistance; - float offsetY = sin(angleShot) * offsetDistance; - Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, - this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; - - GameService::getInstance().addPlayerBullet(PBullet(bulletStartLocation, angleShot)); - } - } - } + void applyGravity(); + void movement(); + void move(Vector2 velocity); + void animate(); + void positionUpdates(); + void handleCollision(); + void consumableCollision(); + void outofboundsCheck(); + void rotateGun(); + void drawGun(); + void shoot(); + void fall(); // Lets the player fall out of the map when he dies }; #endif diff --git a/include/entities/TextureWrapper.h b/include/entities/TextureWrapper.h index dd5d928..129858c 100644 --- a/include/entities/TextureWrapper.h +++ b/include/entities/TextureWrapper.h @@ -3,7 +3,6 @@ #include "raylib.h" #include "enums/ECorner.h" -#include class TextureWrapper { public: @@ -53,26 +52,6 @@ class TextureWrapper { this->position = {this->position.x + deltaPos.x, this->position.y + deltaPos.y}; } - /** Returns the 4 corners of the texture */ - // std::unordered_map getCorners() { - // std::unordered_map corners; - // float top = this->position.y; - // float right = this->position.x+this->size.x; - // float bottom = this->position.y + this->size.y; - // float left = this->position.x; - // float margin = 8; // Space to the corner - // corners[ECorner::TOP_LEFT] = {left+margin, top}; - // corners[ECorner::TOP_RIGHT] = {right-margin, top}; - // corners[ECorner::RIGHT_TOP] = {right, top+margin}; - // corners[ECorner::RIGHT_BOTTOM] = {right, bottom-margin}; - // corners[ECorner::BOTTOM_RIGHT] = {right-margin, bottom}; - // corners[ECorner::BOTTOM_LEFT] = {left+margin, bottom}; - // corners[ECorner::LEFT_BOTTOM] = {left, bottom-margin}; - // corners[ECorner::LEFT_TOP] = {left, top+margin}; - - // return corners; - // } - private: Texture2D texture; Rectangle sourceRect; diff --git a/include/enums/ESound.h b/include/enums/ESound.h index 0f0aa6a..d70f1a4 100644 --- a/include/enums/ESound.h +++ b/include/enums/ESound.h @@ -11,7 +11,8 @@ enum class ESound { POWERUP, PLAYER_DAMAGE, ENEMY_DEFEATED, - VICTORY + VICTORY, + PLAYER_SPOTTED }; #endif \ No newline at end of file diff --git a/include/services/BulletService.h b/include/services/BulletService.h new file mode 100644 index 0000000..4751191 --- /dev/null +++ b/include/services/BulletService.h @@ -0,0 +1,56 @@ +#ifndef BULLET_SERVICE_H +#define BULLET_SERVICE_H + +#include "raylib.h" +#include "services/Service.h" +#include "services/AssetService.h" +#include "entities/PBullet.h" +#include "entities/EBullet.h" +#include +#include + +class BulletService : public Service +{ +public: + void initBullets() + { + this->bullets.clear(); + } + + void bulletLogic() + { + for (auto it = this->bullets.begin(); it != this->bullets.end();) + { + (*it)->logic(); + if ((*it)->getHasCollided()) + it = this->bullets.erase(it); + else + ++it; + } + } + + void drawBullets() + { + for (auto it = this->bullets.begin(); it != this->bullets.end();) + { + (*it)->draw(); + ++it; + } + } + + void addPlayerBullet(Vector2 position, float direction) + { + this->bullets.push_back(std::make_unique(position, direction)); + } + + void addEnemyBullet(Vector2 position, float direction) + { + this->bullets.push_back(std::make_unique(position, direction)); + } + +protected: +private: + std::vector> bullets; +}; + +#endif \ No newline at end of file diff --git a/include/services/GameService.h b/include/services/GameService.h index 7c59182..81f3cf7 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -1,9 +1,9 @@ #ifndef GAME_SERVICE_H #define GAME_SERVICE_H +#include "raylib.h" #include "services/Service.h" -#include "entities/Game.h" -#include "entities/PBullet.h" +#include "services/AudioService.h" #include #include @@ -23,12 +23,20 @@ public: void setPaused(bool paused) { this->paused = paused; + if (this->paused) + AudioService::getInstance().pauseMusic(); } void setGameOver(bool gameOver) { this->gameOver = gameOver; + if (this->gameOver) + { + AudioService::getInstance().setMusic(EMusic::GAME_OVER); + AudioService::getInstance().playMusic(); + } } + void setCamera(Camera2D camera) { this->camera = camera; @@ -39,44 +47,16 @@ public: return this->camera; } - /** - * Returns the position of the mouse in world coordinates - */ Vector2 getMouseWorldPos() { Vector2 mouseWorldPos = {GetMousePosition().x + camera.target.x - camera.offset.x, GetMousePosition().y}; return mouseWorldPos; } - Vector2 toWorldPos(Vector2i pixelPos) { - Vector2 worldPos = {(float)pixelPos.x + camera.target.x - camera.offset.x, (float)pixelPos.y}; - return worldPos; - } - - void addPlayerBullet(PBullet bullet) + Vector2 toWorldPos(Vector2i pixelPos) { - this->playerBullets.push_back(bullet); - } - - void bulletLogic() - { - for (auto it = this->playerBullets.begin(); it != this->playerBullets.end();) - { - it->logic(); - if (it->getHasCollided()) - it = this->playerBullets.erase(it); - else - ++it; - } - } - - void drawBullets() - { - for (auto it = this->playerBullets.begin(); it != this->playerBullets.end();) - { - it->draw(); - ++it; - } + Vector2 worldPos = {(float)pixelPos.x + camera.target.x - camera.offset.x, (float)pixelPos.y}; + return worldPos; } void resetGame() @@ -90,8 +70,6 @@ private: bool paused = false; bool gameOver = false; Camera2D camera; - - std::list playerBullets; }; #endif \ No newline at end of file diff --git a/include/services/PlayerService.h b/include/services/PlayerService.h new file mode 100644 index 0000000..c0b4eca --- /dev/null +++ b/include/services/PlayerService.h @@ -0,0 +1,27 @@ +#ifndef PLAYER_SERVICE_H +#define PLAYER_SERVICE_H + +#include "raylib.h" +#include "services/Service.h" +#include "entities/Player.h" +#include + +class PlayerService : public Service +{ +public: + void initPlayer() + { + this->player = std::make_unique(); + this->player->setPosition({200, 350}); + } + + Player& getPlayer() { + return *this->player; + } + +protected: +private: + std::unique_ptr player; +}; + +#endif \ No newline at end of file diff --git a/src/entities/Bullet.cpp b/src/entities/Bullet.cpp new file mode 100644 index 0000000..f60b9f9 --- /dev/null +++ b/src/entities/Bullet.cpp @@ -0,0 +1,19 @@ +#include "entities/Bullet.h" +#include "services/MapService.h" + +void Bullet::envCollision() + { + if (position.x < 0 || position.y < 0 || position.y > GetScreenHeight()) + { + hasCollided = true; + return; + } + + const int tileSize = MapService::getInstance().getMap().getTileSize(); + Vector2i tilePos = Vector2i((int)this->position.x / tileSize, (int)this->position.y / tileSize); + if (MapService::getInstance().getMap().getColMap()[tilePos.y][tilePos.x] == 1) + { + hasCollided = true; + return; + } + } \ No newline at end of file diff --git a/src/entities/EBullet.cpp b/src/entities/EBullet.cpp new file mode 100644 index 0000000..cd8f468 --- /dev/null +++ b/src/entities/EBullet.cpp @@ -0,0 +1,18 @@ +#include "entities/EBullet.h" +#include "services/PlayerService.h" + +void EBullet::objCollision() + { + Player& player = PlayerService::getInstance().getPlayer(); + + /** If the player is already falling of the map, dont allow bullets to hit it anymore */ + if(player.getHealth() == 0) + return; + + if(this->position.x + this->size > player.getTexture().getPosition().x && this->position.x-this->size < player.getTexture().getPosition().x + player.getTexture().getSize().x && + this->position.y + this->size > player.getTexture().getPosition().y && this->position.y-this->size < player.getTexture().getPosition().y + player.getTexture().getSize().y) { + player.takeDamage(this->damage); + AssetService::getInstance().playSound(ESound::PLAYER_DAMAGE); + this->hasCollided = true; + } + } \ No newline at end of file diff --git a/src/entities/Enemy.cpp b/src/entities/Enemy.cpp new file mode 100644 index 0000000..cffc960 --- /dev/null +++ b/src/entities/Enemy.cpp @@ -0,0 +1,213 @@ +#include "entities/Enemy.h" +#include "services/PlayerService.h" +#include "services/MapService.h" +#include "services/BulletService.h" + +void Enemy::logic() +{ + if (health == 0) + { + fall(); + } + else + { + detectPlayer(); + if (playerSpotted) + { + shoot(); + } + else + { + walk(); + checkForCollision(); + animate(); + } + } + texture.move(velocity); +} + +void Enemy::walk() +{ + if (spriteAnimation.y == Left) + { + velocity.x = -moveSpeed; + } + else + { + velocity.x = moveSpeed; + } +} + +void Enemy::checkForCollision() +{ + std::vector corners = getCorners(); + std::vector> colMap = MapService::getInstance().getMap().getColMap(); + hasCollided = false; + + if (colMap[corners[2].y][corners[2].x] == 0 || colMap[corners[3].y][corners[3].x] == 0) // No ground below enemy + { + hasCollided = true; + } + else if (colMap[corners[0].y][corners[0].x] == 1 || colMap[corners[1].y][corners[1].x] == 1) + { + hasCollided = true; + } + + if (hasCollided) + { + if (spriteAnimation.y == Left) + spriteAnimation.y = Right; + else + spriteAnimation.y = Left; + + velocity.x *= -1; + } +} + +std::vector Enemy::getCorners() +{ + float bottom = texture.getPosition().y + texture.getSize().y + velocity.y; + float left = texture.getPosition().x + velocity.x; + float right = texture.getPosition().x + texture.getSize().x + velocity.x; + float top = texture.getPosition().y + velocity.y; + + int tileSize = MapService::getInstance().getMap().getTileSize(); + // Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); + // Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i leftCenter = Vector2i((int)left / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + // Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); + // Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i rightCenter = Vector2i((int)right / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + + Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side + Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + + std::vector corners; + + corners.push_back(leftCenter); + corners.push_back(rightCenter); + // corners.push_back(leftBottom); + // corners.push_back(rightTop); + // corners.push_back(rightBottom); + + corners.push_back(bottomLeft); + corners.push_back(bottomRight); + return corners; +} + +void Enemy::animate() +{ + if (GetTime() - prevFrame > animationDelay || hasCollided) + { + spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); + + Rectangle sourceRect = this->texture.getSourceRect(); + sourceRect.x = spriteAnimation.x * this->texture.getSize().x; + sourceRect.y = spriteAnimation.y * this->texture.getSize().y; + // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); + this->texture.setSourceRect(sourceRect); + prevFrame = GetTime(); + } +} + +void Enemy::drawHealthBar() +{ + float barLength = (this->texture.getSize().x / maxHealth) * health; + DrawRectangle(this->texture.getPosition().x, this->texture.getPosition().y - 12.0f, barLength, 3, RED); +} + +void Enemy::drawGun() +{ + if (this->spriteAnimation.y == Right) + { + DrawTexturePro( + this->gunRight.getTexture(), + {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, + this->gunRight.getSize().x, + this->gunRight.getSize().y}, + {this->gunRight.getSize().x / 3.0f, this->gunRight.getSize().y / 2.0f}, + this->gunRight.getRotation(), + WHITE); + } + else if (this->spriteAnimation.y == Left) + { + DrawTexturePro( + this->gunLeft.getTexture(), + {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, + this->gunLeft.getSize().x, + this->gunLeft.getSize().y}, + {this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3.0f, this->gunLeft.getSize().y / 2.0f}, + this->gunLeft.getRotation(), + WHITE); + } +} + +void Enemy::fall() +{ + velocity.y += gravity; +} + +void Enemy::detectPlayer() +{ + // playerSpotted = false; + Player &player = PlayerService::getInstance().getPlayer(); + if (this->health > 0) + { + bool spotted = false; + if (this->spriteAnimation.y == Left && this->texture.getPosition().x - player.getTexture().getPosition().x <= spottingRange && + this->texture.getPosition().x - player.getTexture().getPosition().x > 55 && this->texture.getPosition().y + this->texture.getSize().y >= player.getTexture().getPosition().y) + { + spotted = true; + } + else if (spriteAnimation.y == Right && player.getTexture().getPosition().x - this->texture.getPosition().x <= spottingRange && + player.getTexture().getPosition().x - this->texture.getPosition().x > 55 && this->texture.getPosition().y + this->texture.getSize().y >= player.getTexture().getPosition().y) + { + spotted = true; + } + + /** Don't set it again if the detection continues */ + if (spotted && !this->playerSpotted) + { + AssetService::getInstance().playSound(ESound::PLAYER_SPOTTED); + this->playerSpotted = true; + velocity.x = 0.0f; + } + else if (!spotted) + { + this->playerSpotted = false; + } + } +} + +void Enemy::shoot() +{ + Player &player = PlayerService::getInstance().getPlayer(); + /** Stop shooting if the player is dead (and falling out of the map) */ + if (player.getHealth() > 0 && playerSpotted && GetTime() - this->lastShot >= (1 / this->shotsPerSecond)) + { + this->lastShot = GetTime(); + // Play shooting sound + AssetService::getInstance().playSound(ESound::SHOOT); + + if ((player.getTexture().getPosition().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) || + (player.getTexture().getPosition().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left)) + { + + float angleShot = atan2(player.getTexture().getPosition().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + player.getTexture().getPosition().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + + /** Making the bullet originate from the gun's mouth */ + float offsetDistance = this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3; + float offsetX = cos(angleShot) * offsetDistance; + float offsetY = sin(angleShot) * offsetDistance; + Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; + + BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot); + } + } +} \ No newline at end of file diff --git a/src/entities/PBullet.cpp b/src/entities/PBullet.cpp new file mode 100644 index 0000000..936d370 --- /dev/null +++ b/src/entities/PBullet.cpp @@ -0,0 +1,33 @@ +#include "entities/PBullet.h" +#include "services/CollectableService.h" +#include "services/EnemyService.h" + +void PBullet::objCollision() + { + /** Check for collisions with creates */ + for (auto &crate : CollectableService::getInstance().getCrates()) + { + if(this->position.x + this->size > crate.getObject().getPosition().x && this->position.x-this->size < crate.getObject().getPosition().x + crate.getObject().getSize().x && + this->position.y + this->size > crate.getObject().getPosition().y && this->position.y-this->size < crate.getObject().getPosition().y + crate.getObject().getSize().y) { + crate.setHit(true); + AssetService::getInstance().playSound(ESound::CRATE_BREAK); + this->hasCollided = true; + } + } + + for (auto &enemy : EnemyService::getInstance().getEnemies()) + { + /** If the enemy is already falling of the map, dont allow bullets to hit it anymore */ + if(enemy.getHealth() == 0) + continue; + + if(this->position.x + this->size > enemy.getObject().getPosition().x && this->position.x-this->size < enemy.getObject().getPosition().x + enemy.getObject().getSize().x && + this->position.y + this->size > enemy.getObject().getPosition().y && this->position.y-this->size < enemy.getObject().getPosition().y + enemy.getObject().getSize().y) { + enemy.takeDamage(); + if(enemy.getHealth() == 0) { + AssetService::getInstance().playSound(ESound::ENEMY_DEFEATED); + } + this->hasCollided = true; + } + } + } \ No newline at end of file diff --git a/src/entities/Player.cpp b/src/entities/Player.cpp new file mode 100644 index 0000000..81df783 --- /dev/null +++ b/src/entities/Player.cpp @@ -0,0 +1,305 @@ +#include "entities/Player.h" +#include "services/BulletService.h" +#include "services/GameService.h" +#include "services/CollectableService.h" +#include "services/InputService.h" + +void Player::applyGravity() +{ + if (!groundCollision) + { + velocity.y += gravity; + } + else + { + // velocity.y = 0.0f; + } +} + +void Player::movement() +{ + if (InputService::getInstance().isKeyDown(KEY_A)) + { + spriteAnimation.y = Left; + velocity.x = -moveSpeed; + animate(); + } + else if (InputService::getInstance().isKeyDown(KEY_D)) + { + spriteAnimation.y = Right; + velocity.x = moveSpeed; + animate(); + } + else + { + velocity.x = 0.0f; + } + positionUpdates(); + handleCollision(); + + if (!groundCollision) + { + velocity.y += gravity; + } + else + { + velocity.y = 0.0f; + } + + if (InputService::getInstance().isKeyPressed(KEY_W) && groundCollision) + { + velocity.y -= jumpPower; + } + move(velocity); +} + +void Player::move(Vector2 velocity) +{ + this->texture.move(velocity); +} + +void Player::animate() +{ + if (GetTime() - prevFrame > animationDelay) + { + spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); + + Rectangle sourceRect = this->texture.getSourceRect(); + sourceRect.x = spriteAnimation.x * this->texture.getSize().x; + sourceRect.y = spriteAnimation.y * this->texture.getSize().y; + // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); + this->texture.setSourceRect(sourceRect); + prevFrame = GetTime(); + } +} + +void Player::positionUpdates() +{ + const Vector2 pos = this->texture.getPosition(); + const Vector2 size = this->texture.getSize(); + const int tileSize = MapService::getInstance().getMap().getTileSize(); + + const float bottom = pos.y + size.y + velocity.y; + const float left = pos.x + velocity.x; + const float right = pos.x + size.x + velocity.x; + const float top = pos.y + velocity.y; + + Vector2i topLeft(Vector2i((int)(left + 5) / tileSize, (int)top / tileSize)); // +5 to separate top from side + Vector2i topRight(Vector2i((int)(right - 5) / tileSize, (int)top / tileSize)); // -5 to separate top from side + + Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); + Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); + Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); + Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); + + Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side + Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + + tiles.clear(); + + tiles.push_back(topLeft); + tiles.push_back(topRight); + + tiles.push_back(leftTop); + tiles.push_back(leftBottom); + tiles.push_back(rightTop); + tiles.push_back(rightBottom); + + tiles.push_back(bottomLeft); + tiles.push_back(bottomRight); +} + +void Player::handleCollision() +{ + + timesCollision.clear(); + + for (int i = 0; i < tiles.size(); i++) + { + if (MapService::getInstance().getMap().getColMap()[tiles[i].y][tiles[i].x] == 1) + { + timesCollision.push_back(i); + } + } + + groundCollision = false; + + if (timesCollision.size() > 0) + { + for (int i = 0; i < timesCollision.size(); i++) + { + if (timesCollision[i] == 0 || timesCollision[i] == 1) // Topleft and Topright + { + velocity.y = 0; + } + else if (timesCollision[i] == 2 || timesCollision[i] == 3 || // Lefttop and Leftbottom + timesCollision[i] == 4 || timesCollision[i] == 5) // Righttop and Lefttop + { + velocity.x = 0; + } + else if (timesCollision[i] == 6 || timesCollision[i] == 7) // Bottomleft and Bottomright + { + groundCollision = true; + } + } + } +} + +void Player::consumableCollision() +{ + /** Check for collisions with ammo packs */ + for (auto &ammoPack : CollectableService::getInstance().getAmmoPacks()) + { + if (this->texture.getPosition().x + this->texture.getSize().x > ammoPack.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> ammoPack.getObject().getPosition().y && + this->texture.getPosition().y < ammoPack.getObject().getPosition().y + ammoPack.getObject().getSize().y) + { + ammoPack.setPickedUp(true); + AssetService::getInstance().playSound(ESound::AMMO_PICKUP); + ammo += ammoPack.getAmmoAmount(); + } + } + + /** Check for collisions with health packs */ + for (auto &healthPack : CollectableService::getInstance().getHealthPacks()) + { + if (this->texture.getPosition().x + this->texture.getSize().x > healthPack.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> healthPack.getObject().getPosition().y && + this->texture.getPosition().y < healthPack.getObject().getPosition().y + healthPack.getObject().getSize().y) + { + if (health < max_health) + { + healthPack.setPickedUp(true); + AssetService::getInstance().playSound(ESound::HEAL_PICKUP); + health = std::max(health + healthPack.getHealAmount(), (int)max_health); + } + } + } + + /** Checks for collisions with powerups */ + for (auto &powerup : CollectableService::getInstance().getPowerups()) + { + if (this->texture.getPosition().x + this->texture.getSize().x > powerup.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> powerup.getObject().getPosition().y && + this->texture.getPosition().y < powerup.getObject().getPosition().y + powerup.getObject().getSize().y) + { + // if(health < max_health) { + powerup.setPickedUp(true); + AssetService::getInstance().playSound(ESound::POWERUP); + jumpPower += powerup.getExtraJumpPower(); + gainedPowerup = true; + // health = std::max(health+powerup.getHealAmount(),(int)max_health); + // } + } + } + + /** Checks for collisions with the finish flag */ + auto &flag = CollectableService::getInstance().getFlag(); + if (this->texture.getPosition().x + this->texture.getSize().x > flag.getObject().getPosition().x && + this->texture.getPosition().xtexture.getPosition().y + this->texture.getSize().y> flag.getObject().getPosition().y && + this->texture.getPosition().y < flag.getObject().getPosition().y + flag.getObject().getSize().y) + { + flag.setTouched(true); + } +} + +void Player::shoot() +{ + if (InputService::getInstance().isMouseButtonDown(MOUSE_BUTTON_LEFT) && GetTime() - prevShot > firingRate && groundCollision) + { + prevShot = GetTime(); + if (this->ammo == 0 || (GameService::getInstance().getMouseWorldPos().x > (this->texture.getPosition().x - this->gunLeft.getSize().x / 2.0f) && spriteAnimation.y == Left) || + (GameService::getInstance().getMouseWorldPos().x < (this->texture.getPosition().x + this->texture.getSize().x + gunLeft.getSize().x / 2.0f) && spriteAnimation.y == Right)) + { + // Play empty gun sound + AssetService::getInstance().playSound(ESound::NO_AMMO); + return; + } + // Play shooting sound + AssetService::getInstance().playSound(ESound::SHOOT); + ammo--; + + Vector2 mouseWorldPos = GameService::getInstance().getMouseWorldPos(); + + if (mouseWorldPos.x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right || + mouseWorldPos.x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) + { + + float angleShot = atan2(mouseWorldPos.y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + mouseWorldPos.x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + + /** Making the bullet originate from the gun's mouth */ + float offsetDistance = this->gunLeft.getSize().x / 2; + float offsetX = cos(angleShot) * offsetDistance; + float offsetY = sin(angleShot) * offsetDistance; + Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; + + BulletService::getInstance().addPlayerBullet(bulletStartLocation, angleShot); + } + } +} + +void Player::outofboundsCheck() +{ + if (this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) + { + this->health = 0; + GameService::getInstance().setGameOver(true); + } +} + +void Player::rotateGun() +{ + if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right) + { + float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + GameService::getInstance().getMouseWorldPos().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + this->gunRight.setRotation(gunRotationRight * 180 / M_PI); + } + else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) + { + float gunRotationLeft = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + (this->texture.getPosition().x + this->texture.getSize().x / 2.0) - GameService::getInstance().getMouseWorldPos().x); + this->gunLeft.setRotation(-gunRotationLeft * 180 / M_PI); + } +} + +void Player::drawGun() +{ + if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) + { + // + this->texture.getSize().x / 2.0f + // + this->texture.getSize().y / 2.0f + DrawTexturePro( + this->gunRight.getTexture(), + {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, + this->gunRight.getSize().x, + this->gunRight.getSize().y}, + {this->gunRight.getSize().x / 2.0f, this->gunRight.getSize().y / 2.0f}, + this->gunRight.getRotation(), + WHITE); + } + else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) + { + DrawTexturePro( + this->gunLeft.getTexture(), + {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, + {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f, + this->gunLeft.getSize().x, + this->gunLeft.getSize().y}, + {this->gunLeft.getSize().x / 2.0f, this->gunLeft.getSize().y / 2.0f}, + this->gunLeft.getRotation(), + WHITE); + } +} + +void Player::fall() +{ + outofboundsCheck(); + velocity.y += gravity; + move(velocity); +} \ No newline at end of file diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index fb25cf3..653e57c 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -31,6 +31,7 @@ void AssetService::setAssets() sounds[ESound::VICTORY] = loadSound("Audio/Sounds/victorySound.wav"); sounds[ESound::PLAYER_DAMAGE] = loadSound("Audio/Sounds/WaterBalloon.wav"); sounds[ESound::ENEMY_DEFEATED] = loadSound("Audio/Sounds/pain.wav"); + sounds[ESound::PLAYER_SPOTTED] = loadSound("Audio/Sounds/playerSpotted.wav"); backgrounds[EBackground::TUTORIAL] = loadTexture("Textures/TutorialBackground2.png"); backgrounds[EBackground::GAME1] = loadTexture("Textures/GameBackground_1.png"); From d740e1a16748c413f79f8ec619ee5004201760ca Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Tue, 17 Dec 2024 23:15:26 +0100 Subject: [PATCH 09/12] [Updated] Code for use with WASM, [TODO] Make enemy gun follow player, spawn bullets from tip of the barrel --- include/entities/Enemy.h | 7 +- include/entities/Flag.h | 8 +- include/entities/Game.h | 27 ++-- include/entities/Player.h | 5 + include/entities/Powerup.h | 5 +- include/entities/Text.h | 68 +++++---- include/screens/Screen.h | 3 +- include/screens/VictoryScreen.h | 2 + include/services/BulletService.h | 1 + include/services/CollectableService.h | 209 ++------------------------ include/services/EnemyService.h | 53 +------ include/services/GameService.h | 4 +- include/services/InputService.h | 11 +- include/services/StateService.h | 18 +-- scripts/build_wasm.sh | 3 +- src/entities/Map.cpp | 49 +----- src/entities/Player.cpp | 6 +- src/main.cpp | 57 ------- src/screens/MainScreen.cpp | 4 +- src/screens/TitleScreen.cpp | 2 - src/screens/TutorialScreen.cpp | 5 +- src/services/AssetService.cpp | 1 - src/services/CollectableService.cpp | 196 ++++++++++++++++++++++++ src/services/EnemyService.cpp | 51 +++++++ src/services/StateService.cpp | 14 +- 25 files changed, 371 insertions(+), 438 deletions(-) create mode 100644 src/services/CollectableService.cpp create mode 100644 src/services/EnemyService.cpp diff --git a/include/entities/Enemy.h b/include/entities/Enemy.h index 5d811ba..db80e7d 100644 --- a/include/entities/Enemy.h +++ b/include/entities/Enemy.h @@ -6,6 +6,7 @@ #include "entities/TextureWrapper.h" #include "entities/Vector2i.h" #include +#include class Enemy { @@ -47,7 +48,7 @@ public: return this->texture; } - u_int8_t getHealth() + uint8_t getHealth() { return this->health; } @@ -61,8 +62,8 @@ private: TextureWrapper texture; TextureWrapper gunLeft; TextureWrapper gunRight; - u_int8_t health; - u_int8_t maxHealth = 5; + uint8_t health; + uint8_t maxHealth = 5; Vector2 velocity = {0, 0}; float spottingRange = 300; // From how far away enemies can spot the player float gravity = 0.6f; diff --git a/include/entities/Flag.h b/include/entities/Flag.h index fbb4287..7215236 100644 --- a/include/entities/Flag.h +++ b/include/entities/Flag.h @@ -20,10 +20,10 @@ public: void logic() { - if (touched) - { - StateService::getInstance().setScreen(EScreen::VICTORY); - } + // if (touched) + // { + // // StateService::getInstance().setScreen(EScreen::VICTORY); + // } } void draw() diff --git a/include/entities/Game.h b/include/entities/Game.h index df4df1b..0de6b30 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -66,7 +66,7 @@ public: void logic() { - this->crosshair.setPosition(GetMousePosition()); + this->crosshair.setPosition(InputService::getInstance().getMousePos()); if (GameService::getInstance().isPaused()) { @@ -96,6 +96,9 @@ public: { GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; } + if(player.isFinishReached()) { + StateService::getInstance().setScreen(EScreen::VICTORY); + } } void draw() @@ -113,6 +116,7 @@ public: EndMode2D(); + drawHud(); if (GameService::getInstance().isPaused()) { drawPaused(); @@ -120,9 +124,9 @@ public: else if (GameService::getInstance().isGameOver()) { drawGameOver(); + } else { + drawCrosshair(); } - drawHud(); - drawCrosshair(); EndDrawing(); } @@ -184,7 +188,7 @@ private: const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; for (int i = 0; i < AMNT_OF_BUTTONS; i++) { - Text txt = Text("Play Again", EFont::SANSATION, 30, 0, GRAY); + Text txt = Text("Play Again", EFont::SANSATION, 30, 0, BLACK); gameOverScreenButtons.push_back(txt); } gameOverScreenButtons[0].setColor(WHITE); @@ -229,7 +233,7 @@ private: { if (InputService::getInstance().isKeyPressed(KEY_D) || InputService::getInstance().isKeyPressed(KEY_A)) { - gameOverScreenButtons[hoveredGameOverScreenButton].setColor(GRAY); + gameOverScreenButtons[hoveredGameOverScreenButton].setColor(BLACK); /** Move up or down depending on the pressed button. +4 is used so that -1 becomes 3 (the last button) */ hoveredGameOverScreenButton = ((hoveredGameOverScreenButton - (InputService::getInstance().isKeyPressed(KEY_D) ? -1 : 1)) + gameOverScreenButtons.size()) % gameOverScreenButtons.size(); gameOverScreenButtons[hoveredGameOverScreenButton].setColor(WHITE); @@ -254,7 +258,7 @@ private: void drawPaused() { // 100, 100, 255, 60 - DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {0, 0, 255, 100}); + DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {0, 0, 0, 200}); if (pauseScreenButtons.size() > 0) { for (int i = 0; i < pauseScreenButtons.size(); i++) @@ -266,7 +270,7 @@ private: void drawGameOver() { - DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {255, 0, 0, 100}); + DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {100, 0, 0, 200}); gameOverText.draw(); @@ -280,17 +284,10 @@ private: { DrawTextureEx( this->crosshair.getTexture(), - {GetMousePosition().x - this->crosshair.getSize().x / 2.0f, GetMousePosition().y - this->crosshair.getSize().y / 2.0f}, + {InputService::getInstance().getMousePos().x - this->crosshair.getSize().x / 2.0f, InputService::getInstance().getMousePos().y - this->crosshair.getSize().y / 2.0f}, 0, 1.2f, RED); - // DrawTexturePro( - // this->crosshair.getTexture(), - // this->crosshair.getSourceRect(), - // {GetMousePosition().x, GetMousePosition().y, GetMousePosition().x+this->crosshair.getSize().x, GetMousePosition().y+this->crosshair.getSize().y}, - // {this->crosshair.getSize().x/2.0f, this->crosshair.getSize().y/2.0f}, - // 0, - // WHITE); } void drawHud() diff --git a/include/entities/Player.h b/include/entities/Player.h index a8c44a5..d4e0263 100644 --- a/include/entities/Player.h +++ b/include/entities/Player.h @@ -81,6 +81,10 @@ public: this->health = std::max(health - damage, 0); } + bool isFinishReached() { + return this->finishReached; + } + protected: private: TextureWrapper texture; @@ -113,6 +117,7 @@ private: uint8_t health; // Current health of the player uint8_t max_health = 10; // Max health of the player bool gainedPowerup = false; + bool finishReached = false; void applyGravity(); void movement(); diff --git a/include/entities/Powerup.h b/include/entities/Powerup.h index 7375531..837996b 100644 --- a/include/entities/Powerup.h +++ b/include/entities/Powerup.h @@ -4,6 +4,7 @@ #include "raylib.h" #include "services/AssetService.h" #include "entities/TextureWrapper.h" +#include class Powerup { public: @@ -40,8 +41,8 @@ class Powerup { bool pickedUp = false; // When the player has picked up the pack float animationDelay = 0.08f; // How long to wait in between animation frames (in seconds) double lastFrame; // Time since the last frame - u_int8_t currentFrame = 0; // Current frame of the animation - u_int8_t amountOfFrames = 8; // Total amount of animation frames + uint8_t currentFrame = 0; // Current frame of the animation + uint8_t amountOfFrames = 8; // Total amount of animation frames void animate() { if(GetTime() - lastFrame > animationDelay) { diff --git a/include/entities/Text.h b/include/entities/Text.h index 680a206..e667f11 100644 --- a/include/entities/Text.h +++ b/include/entities/Text.h @@ -3,47 +3,53 @@ #include "raylib.h" #include "enums/EFont.h" -#include -#include #include "services/AssetService.h" +#include -class Text { - public: - Text(){} - Text(std::string text, EFont font, int fontSize = 30, int spacing = 0, Color color = WHITE, Vector2 position = {100,100}); - ~Text(){} +class Text +{ +public: + Text() {} + Text(std::string text, EFont font, int fontSize = 30, int spacing = 0, Color color = WHITE, Vector2 position = {100, 100}); + ~Text() { + } - Vector2 getDimensions(); + Vector2 getDimensions(); - void setPosition(Vector2 position) { - this->position = position; - } + void setPosition(Vector2 position) + { + this->position = position; + } - void setColor(Color color) { - this->color = color; - } + void setColor(Color color) + { + this->color = color; + } - void setText(std::string text) { - this->text = text; - } + void setText(std::string text) + { + this->text = text; + } - Vector2 getPosition() { - return position; - } + Vector2 getPosition() + { + return position; + } - std::string getText() { - return this->text; - } + std::string getText() + { + return this->text; + } - void draw(); + void draw(); - private: - std::string text; - Font font; - int fontSize; - int spacing; - Color color; - Vector2 position; +private: + std::string text; + Font font; + int fontSize; + int spacing; + Color color; + Vector2 position; }; #endif \ No newline at end of file diff --git a/include/screens/Screen.h b/include/screens/Screen.h index 90270ec..9acc00c 100644 --- a/include/screens/Screen.h +++ b/include/screens/Screen.h @@ -5,7 +5,8 @@ class Screen { public: Screen(){} - ~Screen() {} + ~Screen() { + } /** * Execute all logic for this frame diff --git a/include/screens/VictoryScreen.h b/include/screens/VictoryScreen.h index a0e71fa..600f790 100644 --- a/include/screens/VictoryScreen.h +++ b/include/screens/VictoryScreen.h @@ -14,6 +14,8 @@ class VictoryScreen : public Screen public: VictoryScreen(); + ~VictoryScreen() { + } void logic() override; void draw() override; diff --git a/include/services/BulletService.h b/include/services/BulletService.h index 4751191..ab9ee19 100644 --- a/include/services/BulletService.h +++ b/include/services/BulletService.h @@ -4,6 +4,7 @@ #include "raylib.h" #include "services/Service.h" #include "services/AssetService.h" +#include "entities/Bullet.h" #include "entities/PBullet.h" #include "entities/EBullet.h" #include diff --git a/include/services/CollectableService.h b/include/services/CollectableService.h index be118ae..94ef4d6 100644 --- a/include/services/CollectableService.h +++ b/include/services/CollectableService.h @@ -3,7 +3,6 @@ #include "raylib.h" #include "services/Service.h" -#include "services/MapService.h" #include "entities/Vector2i.h" #include "entities/Crate.h" #include "entities/Jelly.h" @@ -94,198 +93,22 @@ private: std::vector powerUps; Flag flag; - void initCrates() - { - crates.clear(); - int tileSize = MapService::getInstance().getMap().getTileSize(); - for (int i = 0; i < amountOfCrates; i++) - { - crates.push_back(Crate()); - } - crates[0].getObject().setPosition({38.0f * tileSize, 9.0f * tileSize}); - crates[1].getObject().setPosition({41.0f * tileSize, 5.0f * tileSize}); - crates[2].getObject().setPosition({68.0f * tileSize, 4.0f * tileSize}); - - for (int i = 0; i < crates.size(); i++) - { - Vector2i crateTilePos = Vector2i(crates[i].getObject().getPosition().x / tileSize, crates[i].getObject().getPosition().y / tileSize); - MapService::getInstance().getMap().updateColmapTile(crateTilePos, true); - } - } - - void initJellies() - { - jellies.clear(); - } - - void initAmmoPacks() - { - ammoPacks.clear(); - int tileSize = MapService::getInstance().getMap().getTileSize(); - for (int i = 0; i < amountOfAmmoPacks; i++) - { - ammoPacks.push_back(AmmoPack()); - } - ammoPacks[0].getObject().setPosition({32.0f * tileSize, 9.0f * tileSize}); - ammoPacks[1].getObject().setPosition({48.0f * tileSize, 5.0f * tileSize}); - ammoPacks[2].getObject().setPosition({88.0f * tileSize, 9.0f * tileSize}); - ammoPacks[3].getObject().setPosition({105.0f * tileSize, 15.0f * tileSize}); - } - - void initHealthPacks() - { - healthPacks.clear(); - int tileSize = MapService::getInstance().getMap().getTileSize(); - for (int i = 0; i < amountOfHealthPacks; i++) - { - healthPacks.push_back(HealthPack()); - } - healthPacks[0].getObject().setPosition({28.0f * tileSize, 10.0f * tileSize}); - healthPacks[1].getObject().setPosition({64.0f * tileSize, 6.0f * tileSize}); - healthPacks[2].getObject().setPosition({118.0f * tileSize, 11.0f * tileSize}); - healthPacks[3].getObject().setPosition({74.0f * tileSize, 8.0f * tileSize}); - } - - void initPowerups() - { - powerUps.clear(); - int tileSize = MapService::getInstance().getMap().getTileSize(); - for (int i = 0; i < amountOfPowerups; i++) - { - powerUps.push_back(Powerup()); - } - powerUps[0].getObject().setPosition({41.0f * tileSize+8, 10.0f * tileSize-25}); - } - - void initFlag() { - int tileSize = MapService::getInstance().getMap().getTileSize(); - this->flag = Flag({118.0f*tileSize-tileSize/2.0f, 3.0f * tileSize-8}); - } - - void crateLogic() - { - for (auto it = crates.begin(); it != crates.end();) - { - auto &crate = *it; - /** - * If the crate has been hit: - * 1. Create a Jelly on the location of the crate - * 2. Remove the collision tile from the collision map - * 3. Remove the crate - */ - if (crate.hasBeenHit()) - { - Vector2 pos = crate.getObject().getPosition(); - - jellies.push_back(Jelly(pos)); - - Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); - MapService::getInstance().getMap().updateColmapTile(tilePos, false); - it = crates.erase(it); - } - else - { - ++it; - } - } - } - - void jellyLogic() - { - for (int i = 0; i < jellies.size(); i++) - { - jellies[i].logic(); - } - } - - void ammoPackLogic() - { - for (auto it = ammoPacks.begin(); it != ammoPacks.end();) - { - auto &ammoPack = *it; - if (ammoPack.hasBeenPickedUp()) - { - it = ammoPacks.erase(it); - } - else - { - ++it; - } - } - } - - void healthPackLogic() - { - for (auto it = healthPacks.begin(); it != healthPacks.end();) - { - auto &healthPack = *it; - if (healthPack.hasBeenPickedUp()) - { - it = healthPacks.erase(it); - } - else - { - ++it; - } - } - } - - void powerupLogic() - { - for (auto it = powerUps.begin(); it != powerUps.end();) - { - auto &powerUp = *it; - if (powerUp.hasBeenPickedUp()) - { - it = powerUps.erase(it); - } - else - { - powerUp.logic(); - ++it; - } - } - } - - void drawCrates() - { - for (int i = 0; i < crates.size(); i++) - { - crates[i].draw(); - } - } - - void drawJellies() - { - for (int i = 0; i < jellies.size(); i++) - { - jellies[i].draw(); - } - } - - void drawAmmoPacks() - { - for (int i = 0; i < ammoPacks.size(); i++) - { - ammoPacks[i].draw(); - } - } - - void drawHealthpacks() - { - for (int i = 0; i < healthPacks.size(); i++) - { - healthPacks[i].draw(); - } - } - - void drawPowerups() - { - for (int i = 0; i < powerUps.size(); i++) - { - powerUps[i].draw(); - } - } + void initCrates(); + void initJellies(); + void initAmmoPacks(); + void initHealthPacks(); + void initPowerups(); + void initFlag(); + void crateLogic(); + void jellyLogic(); + void ammoPackLogic(); + void healthPackLogic(); + void powerupLogic(); + void drawCrates(); + void drawJellies(); + void drawAmmoPacks(); + void drawHealthpacks(); + void drawPowerups(); }; #endif \ No newline at end of file diff --git a/include/services/EnemyService.h b/include/services/EnemyService.h index 3a8a1eb..ee56a85 100644 --- a/include/services/EnemyService.h +++ b/include/services/EnemyService.h @@ -3,61 +3,16 @@ #include "raylib.h" #include "services/Service.h" -#include "services/AssetService.h" #include "entities/Enemy.h" #include class EnemyService : public Service { public: - void initEnemies() - { - this->enemies.clear(); - const int AMNT_OF_ENEMIES = 8; - int tileSize = MapService::getInstance().getMap().getTileSize(); - for (int i = 0; i < AMNT_OF_ENEMIES; i++) - { - enemies.push_back(Enemy()); - } - this->enemies[0].getObject().setPosition({31.0f * tileSize, 15.0f * tileSize}); - this->enemies[1].getObject().setPosition({51.0f * tileSize, 10.0f * tileSize}); - this->enemies[2].getObject().setPosition({56.0f * tileSize, 5.0f * tileSize}); - this->enemies[3].getObject().setPosition({78.0f * tileSize, 10.0f * tileSize}); - this->enemies[4].getObject().setPosition({82.0f * tileSize, 15.0f * tileSize}); - this->enemies[5].getObject().setPosition({88.0f * tileSize, 4.0f * tileSize}); - this->enemies[6].getObject().setPosition({98.0f * tileSize, 11.0f * tileSize}); - this->enemies[7].getObject().setPosition({103.0f * tileSize, 15.0f * tileSize}); - } - - void enemyLogic() - { - for (auto it = enemies.begin(); it != enemies.end();) - { - auto &enemy = *it; - if (enemy.getObject().getPosition().y < 0) - { - it = enemies.erase(it); - } - else - { - enemy.logic(); - ++it; - } - } - } - - void drawEnemies() - { - for (int i = 0; i < enemies.size(); i++) - { - enemies[i].draw(); - } - } - - std::vector &getEnemies() - { - return this->enemies; - } + void initEnemies(); + void enemyLogic(); + void drawEnemies(); + std::vector &getEnemies(); protected: private: diff --git a/include/services/GameService.h b/include/services/GameService.h index 81f3cf7..9b6b42a 100644 --- a/include/services/GameService.h +++ b/include/services/GameService.h @@ -4,6 +4,7 @@ #include "raylib.h" #include "services/Service.h" #include "services/AudioService.h" +#include "services/InputService.h" #include #include @@ -49,7 +50,8 @@ public: Vector2 getMouseWorldPos() { - Vector2 mouseWorldPos = {GetMousePosition().x + camera.target.x - camera.offset.x, GetMousePosition().y}; + Vector2 mouseWorldPos = {InputService::getInstance().getMousePos().x + camera.target.x - camera.offset.x, + InputService::getInstance().getMousePos().y}; return mouseWorldPos; } diff --git a/include/services/InputService.h b/include/services/InputService.h index 84b06bc..de793f7 100644 --- a/include/services/InputService.h +++ b/include/services/InputService.h @@ -1,8 +1,8 @@ #ifndef INPUT_SERVICE_H #define INPUT_SERVICE_H -#include "Service.h" #include "raylib.h" +#include "Service.h" #include #include #include @@ -69,6 +69,14 @@ public: } } + void checkMousePos() { + this->mousePosition = GetMousePosition(); + } + + Vector2 getMousePos() { + return this->mousePosition; + } + bool isKeyPressed(KeyboardKey key) { return keysPressed[key]; @@ -88,6 +96,7 @@ public: } private: + Vector2 mousePosition {0,0}; std::unordered_set watchedKeys; std::unordered_map keysPressed; std::unordered_map keysDown; diff --git a/include/services/StateService.h b/include/services/StateService.h index 4e1ce49..750a8fb 100644 --- a/include/services/StateService.h +++ b/include/services/StateService.h @@ -1,16 +1,17 @@ #ifndef STATE_SERVICE_H #define STATE_SERVICE_H -#include -#include -#include "services/Service.h" -#include "screens/Screen.h" -#include "enums/EScreen.h" #include "raylib.h" +#include "enums/EScreen.h" +#include "services/Service.h" +#include "services/InputService.h" +#include "screens/Screen.h" +#include #if defined(PLATFORM_WEB) #include #endif +// #include /** * Handles switching between screens @@ -34,14 +35,10 @@ public: void startGame(); static void staticStep() { - if (IsKeyPressed(KEY_ENTER) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) - { - std::cout << "Input!" << std::endl; - } // Access the singleton instance and call step getInstance().step(); } - + private: std::unique_ptr currentScreen; bool gameRunning = true; @@ -52,6 +49,7 @@ private: */ void step(); void handleMusic(); + }; #endif \ No newline at end of file diff --git a/scripts/build_wasm.sh b/scripts/build_wasm.sh index e921bfc..5227541 100755 --- a/scripts/build_wasm.sh +++ b/scripts/build_wasm.sh @@ -2,7 +2,8 @@ # emcc src/main.cpp -o dist/game.js -Wno-missing-braces -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s USE_GLFW=3 -DPLATFORM_WEB -emcc $(find src/ -name "*.cpp") -o dist/game.js -Wno-missing-braces -I./include -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=268435456 -s MAXIMUM_MEMORY=4294967296 -s --preload-file assets@/assets -s USE_GLFW=3 -DPLATFORM_WEB +# emcc $(find src/ -name "*.cpp") -o dist/game.js -Wno-missing-braces -I./include -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=268435456 -s MAXIMUM_MEMORY=4294967296 -s --preload-file assets@/assets -s USE_GLFW=3 -DPLATFORM_WEB +emcc $(find src/ -name "*.cpp") -o dist/game.js -Wno-missing-braces -I./include -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=536870912 -s MAXIMUM_MEMORY=4294967296 -s --preload-file assets@/assets -s USE_GLFW=3 -s ASSERTIONS=2 -DPLATFORM_WEB # -I./include # -L$LD_LIBRARY_PATH # -I. diff --git a/src/entities/Map.cpp b/src/entities/Map.cpp index 775e090..7e0d2b5 100644 --- a/src/entities/Map.cpp +++ b/src/entities/Map.cpp @@ -1,10 +1,8 @@ #include "entities/Map.h" +#include "services/AssetService.h" #include #include #include -#include -#include "services/AssetService.h" -#include "entities/Map.h" Map::Map() { @@ -114,19 +112,6 @@ void Map::loadTileset() this->tileset = AssetService::getInstance().loadTexture("Textures/tileset.png"); } -// std::unordered_map Map::cornersToTilePos(std::unordered_map corners) -// { -// // std::cout << "Starting converting" << std::endl; -// std::unordered_map cornersTilePos; -// for (const auto &pair : corners) -// { -// // std::cout << "Foreach pair" << std::endl; -// Vector2 original = corners[pair.first]; -// cornersTilePos[pair.first] = {floorf(original.x / this->tileSize), floorf(original.y / this->tileSize)}; -// } -// return cornersTilePos; -// } - void Map::draw() { for (int i = 0; i < map.size(); i++) @@ -146,35 +131,3 @@ void Map::draw() } } -// std::unordered_map Map::checkForCollision(TextureWrapper texture) -// { -// // std::cout << "Converting.." << std::endl; -// std::unordered_map cornersTilePos = cornersToTilePos(texture.getCorners()); -// // std::cout << "Done with converting" << std::endl; - -// std::unordered_map cornerCollisions; - -// for (const auto &pair : cornersTilePos) -// { -// // std::cout << "foreach collision check" << std::endl; -// Vector2 pos = cornersTilePos[pair.first]; -// // std::cout << pos.x << " " << pos.y << std::endl; -// if(colMap[pos.y][pos.x] == 1) { -// cornerCollisions[pair.first] = true; -// } else { -// cornerCollisions[pair.first] = false; -// } -// } -// // std::cout << "Finished collision checks" << std::endl; - -// return cornerCollisions; - -// // for (int i = 0; i < tiles.size(); i++) -// // { -// // if (colMap[tiles[i].y][tiles[i].x] == 1) -// // { -// // timesCollision.push_back(i); -// // } -// // } - -// } diff --git a/src/entities/Player.cpp b/src/entities/Player.cpp index 81df783..f3c54af 100644 --- a/src/entities/Player.cpp +++ b/src/entities/Player.cpp @@ -3,6 +3,7 @@ #include "services/GameService.h" #include "services/CollectableService.h" #include "services/InputService.h" +#include "services/MapService.h" void Player::applyGravity() { @@ -171,7 +172,7 @@ void Player::consumableCollision() { healthPack.setPickedUp(true); AssetService::getInstance().playSound(ESound::HEAL_PICKUP); - health = std::max(health + healthPack.getHealAmount(), (int)max_health); + health = std::min(health + healthPack.getHealAmount(), (int)max_health); } } } @@ -188,8 +189,6 @@ void Player::consumableCollision() AssetService::getInstance().playSound(ESound::POWERUP); jumpPower += powerup.getExtraJumpPower(); gainedPowerup = true; - // health = std::max(health+powerup.getHealAmount(),(int)max_health); - // } } } @@ -200,6 +199,7 @@ void Player::consumableCollision() this->texture.getPosition().y < flag.getObject().getPosition().y + flag.getObject().getSize().y) { flag.setTouched(true); + finishReached = true; } } diff --git a/src/main.cpp b/src/main.cpp index e252ab4..c7b1bed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,66 +1,9 @@ -#include "raylib.h" -// #include "screens/Screen.h" -// #include "screens/TitleScreen.h" #include "services/StateService.h" -// #include - -// #if defined(PLATFORM_WEB) -// #include -// #endif - -// void step(); -// static void gameloop(); int main() { StateService& stateService = StateService::getInstance(); stateService.setScreenSize({640,544}); stateService.startGame(); - // std::cout << "StateService instance: " << service << std::endl; - -// InitWindow(640, 544, "Jelly Rescue"); -// // SetTargetFPS(60); - -// // setScreen(EScreen::TITLE); - -// #if defined(PLATFORM_WEB) -// emscripten_set_main_loop(step, 0, 1); -// #else - -// while (!WindowShouldClose()) -// { -// step(); -// } -// #endif - -// CloseWindow(); - return 0; } - -// static void gameloop() -// { -// // InitAudioDevice(); -// InitWindow(640, 544, "Jelly Rescue"); -// // SetTargetFPS(60); - -// // setScreen(EScreen::TITLE); - -// #if defined(PLATFORM_WEB) -// emscripten_set_main_loop(step, 0, 1); -// #else - -// while (!WindowShouldClose()) -// { -// step(); -// } -// #endif - -// CloseWindow(); -// // CloseAudioDevice(); -// } - -// void step() -// { -// ClearBackground(BLACK); -// } \ No newline at end of file diff --git a/src/screens/MainScreen.cpp b/src/screens/MainScreen.cpp index 0eaf9b4..1371361 100644 --- a/src/screens/MainScreen.cpp +++ b/src/screens/MainScreen.cpp @@ -1,7 +1,7 @@ #include "screens/MainScreen.h" +#include "raylib.h" #include "services/AudioService.h" #include "services/StateService.h" -#include "raylib.h" #include "services/InputService.h" MainScreen::MainScreen() @@ -60,7 +60,7 @@ void MainScreen::mouseLogic() Vector2 pos = buttons[i].getPosition(); Rectangle rect = {pos.x, pos.y, dim.x, dim.y}; - Vector2 mousePos = GetMousePosition(); + Vector2 mousePos = InputService::getInstance().getMousePos(); // mousePos.x >= rect.x && mousePos.x <= (rect.x+rect.width) && if ((mousePos.x + textPadding) >= rect.x && mousePos.x <= (rect.x + rect.width + textPadding) && (mousePos.y + textPadding) >= rect.y && mousePos.y <= (rect.y + rect.height + textPadding)) { diff --git a/src/screens/TitleScreen.cpp b/src/screens/TitleScreen.cpp index 98e7f35..0dd10ed 100644 --- a/src/screens/TitleScreen.cpp +++ b/src/screens/TitleScreen.cpp @@ -3,7 +3,6 @@ #include "services/StateService.h" #include "enums/EFont.h" #include "services/AudioService.h" -#include #include "services/InputService.h" TitleScreen::TitleScreen() @@ -34,7 +33,6 @@ TitleScreen::TitleScreen() void TitleScreen::logic() { - // std::cout << "Next screen!" << std::endl; if (InputService::getInstance().isKeyPressed(KEY_ENTER) || InputService::getInstance().isMouseButtonPressed(MOUSE_BUTTON_LEFT)) { StateService::getInstance().setScreen(EScreen::MAIN); diff --git a/src/screens/TutorialScreen.cpp b/src/screens/TutorialScreen.cpp index 3f51fef..05f5701 100644 --- a/src/screens/TutorialScreen.cpp +++ b/src/screens/TutorialScreen.cpp @@ -1,8 +1,7 @@ #include "screens/TutorialScreen.h" - -#include "services/StateService.h" -#include "enums/EFont.h" #include "enums/EScreen.h" +#include "enums/EFont.h" +#include "services/StateService.h" #include "services/InputService.h" TutorialScreen::TutorialScreen() { diff --git a/src/services/AssetService.cpp b/src/services/AssetService.cpp index 653e57c..47c9c30 100644 --- a/src/services/AssetService.cpp +++ b/src/services/AssetService.cpp @@ -1,5 +1,4 @@ #include "services/AssetService.h" -#include AssetService::AssetService() { setAssetDir("assets/"); diff --git a/src/services/CollectableService.cpp b/src/services/CollectableService.cpp new file mode 100644 index 0000000..69feae9 --- /dev/null +++ b/src/services/CollectableService.cpp @@ -0,0 +1,196 @@ +#include "services/CollectableService.h" +#include "services/MapService.h" + +void CollectableService::initCrates() +{ + crates.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfCrates; i++) + { + crates.push_back(Crate()); + } + crates[0].getObject().setPosition({38.0f * tileSize, 9.0f * tileSize}); + crates[1].getObject().setPosition({41.0f * tileSize, 5.0f * tileSize}); + crates[2].getObject().setPosition({68.0f * tileSize, 4.0f * tileSize}); + + for (int i = 0; i < crates.size(); i++) + { + Vector2i crateTilePos = Vector2i(crates[i].getObject().getPosition().x / tileSize, crates[i].getObject().getPosition().y / tileSize); + MapService::getInstance().getMap().updateColmapTile(crateTilePos, true); + } +} + +void CollectableService::initJellies() +{ + jellies.clear(); +} + +void CollectableService::initAmmoPacks() +{ + ammoPacks.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfAmmoPacks; i++) + { + ammoPacks.push_back(AmmoPack()); + } + ammoPacks[0].getObject().setPosition({32.0f * tileSize, 9.0f * tileSize}); + ammoPacks[1].getObject().setPosition({48.0f * tileSize, 5.0f * tileSize}); + ammoPacks[2].getObject().setPosition({88.0f * tileSize, 9.0f * tileSize}); + ammoPacks[3].getObject().setPosition({105.0f * tileSize, 15.0f * tileSize}); +} + +void CollectableService::initHealthPacks() +{ + healthPacks.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfHealthPacks; i++) + { + healthPacks.push_back(HealthPack()); + } + healthPacks[0].getObject().setPosition({28.0f * tileSize, 10.0f * tileSize}); + healthPacks[1].getObject().setPosition({64.0f * tileSize, 6.0f * tileSize}); + healthPacks[2].getObject().setPosition({118.0f * tileSize, 11.0f * tileSize}); + healthPacks[3].getObject().setPosition({74.0f * tileSize, 8.0f * tileSize}); +} + +void CollectableService::initPowerups() +{ + powerUps.clear(); + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < amountOfPowerups; i++) + { + powerUps.push_back(Powerup()); + } + powerUps[0].getObject().setPosition({41.0f * tileSize + 8, 10.0f * tileSize - 25}); +} + +void CollectableService::initFlag() +{ + int tileSize = MapService::getInstance().getMap().getTileSize(); + this->flag = Flag({118.0f * tileSize - tileSize / 2.0f, 3.0f * tileSize - 8}); +} + +void CollectableService::crateLogic() +{ + for (auto it = crates.begin(); it != crates.end();) + { + auto &crate = *it; + /** + * If the crate has been hit: + * 1. Create a Jelly on the location of the crate + * 2. Remove the collision tile from the collision map + * 3. Remove the crate + */ + if (crate.hasBeenHit()) + { + Vector2 pos = crate.getObject().getPosition(); + + jellies.push_back(Jelly(pos)); + + Vector2i tilePos = Vector2i(pos.x / MapService::getInstance().getMap().getTileSize(), pos.y / MapService::getInstance().getMap().getTileSize()); + MapService::getInstance().getMap().updateColmapTile(tilePos, false); + it = crates.erase(it); + } + else + { + ++it; + } + } +} + +void CollectableService::jellyLogic() +{ + for (int i = 0; i < jellies.size(); i++) + { + jellies[i].logic(); + } +} + +void CollectableService::ammoPackLogic() +{ + for (auto it = ammoPacks.begin(); it != ammoPacks.end();) + { + auto &ammoPack = *it; + if (ammoPack.hasBeenPickedUp()) + { + it = ammoPacks.erase(it); + } + else + { + ++it; + } + } +} + +void CollectableService::healthPackLogic() +{ + for (auto it = healthPacks.begin(); it != healthPacks.end();) + { + auto &healthPack = *it; + if (healthPack.hasBeenPickedUp()) + { + it = healthPacks.erase(it); + } + else + { + ++it; + } + } +} + +void CollectableService::powerupLogic() +{ + for (auto it = powerUps.begin(); it != powerUps.end();) + { + auto &powerUp = *it; + if (powerUp.hasBeenPickedUp()) + { + it = powerUps.erase(it); + } + else + { + powerUp.logic(); + ++it; + } + } +} + +void CollectableService::drawCrates() +{ + for (int i = 0; i < crates.size(); i++) + { + crates[i].draw(); + } +} + +void CollectableService::drawJellies() +{ + for (int i = 0; i < jellies.size(); i++) + { + jellies[i].draw(); + } +} + +void CollectableService::drawAmmoPacks() +{ + for (int i = 0; i < ammoPacks.size(); i++) + { + ammoPacks[i].draw(); + } +} + +void CollectableService::drawHealthpacks() +{ + for (int i = 0; i < healthPacks.size(); i++) + { + healthPacks[i].draw(); + } +} + +void CollectableService::drawPowerups() +{ + for (int i = 0; i < powerUps.size(); i++) + { + powerUps[i].draw(); + } +} \ No newline at end of file diff --git a/src/services/EnemyService.cpp b/src/services/EnemyService.cpp new file mode 100644 index 0000000..646fde2 --- /dev/null +++ b/src/services/EnemyService.cpp @@ -0,0 +1,51 @@ +#include "services/EnemyService.h" +#include "services/MapService.h" + +void EnemyService::initEnemies() +{ + this->enemies.clear(); + const int AMNT_OF_ENEMIES = 8; + int tileSize = MapService::getInstance().getMap().getTileSize(); + for (int i = 0; i < AMNT_OF_ENEMIES; i++) + { + enemies.push_back(Enemy()); + } + this->enemies[0].getObject().setPosition({31.0f * tileSize, 15.0f * tileSize}); + this->enemies[1].getObject().setPosition({51.0f * tileSize, 10.0f * tileSize}); + this->enemies[2].getObject().setPosition({56.0f * tileSize, 5.0f * tileSize}); + this->enemies[3].getObject().setPosition({78.0f * tileSize, 10.0f * tileSize}); + this->enemies[4].getObject().setPosition({82.0f * tileSize, 15.0f * tileSize}); + this->enemies[5].getObject().setPosition({88.0f * tileSize, 4.0f * tileSize}); + this->enemies[6].getObject().setPosition({98.0f * tileSize, 11.0f * tileSize}); + this->enemies[7].getObject().setPosition({103.0f * tileSize, 15.0f * tileSize}); +} + +void EnemyService::enemyLogic() +{ + for (auto it = enemies.begin(); it != enemies.end();) + { + auto &enemy = *it; + if (enemy.getObject().getPosition().y < 0) + { + it = enemies.erase(it); + } + else + { + enemy.logic(); + ++it; + } + } +} + +void EnemyService::drawEnemies() +{ + for (int i = 0; i < enemies.size(); i++) + { + enemies[i].draw(); + } +} + +std::vector &EnemyService::getEnemies() +{ + return this->enemies; +} \ No newline at end of file diff --git a/src/services/StateService.cpp b/src/services/StateService.cpp index c477d51..9b523af 100644 --- a/src/services/StateService.cpp +++ b/src/services/StateService.cpp @@ -49,17 +49,9 @@ void StateService::startGame() // InitWindow(640,544, "Jelly Rescue"); InitWindow(screenSize.x, screenSize.y, "Jelly Rescue"); SetTargetFPS(45); + #if not defined(PLATFORM_WEB) HideCursor(); - - // #if defined(PLATFORM_WEB) - // emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_CANVAS, nullptr, 1, staticStep); - // #endif - - // #if defined(PLATFORM_WEB) - // emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_CANVAS, nullptr, 1, [](const EmscriptenFocusEvent* event) { - // std::cout << "Canvas Focused!" << std::endl; - // }); - // #endif + #endif setScreen(EScreen::TITLE); @@ -67,7 +59,6 @@ void StateService::startGame() emscripten_set_main_loop(StateService::staticStep, 0, 1); #else - // while (!WindowShouldClose() && instance->gameRunning) while (!WindowShouldClose() && gameRunning) { step(); @@ -90,6 +81,7 @@ void StateService::handleMusic() void StateService::step() { InputService::getInstance().checkKeys(); + InputService::getInstance().checkMousePos(); handleMusic(); currentScreen->draw(); From c0bfe9bd13d2a7e98c6250a2f33e88dec38d268a Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Wed, 18 Dec 2024 21:57:36 +0100 Subject: [PATCH 10/12] [Updated] Enemies now track player movement, and the gun rotates appropriately --- .vscode/settings.json | 6 +++- html/game.html | 56 ++++++++++++++++++++++++++++++ include/entities/Bullet.h | 2 +- include/entities/Enemy.h | 1 + include/entities/Game.h | 27 +++++++++++++-- scripts/build_wasm.sh | 2 +- src/entities/Enemy.cpp | 70 ++++++++++++++++++++++++++++++++------ src/screens/MainScreen.cpp | 19 +++++++++-- 8 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 html/game.html diff --git a/.vscode/settings.json b/.vscode/settings.json index 035ee7a..aed8e9a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -55,6 +55,10 @@ "format": "cpp", "text_encoding": "cpp", "ratio": "cpp", - "cinttypes": "cpp" + "cinttypes": "cpp", + "__verbose_abort": "cpp", + "cfenv": "cpp", + "cstring": "cpp", + "fstream": "cpp" } } \ No newline at end of file diff --git a/html/game.html b/html/game.html new file mode 100644 index 0000000..a6ee798 --- /dev/null +++ b/html/game.html @@ -0,0 +1,56 @@ + + + + + + + raylib web game + + + + + + +

+ + + + + \ No newline at end of file diff --git a/include/entities/Bullet.h b/include/entities/Bullet.h index db9f976..1c44314 100644 --- a/include/entities/Bullet.h +++ b/include/entities/Bullet.h @@ -27,7 +27,7 @@ public: void draw() { - DrawCircleGradient(position.x, position.y, size, color, BLACK); + DrawCircleGradient(position.x, position.y, size, color, ORANGE); } bool getHasCollided() diff --git a/include/entities/Enemy.h b/include/entities/Enemy.h index db80e7d..a3c4d38 100644 --- a/include/entities/Enemy.h +++ b/include/entities/Enemy.h @@ -82,6 +82,7 @@ private: float shotsPerSecond = 8.0f; double lastShot; bool playerSpotted = false; + float aimSpeed = 2.0f; // How quickly the enemy can angle its gun to match the player (and to return to default) void walk(); void checkForCollision(); diff --git a/include/entities/Game.h b/include/entities/Game.h index 0de6b30..a4b89ca 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -89,14 +89,15 @@ public: CollectableService::getInstance().collectableLogic(); EnemyService::getInstance().enemyLogic(); - auto& player = PlayerService::getInstance().getPlayer(); + auto &player = PlayerService::getInstance().getPlayer(); player.logic(); if (player.getTexture().getPosition().x > (StateService::getInstance().getScreenSize().x / 2.0f) && player.getTexture().getPosition().x + (StateService::getInstance().getScreenSize().x / 2.0f) < background.getBackgroundSize().x) { GameService::getInstance().getCamera().offset = {(StateService::getInstance().getScreenSize().x / 2.0f) + ((StateService::getInstance().getScreenSize().x / 2.0f) - player.getTexture().getPosition().x), StateService::getInstance().getScreenSize().y / 2.0f}; } - if(player.isFinishReached()) { + if (player.isFinishReached()) + { StateService::getInstance().setScreen(EScreen::VICTORY); } } @@ -124,7 +125,9 @@ public: else if (GameService::getInstance().isGameOver()) { drawGameOver(); - } else { + } + else + { drawCrosshair(); } EndDrawing(); @@ -154,9 +157,16 @@ private: void initPauseScreen() { +#if defined(PLATFORM_WEB) + const int TOP_GAP = 100; + const int BOTTOM_GAP = 100; + const int AMNT_OF_BUTTONS = 2; +#else const int TOP_GAP = 100; const int BOTTOM_GAP = 50; const int AMNT_OF_BUTTONS = 4; +#endif + const int TOTAL_SPACE = GetScreenHeight() - (TOP_GAP + BOTTOM_GAP); const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; @@ -166,13 +176,24 @@ private: pauseScreenButtons.push_back(txt); } pauseScreenButtons[0].setColor(RED); + +#if defined(PLATFORM_WEB) + pauseScreenButtons[1].setText("Quit to Main Menu"); +#else pauseScreenButtons[1].setText("Save Game"); pauseScreenButtons[2].setText("Quit to Main Menu"); pauseScreenButtons[3].setText("Quit to Desktop"); +#endif for (int i = 0; i < AMNT_OF_BUTTONS; i++) { +#if defined(PLATFORM_WEB) + pauseScreenButtons[i].setPosition({(float)(GetScreenWidth() - pauseScreenButtons[i].getDimensions().x) / 2.0f, (float)TOP_GAP + SPACE_PER_ITEM * i + SPACE_PER_ITEM / 2.0f}); + +#else pauseScreenButtons[i].setPosition({(float)(GetScreenWidth() - pauseScreenButtons[i].getDimensions().x) / 2.0f, (float)TOP_GAP + SPACE_PER_ITEM * i}); + +#endif } } diff --git a/scripts/build_wasm.sh b/scripts/build_wasm.sh index 5227541..79c09b7 100755 --- a/scripts/build_wasm.sh +++ b/scripts/build_wasm.sh @@ -3,7 +3,7 @@ # emcc $(find src/ -name "*.cpp") -o dist/game.js -Wno-missing-braces -I./include -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=268435456 -s MAXIMUM_MEMORY=4294967296 -s --preload-file assets@/assets -s USE_GLFW=3 -DPLATFORM_WEB -emcc $(find src/ -name "*.cpp") -o dist/game.js -Wno-missing-braces -I./include -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=536870912 -s MAXIMUM_MEMORY=4294967296 -s --preload-file assets@/assets -s USE_GLFW=3 -s ASSERTIONS=2 -DPLATFORM_WEB +emcc $(find src/ -name "*.cpp") -o dist/game.js -O3 -Wno-missing-braces -I./include -I/app/libs/raylib/build/raylib/include -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=536870912 -s MAXIMUM_MEMORY=4294967296 -s --preload-file assets@/assets -s USE_GLFW=3 -s ASSERTIONS=2 -DPLATFORM_WEB # -I./include # -L$LD_LIBRARY_PATH # -I. diff --git a/src/entities/Enemy.cpp b/src/entities/Enemy.cpp index cffc960..3ab3b08 100644 --- a/src/entities/Enemy.cpp +++ b/src/entities/Enemy.cpp @@ -2,6 +2,7 @@ #include "services/PlayerService.h" #include "services/MapService.h" #include "services/BulletService.h" +#include void Enemy::logic() { @@ -74,10 +75,10 @@ std::vector Enemy::getCorners() int tileSize = MapService::getInstance().getMap().getTileSize(); // Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); // Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i leftCenter = Vector2i((int)left / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + Vector2i leftCenter = Vector2i((int)(left -(spriteAnimation.y == Left ? gunLeft.getSize().x*(2.0f/3.0f) : 0.0f)) / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); // Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); // Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i rightCenter = Vector2i((int)right / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + Vector2i rightCenter = Vector2i((int)(right+ (spriteAnimation.y == Right ? gunRight.getSize().x*(2.0f/3.0f) : 0.0f)) / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side @@ -158,15 +159,29 @@ void Enemy::detectPlayer() if (this->health > 0) { bool spotted = false; + Vector2 playerCenter = {player.getTexture().getPosition().x + player.getTexture().getSize().x / 2.0f, player.getTexture().getPosition().y + player.getTexture().getSize().y / 2.0f}; + Vector2 enemyCenter = {texture.getPosition().x + texture.getSize().x / 2.0f, texture.getPosition().y + texture.getSize().y / 2.0f}; if (this->spriteAnimation.y == Left && this->texture.getPosition().x - player.getTexture().getPosition().x <= spottingRange && this->texture.getPosition().x - player.getTexture().getPosition().x > 55 && this->texture.getPosition().y + this->texture.getSize().y >= player.getTexture().getPosition().y) { spotted = true; + float gunRotationLeft = atan2(playerCenter.y - enemyCenter.y, + enemyCenter.x - playerCenter.x); + float target = -gunRotationLeft * 180 / M_PI; + // float delta = target - this->gunLeft.getRotation(); + // float sign = std::copysign(1.0f, delta); + this->gunLeft.setRotation(std::min(this->gunLeft.getRotation() + aimSpeed, target)); } else if (spriteAnimation.y == Right && player.getTexture().getPosition().x - this->texture.getPosition().x <= spottingRange && player.getTexture().getPosition().x - this->texture.getPosition().x > 55 && this->texture.getPosition().y + this->texture.getSize().y >= player.getTexture().getPosition().y) { spotted = true; + float gunRotationRight = atan2(playerCenter.y - enemyCenter.y, + playerCenter.x - enemyCenter.x); + float target = gunRotationRight * 180 / M_PI; + + // this->gunRight.setRotation(gunRotationRight * 180 / M_PI); + this->gunRight.setRotation(std::max(this->gunRight.getRotation() - aimSpeed, target)); } /** Don't set it again if the detection continues */ @@ -179,6 +194,9 @@ void Enemy::detectPlayer() else if (!spotted) { this->playerSpotted = false; + /** Slowly ease the gun back to default position */ + this->gunLeft.setRotation(std::max(this->gunLeft.getRotation() - aimSpeed/2.0f, 0.0f)); + this->gunRight.setRotation(std::min(this->gunRight.getRotation() + aimSpeed/2.0f, 0.0f)); } } } @@ -189,25 +207,57 @@ void Enemy::shoot() /** Stop shooting if the player is dead (and falling out of the map) */ if (player.getHealth() > 0 && playerSpotted && GetTime() - this->lastShot >= (1 / this->shotsPerSecond)) { + this->lastShot = GetTime(); // Play shooting sound AssetService::getInstance().playSound(ESound::SHOOT); - if ((player.getTexture().getPosition().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) || - (player.getTexture().getPosition().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left)) - { + float offsetDistance = this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3; + Vector2 playerCenter = {player.getTexture().getPosition().x + player.getTexture().getSize().x / 2.0f, player.getTexture().getPosition().y + player.getTexture().getSize().y / 2.0f}; + Vector2 enemyCenter = {texture.getPosition().x + texture.getSize().x / 2.0f, texture.getPosition().y + texture.getSize().y / 2.0f}; + - float angleShot = atan2(player.getTexture().getPosition().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), - player.getTexture().getPosition().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + // if (spriteAnimation.y == Left) + // { - /** Making the bullet originate from the gun's mouth */ - float offsetDistance = this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3; + float angleShot = atan2(playerCenter.y - enemyCenter.y, + playerCenter.x - enemyCenter.x); + + // float gunRotationRadians = this->gunLeft.getRotation() * (M_PI / 180.0f); // Convert degrees to radians float offsetX = cos(angleShot) * offsetDistance; float offsetY = sin(angleShot) * offsetDistance; Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot); - } + // } + + // if ((player.getTexture().getPosition().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) || + // (player.getTexture().getPosition().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left)) + // { + + // // float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + // // GameService::getInstance().getMouseWorldPos().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + // // this->gunRight.setRotation(gunRotationRight * 180 / M_PI); + + // float angleShot = atan2(player.getTexture().getPosition().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), + // player.getTexture().getPosition().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); + + // // this->gunRight.setRotation(angleShot * 180 / M_PI); + // // this->gunLeft.setRotation(-angleShot * 180 / M_PI); + // /** Making the bullet originate from the gun's mouth */ + // float offsetDistance = this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3; + // float offsetX = cos(angleShot) * offsetDistance; + // float offsetY = sin(angleShot) * offsetDistance; + // Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, + // this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; + + // // BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot); + // } + // else + // { + // // this->gunRight.setRotation(0); + // // this->gunLeft.setRotation(0); + // } } } \ No newline at end of file diff --git a/src/screens/MainScreen.cpp b/src/screens/MainScreen.cpp index 1371361..d13339e 100644 --- a/src/screens/MainScreen.cpp +++ b/src/screens/MainScreen.cpp @@ -6,9 +6,16 @@ MainScreen::MainScreen() { +#if defined(PLATFORM_WEB) + const int TOP_GAP = 100; + const int BOTTOM_GAP = 100; + const int AMNT_OF_BUTTONS = 2; +#else const int TOP_GAP = 100; const int BOTTOM_GAP = 50; const int AMNT_OF_BUTTONS = 4; +#endif + const int TOTAL_SPACE = GetScreenHeight() - (TOP_GAP + BOTTOM_GAP); const int SPACE_PER_ITEM = TOTAL_SPACE / AMNT_OF_BUTTONS; @@ -18,13 +25,22 @@ MainScreen::MainScreen() buttons.push_back(txt); } buttons[0].setColor(RED); +#if defined(PLATFORM_WEB) + buttons[1].setText("tutorial"); +#else buttons[1].setText("load game"); buttons[2].setText("tutorial"); buttons[3].setText("quit"); +#endif for (int i = 0; i < AMNT_OF_BUTTONS; i++) { +#if defined(PLATFORM_WEB) + buttons[i].setPosition({(float)(GetScreenWidth() - buttons[i].getDimensions().x) / 2.0f, (float)TOP_GAP + SPACE_PER_ITEM * i + SPACE_PER_ITEM / 2.0f}); + +#else buttons[i].setPosition({(float)(GetScreenWidth() - buttons[i].getDimensions().x) / 2.0f, (float)TOP_GAP + SPACE_PER_ITEM * i}); +#endif } /** Will be ignored if already active (like when coming from the TitleScreen) */ @@ -45,7 +61,6 @@ void MainScreen::logic() clickLogic(); } - void MainScreen::mouseLogic() { for (int i = 0; i < buttons.size(); i++) @@ -78,7 +93,7 @@ void MainScreen::keyboardLogic() { buttons[hoveredButton].setColor(WHITE); /** Move up or down depending on the pressed button. +4 is used so that -1 becomes 3 (the last button) */ - hoveredButton = ((hoveredButton - (InputService::getInstance().isKeyPressed(KEY_S) ? -1 : 1)) + 4) % 4; + hoveredButton = ((hoveredButton - (InputService::getInstance().isKeyPressed(KEY_S) ? -1 : 1)) + buttons.size()) % buttons.size(); buttons[hoveredButton].setColor(RED); } } From 33f6504b6c7801d20c9ac70a297fc6f88dc089e1 Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Thu, 19 Dec 2024 22:40:59 +0100 Subject: [PATCH 11/12] [Added] Ability to serve the project using nginx on localhost:4600 --- Dockerfile | 37 ++++++++++++- README.md | 39 +++++++++++++- docker-compose.yml | 13 +++++ html/game.html | 56 ------------------- html/index.html | 114 +++++++++++++++++++++++++++++++++++++++ include/entities/Crate.h | 2 +- include/entities/Game.h | 2 + include/entities/Jelly.h | 4 +- src/entities/Enemy.cpp | 73 +++++-------------------- src/entities/PBullet.cpp | 1 + 10 files changed, 220 insertions(+), 121 deletions(-) create mode 100644 docker-compose.yml delete mode 100644 html/game.html create mode 100644 html/index.html diff --git a/Dockerfile b/Dockerfile index 1eab433..2de1b50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,4 +63,39 @@ RUN cd libs/raylib/src && \ emar rcs libraylib.a rcore.o rshapes.o rtextures.o rtext.o rmodels.o utils.o raudio.o && \ cd ../.. -CMD ["bash"] +# CMD ["bash"] + +FROM development as build + +RUN mkdir dist html src include assets + +COPY include include +COPY src src +COPY html html +COPY assets assets + +# CMD ["tail", "-f", "/dev/null"] +# Run the production build command to generate game.js and game.wasm +RUN source /app/libs/emsdk/emsdk_env.sh && \ + emcc $(find src/ -name "*.cpp") -o dist/game.js -O3 -Wno-missing-braces \ + -I./include -I/app/libs/raylib/build/raylib/include \ + -L/app/libs/raylib/src /app/libs/raylib/src/libraylib.a \ + -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=536870912 -s MAXIMUM_MEMORY=4294967296 \ + -s --preload-file assets@/assets -s USE_GLFW=3 -s ASSERTIONS=2 -DPLATFORM_WEB + +# CMD ["tail", "-f", "/dev/null"] + +FROM nginx:alpine as production + +# Remove the default nginx index.html +RUN rm -rf /usr/share/nginx/html/* + +# Copy the generated files from the production stage into the Nginx html directory +COPY --from=build /app/dist/ /usr/share/nginx/html/ +COPY --from=build /app/html/index.html /usr/share/nginx/html/ + +# Expose port 80 to allow Nginx to serve the files +EXPOSE 80 + +# Start Nginx server +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/README.md b/README.md index 0d2989a..e1aca3d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,39 @@ # WebJelly-Rescue -Jelly Rescue, but now for the web! +[Jelly Rescue](https://github.com/Ninjaws/JellyGame), but now for the web! + +## Installation +### Install Docker +- On Windows and Mac +``` +Install Docker Desktop +Windows: https://docs.docker.com/desktop/install/windows-install/ +Mac: https://docs.docker.com/desktop/install/mac-install/ +``` +- On Linux +```bash +# For Debian-based systems +sudo apt install -y docker.io +sudo apt install docker-compose + +#For Arch-based systems +sudo pacman -S docker.io +sudo pacman -S docker-compose + +# Making Docker run now and on startup +sudo systemctl start docker +sudo systemctl enable docker + +# Using the CLI without sudo +sudo usermod -aG docker $USER +sudo reboot +``` + +## How to run +- Navigate to the root of the project +- Open a terminal +- Start the container: +```bash +docker compose up #(might take a few minutes to build) +``` +- Visit https://localhost:4600 to play the game locally +- Or visit play it live [here!](https://jelly.ianvink.nl) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e3345e3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + frontend: + build: + context: . + dockerfile: Dockerfile + target: production + image: jelly-rescue-prod + container_name: jelly-rescue-prod-cont + ports: + - "4600:80" + restart: always + cpu_period: 100000 # Default is 100,000 microseconds (100ms) + cpu_quota: 30000 # 5% of the total CPU time \ No newline at end of file diff --git a/html/game.html b/html/game.html deleted file mode 100644 index a6ee798..0000000 --- a/html/game.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - raylib web game - - - - - - -

- - - - - \ No newline at end of file diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..728fa2a --- /dev/null +++ b/html/index.html @@ -0,0 +1,114 @@ + + + + + + + Jelly Rescue + + + + + +

+ + +
+

+ + + + + diff --git a/include/entities/Crate.h b/include/entities/Crate.h index c9f86d5..3e9ccb5 100644 --- a/include/entities/Crate.h +++ b/include/entities/Crate.h @@ -43,7 +43,7 @@ public: private: TextureWrapper texture; - bool hit; // Whether a PBullet has hit the crate + bool hit = false; // Whether a PBullet has hit the crate }; #endif \ No newline at end of file diff --git a/include/entities/Game.h b/include/entities/Game.h index a4b89ca..d0360c8 100644 --- a/include/entities/Game.h +++ b/include/entities/Game.h @@ -14,6 +14,7 @@ #include "services/BulletService.h" #include "entities/Text.h" #include +#include class Game { @@ -49,6 +50,7 @@ public: this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15, 15}, {0, 0}, {0, 0, 15, 15}); CollectableService::getInstance().initCollectables(); + std::cout << "Initting" << std::endl; initHud(); initPauseScreen(); diff --git a/include/entities/Jelly.h b/include/entities/Jelly.h index 7d42aa0..b221a37 100644 --- a/include/entities/Jelly.h +++ b/include/entities/Jelly.h @@ -10,13 +10,13 @@ class Jelly public: Jelly(){ this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY),{32,32}); - AssetService::getInstance().playSound(ESound::JELLY_FREED); + // AssetService::getInstance().playSound(ESound::JELLY_FREED); } Jelly(Vector2 startPos) { this->startPosition = startPos; this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::JELLY),{32,32},startPos); - AssetService::getInstance().playSound(ESound::JELLY_FREED); + // AssetService::getInstance().playSound(ESound::JELLY_FREED); } void logic() diff --git a/src/entities/Enemy.cpp b/src/entities/Enemy.cpp index 3ab3b08..7c85ccc 100644 --- a/src/entities/Enemy.cpp +++ b/src/entities/Enemy.cpp @@ -73,23 +73,16 @@ std::vector Enemy::getCorners() float top = texture.getPosition().y + velocity.y; int tileSize = MapService::getInstance().getMap().getTileSize(); - // Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); - // Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i leftCenter = Vector2i((int)(left -(spriteAnimation.y == Left ? gunLeft.getSize().x*(2.0f/3.0f) : 0.0f)) / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); - // Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); - // Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); - Vector2i rightCenter = Vector2i((int)(right+ (spriteAnimation.y == Right ? gunRight.getSize().x*(2.0f/3.0f) : 0.0f)) / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + Vector2i leftCenter = Vector2i((int)(left - (spriteAnimation.y == Left ? gunLeft.getSize().x * (2.0f / 3.0f) : 0.0f)) / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); + Vector2i rightCenter = Vector2i((int)(right + (spriteAnimation.y == Right ? gunRight.getSize().x * (2.0f / 3.0f) : 0.0f)) / tileSize, (int)(bottom - texture.getSize().y / 2.0) / tileSize); - Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side - Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side + Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); + Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); std::vector corners; corners.push_back(leftCenter); corners.push_back(rightCenter); - // corners.push_back(leftBottom); - // corners.push_back(rightTop); - // corners.push_back(rightBottom); corners.push_back(bottomLeft); corners.push_back(bottomRight); @@ -105,7 +98,6 @@ void Enemy::animate() Rectangle sourceRect = this->texture.getSourceRect(); sourceRect.x = spriteAnimation.x * this->texture.getSize().x; sourceRect.y = spriteAnimation.y * this->texture.getSize().y; - // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); this->texture.setSourceRect(sourceRect); prevFrame = GetTime(); } @@ -154,7 +146,6 @@ void Enemy::fall() void Enemy::detectPlayer() { - // playerSpotted = false; Player &player = PlayerService::getInstance().getPlayer(); if (this->health > 0) { @@ -168,8 +159,6 @@ void Enemy::detectPlayer() float gunRotationLeft = atan2(playerCenter.y - enemyCenter.y, enemyCenter.x - playerCenter.x); float target = -gunRotationLeft * 180 / M_PI; - // float delta = target - this->gunLeft.getRotation(); - // float sign = std::copysign(1.0f, delta); this->gunLeft.setRotation(std::min(this->gunLeft.getRotation() + aimSpeed, target)); } else if (spriteAnimation.y == Right && player.getTexture().getPosition().x - this->texture.getPosition().x <= spottingRange && @@ -179,8 +168,6 @@ void Enemy::detectPlayer() float gunRotationRight = atan2(playerCenter.y - enemyCenter.y, playerCenter.x - enemyCenter.x); float target = gunRotationRight * 180 / M_PI; - - // this->gunRight.setRotation(gunRotationRight * 180 / M_PI); this->gunRight.setRotation(std::max(this->gunRight.getRotation() - aimSpeed, target)); } @@ -195,8 +182,8 @@ void Enemy::detectPlayer() { this->playerSpotted = false; /** Slowly ease the gun back to default position */ - this->gunLeft.setRotation(std::max(this->gunLeft.getRotation() - aimSpeed/2.0f, 0.0f)); - this->gunRight.setRotation(std::min(this->gunRight.getRotation() + aimSpeed/2.0f, 0.0f)); + this->gunLeft.setRotation(std::max(this->gunLeft.getRotation() - aimSpeed / 2.0f, 0.0f)); + this->gunRight.setRotation(std::min(this->gunRight.getRotation() + aimSpeed / 2.0f, 0.0f)); } } } @@ -215,49 +202,15 @@ void Enemy::shoot() float offsetDistance = this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3; Vector2 playerCenter = {player.getTexture().getPosition().x + player.getTexture().getSize().x / 2.0f, player.getTexture().getPosition().y + player.getTexture().getSize().y / 2.0f}; Vector2 enemyCenter = {texture.getPosition().x + texture.getSize().x / 2.0f, texture.getPosition().y + texture.getSize().y / 2.0f}; - - // if (spriteAnimation.y == Left) - // { + float angleShot = atan2(playerCenter.y - enemyCenter.y, + playerCenter.x - enemyCenter.x); - float angleShot = atan2(playerCenter.y - enemyCenter.y, - playerCenter.x - enemyCenter.x); + float offsetX = cos(angleShot) * offsetDistance; + float offsetY = sin(angleShot) * offsetDistance; + Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, + this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; - // float gunRotationRadians = this->gunLeft.getRotation() * (M_PI / 180.0f); // Convert degrees to radians - float offsetX = cos(angleShot) * offsetDistance; - float offsetY = sin(angleShot) * offsetDistance; - Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, - this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; - - BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot); - // } - - // if ((player.getTexture().getPosition().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) || - // (player.getTexture().getPosition().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left)) - // { - - // // float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), - // // GameService::getInstance().getMouseWorldPos().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); - // // this->gunRight.setRotation(gunRotationRight * 180 / M_PI); - - // float angleShot = atan2(player.getTexture().getPosition().y - (this->texture.getPosition().y + this->texture.getSize().y / 2.0), - // player.getTexture().getPosition().x - (this->texture.getPosition().x + this->texture.getSize().x / 2.0)); - - // // this->gunRight.setRotation(angleShot * 180 / M_PI); - // // this->gunLeft.setRotation(-angleShot * 180 / M_PI); - // /** Making the bullet originate from the gun's mouth */ - // float offsetDistance = this->gunLeft.getSize().x - this->gunLeft.getSize().x / 3; - // float offsetX = cos(angleShot) * offsetDistance; - // float offsetY = sin(angleShot) * offsetDistance; - // Vector2 bulletStartLocation = {this->texture.getPosition().x + this->texture.getSize().x / 2.0f + offsetX, - // this->texture.getPosition().y + this->texture.getSize().y / 2.0f + offsetY}; - - // // BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot); - // } - // else - // { - // // this->gunRight.setRotation(0); - // // this->gunLeft.setRotation(0); - // } + BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot); } } \ No newline at end of file diff --git a/src/entities/PBullet.cpp b/src/entities/PBullet.cpp index 936d370..9772e96 100644 --- a/src/entities/PBullet.cpp +++ b/src/entities/PBullet.cpp @@ -11,6 +11,7 @@ void PBullet::objCollision() this->position.y + this->size > crate.getObject().getPosition().y && this->position.y-this->size < crate.getObject().getPosition().y + crate.getObject().getSize().y) { crate.setHit(true); AssetService::getInstance().playSound(ESound::CRATE_BREAK); + AssetService::getInstance().playSound(ESound::JELLY_FREED); this->hasCollided = true; } } From f87739864922aa621da332018f2ba1a96bc43912 Mon Sep 17 00:00:00 2001 From: Ninjaws Date: Thu, 19 Dec 2024 22:44:41 +0100 Subject: [PATCH 12/12] [Added] Gitea workflows for CI/CD --- .gitea/workflows/deploy.yaml | 14 ++++++++++++++ .gitea/workflows/testing.yaml | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .gitea/workflows/deploy.yaml create mode 100644 .gitea/workflows/testing.yaml diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml new file mode 100644 index 0000000..013c05f --- /dev/null +++ b/.gitea/workflows/deploy.yaml @@ -0,0 +1,14 @@ +name: Jelly Rescue Deployment +run-name: ${{ gitea.actor }} is deploying Jelly Rescue +on: + push: + branches: + - main + +jobs: + JellyRescue Runner: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - run: docker compose up -d --build diff --git a/.gitea/workflows/testing.yaml b/.gitea/workflows/testing.yaml new file mode 100644 index 0000000..18267d8 --- /dev/null +++ b/.gitea/workflows/testing.yaml @@ -0,0 +1,13 @@ +name: Jelly Rescue Testing +run-name: ${{ gitea.actor }} is testing Jelly Rescue +on: [push] + +jobs: + JellyRescue Runner: + runs-on: ubuntu-latest + steps: + - run: docker --version + - run: docker compose version + - name: Check out repository code + uses: actions/checkout@v4 + # Run tests