diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index a4fc976..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": {
@@ -29,7 +30,7 @@
"extensions": [
"esbenp.prettier-vscode",
"ms-vscode.cpptools-extension-pack",
- "jeff-hykin.better-cpp-syntax"
+ "vadimcn.vscode-lldb"
]
}
},
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
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/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..aed8e9a
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,64 @@
+{
+ "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",
+ "ratio": "cpp",
+ "cinttypes": "cpp",
+ "__verbose_abort": "cpp",
+ "cfenv": "cpp",
+ "cstring": "cpp",
+ "fstream": "cpp"
+ }
+}
\ 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..2de1b50 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/*
@@ -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/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/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..d8d892a 100644
--- a/include/entities/Background.h
+++ b/include/entities/Background.h
@@ -8,38 +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() {}
+ void draw()
+ {
+ for (int i = 0; i < textures.size(); i++)
+ {
+ TextureWrapper texture = textures[i];
+ DrawTextureRec(texture.getTexture(), texture.getSourceRect(), texture.getPosition(), WHITE);
}
- ~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);
- }
- }
- 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
new file mode 100644
index 0000000..1c44314
--- /dev/null
+++ b/include/entities/Bullet.h
@@ -0,0 +1,57 @@
+#ifndef BULLET_H
+#define BULLET_H
+
+#include "raylib.h"
+#include "entities/TextureWrapper.h"
+#include "entities/Vector2i.h"
+#include
+
+class Bullet
+{
+public:
+ Bullet() {}
+ Bullet(Vector2 pos, float direction)
+ {
+ this->position = pos;
+ this->direction = direction;
+ }
+
+ void logic()
+ {
+ move();
+ hasCollided = false;
+ objCollision();
+ envCollision();
+ // // detectCollision();
+ }
+
+ void draw()
+ {
+ DrawCircleGradient(position.x, position.y, size, color, ORANGE);
+ }
+
+ bool getHasCollided()
+ {
+ return this->hasCollided;
+ }
+
+protected:
+ Color color = RED;
+ Vector2 position;
+ int size = 7;
+ bool hasCollided = false;
+
+private:
+ float direction;
+ float velocity = 15.0f;
+
+ void move()
+ {
+ position.x += velocity * cos(direction);
+ position.y += velocity * sin(direction);
+ }
+ void envCollision();
+ virtual void objCollision() = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/include/entities/Crate.h b/include/entities/Crate.h
new file mode 100644
index 0000000..3e9ccb5
--- /dev/null
+++ b/include/entities/Crate.h
@@ -0,0 +1,49 @@
+#ifndef CRATE_H
+#define CRATE_H
+
+#include "services/AssetService.h"
+#include "entities/TextureWrapper.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 = false; // 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
new file mode 100644
index 0000000..9d76b9b
--- /dev/null
+++ b/include/entities/EBullet.h
@@ -0,0 +1,22 @@
+#ifndef EBULLET_H
+#define EBULLET_H
+
+#include "services/AssetService.h"
+#include "entities/Bullet.h"
+#include
+
+class EBullet : public Bullet
+{
+public:
+ EBullet() {}
+ EBullet(Vector2 pos, float angle) : Bullet(pos, angle)
+ {
+ this->color = RED;
+ }
+
+private:
+ uint8_t damage = 1;
+ void objCollision() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/include/entities/Enemy.h b/include/entities/Enemy.h
new file mode 100644
index 0000000..a3c4d38
--- /dev/null
+++ b/include/entities/Enemy.h
@@ -0,0 +1,98 @@
+#ifndef ENEMY_H
+#define ENEMY_H
+
+#include "raylib.h"
+#include "services/AssetService.h"
+#include "entities/TextureWrapper.h"
+#include "entities/Vector2i.h"
+#include
+#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();
+ 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);
+ 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();
+
+ void draw()
+ {
+ drawGun();
+ drawHealthBar();
+ DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), WHITE);
+ }
+
+ TextureWrapper &getObject()
+ {
+ return this->texture;
+ }
+
+ uint8_t getHealth()
+ {
+ return this->health;
+ }
+
+ void takeDamage()
+ {
+ this->health = std::max(health - 1, 0);
+ }
+
+private:
+ TextureWrapper texture;
+ TextureWrapper gunLeft;
+ TextureWrapper gunRight;
+ 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;
+ 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
+ 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();
+ 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/Flag.h b/include/entities/Flag.h
new file mode 100644
index 0000000..7215236
--- /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 71491ce..d0360c8 100644
--- a/include/entities/Game.h
+++ b/include/entities/Game.h
@@ -7,19 +7,28 @@
#include "services/AudioService.h"
#include "services/MapService.h"
#include "services/InputService.h"
-#include "entities/Player.h"
+#include "services/GameService.h"
+#include "services/CollectableService.h"
+#include "services/EnemyService.h"
+#include "services/PlayerService.h"
+#include "services/BulletService.h"
+#include "entities/Text.h"
+#include
+#include
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().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});
+ 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), {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;
wrappers.push_back(wrapper1);
@@ -35,56 +44,347 @@ 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);
+ BulletService::getInstance().initBullets();
+ PlayerService::getInstance().initPlayer();
+ this->crosshair = TextureWrapper(AssetService::getInstance().getSprite(ESprite::CROSSHAIR), {15, 15}, {0, 0}, {0, 0, 15, 15});
- this->player = Player();
- this->player.getTexture().setPosition({200,350});
+ CollectableService::getInstance().initCollectables();
+ std::cout << "Initting" << std::endl;
- InputService::getInstance().setKeysToWatch({KEY_SPACE, KEY_A, KEY_D}, {MOUSE_BUTTON_LEFT});
+ 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);
AudioService::getInstance().playMusic();
}
+
~Game()
{
}
void logic()
{
- // 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};
+ this->crosshair.setPosition(InputService::getInstance().getMousePos());
+ if (GameService::getInstance().isPaused())
+ {
+ pausedLogic();
+ return;
+ }
+ else if (GameService::getInstance().isGameOver())
+ {
+ gameOverLogic();
+ return;
+ }
+
+ if (InputService::getInstance().isKeyPressed(KEY_ENTER))
+ {
+ GameService::getInstance().setPaused(true);
+ return;
+ }
+
+ BulletService::getInstance().bulletLogic();
+ CollectableService::getInstance().collectableLogic();
+ EnemyService::getInstance().enemyLogic();
+
+ 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())
+ {
+ StateService::getInstance().setScreen(EScreen::VICTORY);
}
}
void draw()
{
BeginDrawing();
- BeginMode2D(this->camera);
+ BeginMode2D(GameService::getInstance().getCamera());
this->background.draw();
+
+ BulletService::getInstance().drawBullets();
this->map.draw();
- this->player.draw();
+ CollectableService::getInstance().drawCollectables();
+ EnemyService::getInstance().drawEnemies();
+ PlayerService::getInstance().getPlayer().draw();
EndMode2D();
+
+ drawHud();
+ if (GameService::getInstance().isPaused())
+ {
+ drawPaused();
+ }
+ else if (GameService::getInstance().isGameOver())
+ {
+ drawGameOver();
+ }
+ else
+ {
+ drawCrosshair();
+ }
EndDrawing();
}
private:
- Camera2D camera;
Background background;
Map map;
- Player player;
- // std::vector enemies
- // std::vector crates
- // std::vector playerBullets
- // std::vector enemyBullets
- // std::vector jellies
- // std::vector ammo
- // std::vector healthpacks
- // Powerup powerup
+ TextureWrapper crosshair;
+ std::vector enemies;
+
+ std::vector pauseScreenButtons;
+ int hoveredPauseScreenButton = 0;
+
+ Text gameOverText;
+ std::vector gameOverScreenButtons;
+ int hoveredGameOverScreenButton = 0;
+
+ 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 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;
+
+ 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);
+
+#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
+ }
+ }
+
+ void initGameOverScreen()
+ {
+ 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;
+ 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::SANSATION, 30, 0, BLACK);
+ gameOverScreenButtons.push_back(txt);
+ }
+ 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});
+ }
+ }
+
+ 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(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);
+ }
+
+ if (InputService::getInstance().isKeyPressed(KEY_ENTER))
+ {
+ 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);
+ }
+ GameService::getInstance().setGameOver(false);
+ }
+ }
+
+ void drawPaused()
+ {
+ // 100, 100, 255, 60
+ 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++)
+ {
+ pauseScreenButtons[i].draw();
+ }
+ }
+ }
+
+ void drawGameOver()
+ {
+ DrawRectangle(0, 0, StateService::getInstance().getScreenSize().x, StateService::getInstance().getScreenSize().y, {100, 0, 0, 200});
+
+ gameOverText.draw();
+
+ for (int i = 0; i < gameOverScreenButtons.size(); i++)
+ {
+ gameOverScreenButtons[i].draw();
+ }
+ }
+
+ void drawCrosshair()
+ {
+ DrawTextureEx(
+ this->crosshair.getTexture(),
+ {InputService::getInstance().getMousePos().x - this->crosshair.getSize().x / 2.0f, InputService::getInstance().getMousePos().y - this->crosshair.getSize().y / 2.0f},
+ 0,
+ 1.2f,
+ RED);
+ }
+
+ void drawHud()
+ {
+ drawHudHealth();
+ drawHudBullets();
+ drawHudJellies();
+ }
+
+ void drawHudBullets()
+ {
+ Vector2i pixel_pos = Vector2i(5, 40);
+ Texture2D ammoSprite = AssetService::getInstance().getSprite(ESprite::HUD_AMMO);
+ Vector2i display_pos;
+ for (int i = 0; i < PlayerService::getInstance().getPlayer().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 < 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 < 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);
+ 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);
+ }
+ }
+
+ 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..b221a37
--- /dev/null
+++ b/include/entities/Jelly.h
@@ -0,0 +1,49 @@
+#ifndef JELLY_H
+#define JELLY_H
+
+#include "raylib.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/Map.h b/include/entities/Map.h
index 7aeece0..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
/**
@@ -18,12 +19,20 @@ 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;
}
+ std::vector> getColMap() {
+ 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;
@@ -41,7 +50,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/PBullet.h b/include/entities/PBullet.h
new file mode 100644
index 0000000..4dfa76f
--- /dev/null
+++ b/include/entities/PBullet.h
@@ -0,0 +1,20 @@
+#ifndef PBULLET_H
+#define PBULLET_H
+
+#include "services/AssetService.h"
+#include "entities/Bullet.h"
+
+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 eea1a0b..d4e0263 100644
--- a/include/entities/Player.h
+++ b/include/entities/Player.h
@@ -2,36 +2,54 @@
#define PLAYER_H
#include "raylib.h"
-#include
-#include "services/AssetService.h"
-#include "services/MapService.h"
-#include "services/InputService.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
+#include
+#include
+
+class PBullet;
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, 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()
{
}
void logic()
{
- gravity();
+ /** Fall out of the map when the jelly has been killed */
+ if(this->health == 0) {
+ fall();
+ return;
+ }
+ rotateGun();
+ shoot();
movement();
- handleCollision();
+ outofboundsCheck();
+ consumableCollision();
}
void draw()
{
- DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), WHITE);
+ drawGun();
+ DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), gainedPowerup ? RED : WHITE);
}
TextureWrapper &getTexture()
@@ -39,85 +57,80 @@ public:
return this->texture;
}
+ void setPosition(Vector2 pos)
+ {
+ this->texture.setPosition(pos);
+ }
+
+ int getAmmo()
+ {
+ return this->ammo;
+ }
+
+ uint8_t getHealth()
+ {
+ return this->health;
+ }
+
+ uint8_t getMaxHealth()
+ {
+ return this->max_health;
+ }
+
+ void takeDamage(uint8_t damage) {
+ this->health = std::max(health - damage, 0);
+ }
+
+ bool isFinishReached() {
+ return this->finishReached;
+ }
+
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; // 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
+ 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 gravity()
- {
- if (!groundCollision)
- {
- velocity.y += 0.6f;
- }
- else
- {
- // velocity.y = 0.0f;
- }
- }
+ 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;
+ bool finishReached = false;
- void movement()
- {
- velocity.x = 0.0f;
- if (InputService::getInstance().isKeyDown(KEY_A))
- {
- velocity.x -= 5.0f;
- }
- else if (InputService::getInstance().isKeyDown(KEY_D))
- {
- velocity.x += 5.0f;
- }
-
- if (InputService::getInstance().isKeyPressed(KEY_SPACE) && groundCollision)
- {
- velocity.y -= 7.2f;
- }
- }
-
- /**
- * 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()
- {
- 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 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/Powerup.h b/include/entities/Powerup.h
new file mode 100644
index 0000000..837996b
--- /dev/null
+++ b/include/entities/Powerup.h
@@ -0,0 +1,59 @@
+#ifndef POWERUP_H
+#define POWERUP_H
+
+#include "raylib.h"
+#include "services/AssetService.h"
+#include "entities/TextureWrapper.h"
+#include
+
+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
+ uint8_t currentFrame = 0; // Current frame of the animation
+ uint8_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/entities/Text.h b/include/entities/Text.h
index 21dd3b0..e667f11 100644
--- a/include/entities/Text.h
+++ b/include/entities/Text.h
@@ -3,46 +3,53 @@
#include "raylib.h"
#include "enums/EFont.h"
-#include
-#include
#include "services/AssetService.h"
+#include
-class Text {
- public:
- 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/entities/TextureWrapper.h b/include/entities/TextureWrapper.h
index accd1c8..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:
@@ -12,9 +11,9 @@ class TextureWrapper {
this->texture = texture;
this->size = size;
this->position = position;
+ this->sourceRect = sourceRect;
}
~TextureWrapper() {
-
}
Texture2D getTexture() {
@@ -33,6 +32,10 @@ class TextureWrapper {
return this->position;
}
+ float getRotation() {
+ return this->rotation;
+ }
+
void setSourceRect(Rectangle rect) {
this->sourceRect = rect;
}
@@ -41,28 +44,12 @@ class TextureWrapper {
this->position = position;
}
- void move(Vector2 deltaPos) {
- this->position = {this->position.x + deltaPos.x, this->position.y + deltaPos.y};
+ void setRotation(float rot) {
+ this->rotation = rot;
}
- /** 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;
+ void move(Vector2 deltaPos) {
+ this->position = {this->position.x + deltaPos.x, this->position.y + deltaPos.y};
}
private:
@@ -70,6 +57,7 @@ class TextureWrapper {
Rectangle sourceRect;
Vector2 size;
Vector2 position;
+ float rotation;
};
#endif
\ No newline at end of file
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/ESound.h b/include/enums/ESound.h
new file mode 100644
index 0000000..d70f1a4
--- /dev/null
+++ b/include/enums/ESound.h
@@ -0,0 +1,18 @@
+#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,
+ PLAYER_SPOTTED
+};
+
+#endif
\ No newline at end of file
diff --git a/include/enums/ESprite.h b/include/enums/ESprite.h
index 5e70281..77f4950 100644
--- a/include/enums/ESprite.h
+++ b/include/enums/ESprite.h
@@ -15,7 +15,9 @@ enum class ESprite {
POWERUP,
HUD_HEART,
HUD_HEART_EMPTY,
- HUD_AMMO
+ HUD_AMMO,
+ CROSSHAIR,
+ FLAG,
};
#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
new file mode 100644
index 0000000..600f790
--- /dev/null
+++ b/include/screens/VictoryScreen.h
@@ -0,0 +1,30 @@
+#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();
+ ~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/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/BulletService.h b/include/services/BulletService.h
new file mode 100644
index 0000000..ab9ee19
--- /dev/null
+++ b/include/services/BulletService.h
@@ -0,0 +1,57 @@
+#ifndef BULLET_SERVICE_H
+#define BULLET_SERVICE_H
+
+#include "raylib.h"
+#include "services/Service.h"
+#include "services/AssetService.h"
+#include "entities/Bullet.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/CollectableService.h b/include/services/CollectableService.h
new file mode 100644
index 0000000..94ef4d6
--- /dev/null
+++ b/include/services/CollectableService.h
@@ -0,0 +1,114 @@
+#ifndef COLLECTABLE_SERVICE_H
+#define COLLECTABLE_SERVICE_H
+
+#include "raylib.h"
+#include "services/Service.h"
+#include "entities/Vector2i.h"
+#include "entities/Crate.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:
+ void initCollectables()
+ {
+ initCrates();
+ initJellies();
+ initAmmoPacks();
+ initHealthPacks();
+ initPowerups();
+ initFlag();
+ }
+
+ void collectableLogic()
+ {
+ crateLogic();
+ jellyLogic();
+ ammoPackLogic();
+ healthPackLogic();
+ powerupLogic();
+ flag.logic();
+ }
+
+ void drawCollectables()
+ {
+ drawCrates();
+ drawJellies();
+ drawAmmoPacks();
+ drawHealthpacks();
+ drawPowerups();
+ flag.draw();
+ }
+
+ std::vector &getCrates()
+ {
+ return this->crates;
+ }
+
+ std::vector &getAmmoPacks()
+ {
+ return this->ammoPacks;
+ }
+
+ std::vector &getHealthPacks()
+ {
+ return this->healthPacks;
+ }
+
+ std::vector &getPowerups() {
+ return this->powerUps;
+ }
+
+ Flag& getFlag() {
+ return this->flag;
+ }
+
+ 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;
+ Flag flag;
+
+ 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
new file mode 100644
index 0000000..ee56a85
--- /dev/null
+++ b/include/services/EnemyService.h
@@ -0,0 +1,22 @@
+#ifndef ENEMY_SERVICE_H
+#define ENEMY_SERVICE_H
+
+#include "raylib.h"
+#include "services/Service.h"
+#include "entities/Enemy.h"
+#include
+
+class EnemyService : public Service
+{
+public:
+ void initEnemies();
+ void enemyLogic();
+ void drawEnemies();
+ std::vector &getEnemies();
+
+protected:
+private:
+ std::vector enemies;
+};
+
+#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..9b6b42a
--- /dev/null
+++ b/include/services/GameService.h
@@ -0,0 +1,77 @@
+#ifndef GAME_SERVICE_H
+#define GAME_SERVICE_H
+
+#include "raylib.h"
+#include "services/Service.h"
+#include "services/AudioService.h"
+#include "services/InputService.h"
+#include
+#include
+
+class GameService : public Service
+{
+public:
+ bool isPaused()
+ {
+ return this->paused;
+ }
+
+ bool isGameOver()
+ {
+ return this->gameOver;
+ }
+
+ 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;
+ }
+
+ Camera2D &getCamera()
+ {
+ return this->camera;
+ }
+
+ Vector2 getMouseWorldPos()
+ {
+ Vector2 mouseWorldPos = {InputService::getInstance().getMousePos().x + camera.target.x - camera.offset.x,
+ InputService::getInstance().getMousePos().y};
+ return mouseWorldPos;
+ }
+
+ Vector2 toWorldPos(Vector2i pixelPos)
+ {
+ Vector2 worldPos = {(float)pixelPos.x + camera.target.x - camera.offset.x, (float)pixelPos.y};
+ return worldPos;
+ }
+
+ void resetGame()
+ {
+ paused = false;
+ gameOver = false;
+ // playerBullets.clear();
+ }
+
+private:
+ bool paused = false;
+ bool gameOver = false;
+ Camera2D camera;
+};
+
+#endif
\ No newline at end of file
diff --git a/include/services/InputService.h b/include/services/InputService.h
index e7f537b..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
@@ -57,13 +57,26 @@ public:
for (auto button : watchedMouseButtons)
{
mouseButtonsPressed[button] = false;
+ mouseButtonsDown[button] = false;
if (IsMouseButtonPressed(button))
{
mouseButtonsPressed[button] = true;
}
+ if (IsMouseButtonDown(button))
+ {
+ mouseButtonsDown[button] = true;
+ }
}
}
+ void checkMousePos() {
+ this->mousePosition = GetMousePosition();
+ }
+
+ Vector2 getMousePos() {
+ return this->mousePosition;
+ }
+
bool isKeyPressed(KeyboardKey key)
{
return keysPressed[key];
@@ -78,22 +91,30 @@ public:
return mouseButtonsPressed[button];
}
+ bool isMouseButtonDown(MouseButton button) {
+ return mouseButtonsDown[button];
+ }
+
private:
+ Vector2 mousePosition {0,0};
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;
}
}
};
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/include/services/StateService.h b/include/services/StateService.h
index 01960b6..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
@@ -32,30 +33,23 @@ public:
* Start the game loop
*/
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;
-
Vector2 screenSize;
/**
* One step of the gameloop
*/
void step();
-
void handleMusic();
+
};
#endif
\ No newline at end of file
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/scripts/build_wasm.sh b/scripts/build_wasm.sh
index e921bfc..79c09b7 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 -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/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..7c85ccc
--- /dev/null
+++ b/src/entities/Enemy.cpp
@@ -0,0 +1,216 @@
+#include "entities/Enemy.h"
+#include "services/PlayerService.h"
+#include "services/MapService.h"
+#include "services/BulletService.h"
+#include
+
+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 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));
+ Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize));
+
+ std::vector corners;
+
+ corners.push_back(leftCenter);
+ corners.push_back(rightCenter);
+
+ 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;
+ 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()
+{
+ Player &player = PlayerService::getInstance().getPlayer();
+ 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;
+ 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(std::max(this->gunRight.getRotation() - aimSpeed, target));
+ }
+
+ /** 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;
+ /** 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));
+ }
+ }
+}
+
+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);
+
+ 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(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};
+
+ BulletService::getInstance().addEnemyBullet(bulletStartLocation, angleShot);
+ }
+}
\ No newline at end of file
diff --git a/src/entities/Map.cpp b/src/entities/Map.cpp
index c1ddcc5..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/PBullet.cpp b/src/entities/PBullet.cpp
new file mode 100644
index 0000000..9772e96
--- /dev/null
+++ b/src/entities/PBullet.cpp
@@ -0,0 +1,34 @@
+#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);
+ 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;
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/entities/Player.cpp b/src/entities/Player.cpp
new file mode 100644
index 0000000..f3c54af
--- /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"
+#include "services/MapService.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::min(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;
+ }
+ }
+
+ /** 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);
+ finishReached = 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/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/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/MainScreen.cpp b/src/screens/MainScreen.cpp
index 77f6293..d13339e 100644
--- a/src/screens/MainScreen.cpp
+++ b/src/screens/MainScreen.cpp
@@ -1,14 +1,21 @@
#include "screens/MainScreen.h"
+#include "raylib.h"
#include "services/AudioService.h"
#include "services/StateService.h"
-#include "raylib.h"
#include "services/InputService.h"
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) */
@@ -35,24 +51,16 @@ 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}, {});
}
void MainScreen::logic()
{
- // Vector2 mouseD = GetMouseDelta();
- // std::cout << mouseD.y <= 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))
{
@@ -81,18 +89,18 @@ 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)) + buttons.size()) % buttons.size();
buttons[hoveredButton].setColor(RED);
}
}
void MainScreen::clickLogic()
{
- if (InputService::getInstance().isKeyPressed(KEY_ENTER) || InputService::getInstance().isMouseButtonPressed(MOUSE_BUTTON_LEFT))
+ if (InputService::getInstance().isKeyPressed(KEY_ENTER))
{
std::string selectedButton = buttons[hoveredButton].getText();
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 86b7ea5..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() {
@@ -13,12 +12,12 @@ TutorialScreen::TutorialScreen() {
this->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/screens/VictoryScreen.cpp b/src/screens/VictoryScreen.cpp
new file mode 100644
index 0000000..3c366df
--- /dev/null
+++ b/src/screens/VictoryScreen.cpp
@@ -0,0 +1,119 @@
+#include "screens/VictoryScreen.h"
+#include "services/InputService.h"
+#include "services/AudioService.h"
+#include "services/CollectableService.h"
+#include
+#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 06039a9..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/");
@@ -17,9 +16,22 @@ 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 */
+ 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");
+ 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");
+ 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");
backgrounds[EBackground::GAME2] = loadTexture("Textures/GameBackground_2.jpg");
@@ -43,6 +55,8 @@ 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");
+ sprites[ESprite::FLAG] = loadTexture("Textures/flag.png");
}
Font AssetService::getFont(EFont font, int fontSize)
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/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 9c56ebf..9b523af 100644
--- a/src/services/StateService.cpp
+++ b/src/services/StateService.cpp
@@ -1,12 +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/VictoryScreen.h"
#include
-#include "services/AudioService.h"
-#include "services/InputService.h"
void StateService::setScreen(EScreen state)
{
@@ -24,6 +25,9 @@ void StateService::setScreen(EScreen state)
case EScreen::GAME:
currentScreen = std::make_unique();
break;
+ case EScreen::VICTORY:
+ currentScreen = std::make_unique();
+ break;
case EScreen::CLOSE:
gameRunning = false;
break;
@@ -44,17 +48,10 @@ void StateService::startGame()
InitAudioDevice();
// InitWindow(640,544, "Jelly Rescue");
InitWindow(screenSize.x, screenSize.y, "Jelly Rescue");
- SetTargetFPS(60);
-
- // #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
+ SetTargetFPS(45);
+ #if not defined(PLATFORM_WEB)
+ HideCursor();
+ #endif
setScreen(EScreen::TITLE);
@@ -62,7 +59,6 @@ void StateService::startGame()
emscripten_set_main_loop(StateService::staticStep, 0, 1);
#else
- // while (!WindowShouldClose() && instance->gameRunning)
while (!WindowShouldClose() && gameRunning)
{
step();
@@ -85,6 +81,7 @@ void StateService::handleMusic()
void StateService::step()
{
InputService::getInstance().checkKeys();
+ InputService::getInstance().checkMousePos();
handleMusic();
currentScreen->draw();