diff --git a/.gitmodules b/.gitmodules index a0c91fc..84e7c1b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,6 +14,9 @@ path = extern/SDL_ttf url = https://github.com/libsdl-org/SDL_ttf.git branch = release-2.22.x +[submodule "extern/tmxlite"] + path = extern/tmxlite + url = https://github.com/fallahn/tmxlite.git [submodule "docs/doxygen-awesome-css"] path = docs/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 08e3c8e..ea6a26b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,13 @@ cmake_minimum_required(VERSION 3.15) -project(SDL_Minigame) +project(engine) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) -set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(ENGINE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(ENGINE_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) +set(ENGINE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(BUILD_SHARED_LIBS FALSE) @@ -15,22 +15,28 @@ set(BUILD_SHARED_LIBS FALSE) set(SDL2MIXER_VENDORED ON) set(SDL2TTF_VENDORED ON) +set(SDL2_SOURCE_DIR “${ENGINE_SOURCE_DIR}/extern/SDL”) + +set(TMXLITE_STATIC_LIB TRUE) + add_subdirectory(extern/SDL EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_image EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_mixer EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_ttf EXCLUDE_FROM_ALL) +add_subdirectory(extern/tmxlite/tmxlite EXCLUDE_FROM_ALL) -file(GLOB_RECURSE SOURCES ${PROJECT_SOURCE_DIR}/src/*.cpp) -add_executable(${PROJECT_NAME} ${SOURCES}) +file(GLOB_RECURSE SOURCES ${ENGINE_SOURCE_DIR}/src/*.cpp) +add_library(${PROJECT_NAME} ${SOURCES}) -target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE_DIR}) +target_include_directories(${PROJECT_NAME} PUBLIC ${ENGINE_INCLUDE_DIR}) -target_link_libraries(${PROJECT_NAME} PRIVATE +target_link_libraries(${PROJECT_NAME} PUBLIC # should be private when all SDL functionality has a wrapper SDL2::SDL2main SDL2::SDL2-static SDL2_image::SDL2_image-static SDL2_mixer::SDL2_mixer-static SDL2_ttf::SDL2_ttf-static + tmxlite ) if(CMAKE_BUILD_TYPE MATCHES "Debug") @@ -38,5 +44,11 @@ if(CMAKE_BUILD_TYPE MATCHES "Debug") #target_link_libraries(${PROJECT_NAME} PRIVATE "-fsanitize=address") endif() - -file(COPY ${PROJECT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BINARY_DIR}) \ No newline at end of file +# link/copy compile commands to root dir +if(EXISTS ${PROJECT_BINARY_DIR}/compile_commands.json) + file(CREATE_LINK + ${PROJECT_BINARY_DIR}/compile_commands.json + ${PROJECT_SOURCE_DIR}/compile_commands.json + COPY_ON_ERROR SYMBOLIC + ) +endif() \ No newline at end of file diff --git a/assets/Map.aseprite b/assets/Map.aseprite deleted file mode 100644 index 7cb0feb..0000000 Binary files a/assets/Map.aseprite and /dev/null differ diff --git a/assets/MapNew-Sheet.aseprite b/assets/MapNew-Sheet.aseprite deleted file mode 100644 index 7f27b16..0000000 Binary files a/assets/MapNew-Sheet.aseprite and /dev/null differ diff --git a/assets/MapNew.aseprite b/assets/MapNew.aseprite deleted file mode 100644 index fb12db2..0000000 Binary files a/assets/MapNew.aseprite and /dev/null differ diff --git a/assets/MapNew.json b/assets/MapNew.json deleted file mode 100644 index f7526fc..0000000 --- a/assets/MapNew.json +++ /dev/null @@ -1,106 +0,0 @@ -{ "frames": [ - { - "filename": "MapNew.aseprite", - "frame": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 32, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 64, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 96, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 128, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 160, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 192, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 224, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 256, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - }, - { - "filename": "MapNew.aseprite", - "frame": { "x": 288, "y": 0, "w": 32, "h": 32 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, - "sourceSize": { "w": 32, "h": 32 }, - "duration": 100 - } - ], - "meta": { - "app": "https://www.aseprite.org/", - "version": "1.3.2-x64", - "format": "RGBA8888", - "size": { "w": 320, "h": 32 }, - "scale": "1", - "frameTags": [ - ], - "layers": [ - ], - "slices": [ - ] - } -} diff --git a/assets/MapNewBackup.aseprite b/assets/MapNewBackup.aseprite deleted file mode 100644 index fb12db2..0000000 Binary files a/assets/MapNewBackup.aseprite and /dev/null differ diff --git a/assets/MapTest.aseprite b/assets/MapTest.aseprite deleted file mode 100644 index c047008..0000000 Binary files a/assets/MapTest.aseprite and /dev/null differ diff --git a/assets/Player1Victory.png b/assets/Player1Victory.png deleted file mode 100644 index f50a386..0000000 Binary files a/assets/Player1Victory.png and /dev/null differ diff --git a/assets/Player2Victory.png b/assets/Player2Victory.png deleted file mode 100644 index 7a20be3..0000000 Binary files a/assets/Player2Victory.png and /dev/null differ diff --git a/assets/SDL_map_test.txt b/assets/SDL_map_test.txt deleted file mode 100644 index 809f2e3..0000000 --- a/assets/SDL_map_test.txt +++ /dev/null @@ -1,20 +0,0 @@ -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,2,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,2,2,2,3 -3,3,3,2,3,3,3,3,2,3,7,1,1,1,9,3,3,3,3,3,3,3,2,2,3 -3,3,2,2,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,2,2,2,3,3,3,3,3,7,1,1,1,9,3,3,2,3,3,3,3,3,3,3 -3,3,2,2,2,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,2,2,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,3,2,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,2,3,3,3,3 -3,3,3,2,2,3,3,3,3,3,7,1,1,1,9,3,3,3,3,2,2,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,2,2,2,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,2,2,3,3,3,3 -3,3,3,3,3,3,3,2,3,3,7,1,1,1,9,3,2,3,3,2,3,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,2,3,3,3,3,3,3,3,3 -2,2,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,2,2,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 -3,3,3,3,3,3,3,3,3,3,7,1,1,1,9,3,3,3,3,3,3,3,3,3,3 diff --git a/assets/VictoryBackup.aseprite b/assets/VictoryBackup.aseprite deleted file mode 100644 index b53cd97..0000000 Binary files a/assets/VictoryBackup.aseprite and /dev/null differ diff --git a/assets/atk_speed_powerup.png b/assets/atk_speed_powerup.png deleted file mode 100644 index 9bcce69..0000000 Binary files a/assets/atk_speed_powerup.png and /dev/null differ diff --git a/assets/cat.png b/assets/cat.png deleted file mode 100644 index 4b52f0f..0000000 Binary files a/assets/cat.png and /dev/null differ diff --git a/assets/characterSelection.png b/assets/characterSelection.png deleted file mode 100644 index 68e958a..0000000 Binary files a/assets/characterSelection.png and /dev/null differ diff --git a/assets/chicken_gentleman_spritesheet.png b/assets/chicken_gentleman_spritesheet.png deleted file mode 100644 index 848c426..0000000 Binary files a/assets/chicken_gentleman_spritesheet.png and /dev/null differ diff --git a/assets/chicken_knight_spritesheet.png b/assets/chicken_knight_spritesheet.png deleted file mode 100644 index 2304ae6..0000000 Binary files a/assets/chicken_knight_spritesheet.png and /dev/null differ diff --git a/assets/chicken_mlady_spritesheet.png b/assets/chicken_mlady_spritesheet.png deleted file mode 100644 index c075990..0000000 Binary files a/assets/chicken_mlady_spritesheet.png and /dev/null differ diff --git a/assets/chicken_neutral.png b/assets/chicken_neutral.png deleted file mode 100644 index 8a68462..0000000 Binary files a/assets/chicken_neutral.png and /dev/null differ diff --git a/assets/chicken_neutral_knight.png b/assets/chicken_neutral_knight.png deleted file mode 100644 index 1cf69c9..0000000 Binary files a/assets/chicken_neutral_knight.png and /dev/null differ diff --git a/assets/chicken_neutral_mlady.png b/assets/chicken_neutral_mlady.png deleted file mode 100644 index 3973d7d..0000000 Binary files a/assets/chicken_neutral_mlady.png and /dev/null differ diff --git a/assets/chicken_neutral_wizard.png b/assets/chicken_neutral_wizard.png deleted file mode 100644 index aba3980..0000000 Binary files a/assets/chicken_neutral_wizard.png and /dev/null differ diff --git a/assets/chicken_spritesheet.png b/assets/chicken_spritesheet.png deleted file mode 100644 index ba003eb..0000000 Binary files a/assets/chicken_spritesheet.png and /dev/null differ diff --git a/assets/chicken_wizard_spritesheet.png b/assets/chicken_wizard_spritesheet.png deleted file mode 100644 index 8694c93..0000000 Binary files a/assets/chicken_wizard_spritesheet.png and /dev/null differ diff --git a/assets/chicken_wizzard_spritesheet.png b/assets/chicken_wizzard_spritesheet.png deleted file mode 100644 index 27e8796..0000000 Binary files a/assets/chicken_wizzard_spritesheet.png and /dev/null differ diff --git a/assets/cow.png b/assets/cow.png deleted file mode 100644 index 956895f..0000000 Binary files a/assets/cow.png and /dev/null differ diff --git a/assets/dirt.png b/assets/dirt.png deleted file mode 100644 index b60025b..0000000 Binary files a/assets/dirt.png and /dev/null differ diff --git a/assets/egg.png b/assets/egg.png deleted file mode 100644 index f0a12bf..0000000 Binary files a/assets/egg.png and /dev/null differ diff --git a/assets/grass.png b/assets/grass.png deleted file mode 100644 index b096692..0000000 Binary files a/assets/grass.png and /dev/null differ diff --git a/assets/grass_water_left.png b/assets/grass_water_left.png deleted file mode 100644 index 6a22a75..0000000 Binary files a/assets/grass_water_left.png and /dev/null differ diff --git a/assets/grass_water_right.png b/assets/grass_water_right.png deleted file mode 100644 index fa75ce7..0000000 Binary files a/assets/grass_water_right.png and /dev/null differ diff --git a/assets/heart.png b/assets/heart.png deleted file mode 100644 index 867809b..0000000 Binary files a/assets/heart.png and /dev/null differ diff --git a/assets/heart_powerup.png b/assets/heart_powerup.png deleted file mode 100644 index 0a2a92d..0000000 Binary files a/assets/heart_powerup.png and /dev/null differ diff --git a/assets/iconImage.bmp b/assets/iconImage.bmp deleted file mode 100644 index 6eddbef..0000000 Binary files a/assets/iconImage.bmp and /dev/null differ diff --git a/assets/movement_speed_powerup.png b/assets/movement_speed_powerup.png deleted file mode 100644 index e0876ec..0000000 Binary files a/assets/movement_speed_powerup.png and /dev/null differ diff --git a/assets/sound/steps.wav b/assets/sound/steps.wav deleted file mode 100644 index a060c29..0000000 Binary files a/assets/sound/steps.wav and /dev/null differ diff --git a/assets/sound/throw_egg.wav b/assets/sound/throw_egg.wav deleted file mode 100644 index 67dc0e7..0000000 Binary files a/assets/sound/throw_egg.wav and /dev/null differ diff --git a/assets/startscreen.png b/assets/startscreen.png deleted file mode 100644 index a1fe40d..0000000 Binary files a/assets/startscreen.png and /dev/null differ diff --git a/assets/stone.png b/assets/stone.png deleted file mode 100644 index 4798d7b..0000000 Binary files a/assets/stone.png and /dev/null differ diff --git a/assets/title_screen.png b/assets/title_screen.png deleted file mode 100644 index e8bb2f3..0000000 Binary files a/assets/title_screen.png and /dev/null differ diff --git a/assets/title_screen_backup.aseprite b/assets/title_screen_backup.aseprite deleted file mode 100644 index 21d6030..0000000 Binary files a/assets/title_screen_backup.aseprite and /dev/null differ diff --git a/assets/water.png b/assets/water.png deleted file mode 100644 index 88dfbeb..0000000 Binary files a/assets/water.png and /dev/null differ diff --git a/assets/water1.ase b/assets/water1.ase deleted file mode 100644 index 86e75e3..0000000 Binary files a/assets/water1.ase and /dev/null differ diff --git a/assets/water1.png b/assets/water1.png deleted file mode 100644 index 7ef949c..0000000 Binary files a/assets/water1.png and /dev/null differ diff --git a/extern/tmxlite b/extern/tmxlite new file mode 160000 index 0000000..fcef1a2 --- /dev/null +++ b/extern/tmxlite @@ -0,0 +1 @@ +Subproject commit fcef1a28ade8406e290d5fd168a8950e6996844f diff --git a/include/AnimationHandler.h b/include/AnimationHandler.h index 069fc2f..b05a1d6 100644 --- a/include/AnimationHandler.h +++ b/include/AnimationHandler.h @@ -16,11 +16,5 @@ struct Animation } }; -enum AnimationType //TODO enum class -{ - IDLE = 0, - WALK = 1 -}; - diff --git a/include/AssetManager.h b/include/AssetManager.h index 4d6931e..1d3c97a 100644 --- a/include/AssetManager.h +++ b/include/AssetManager.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "Entity.h" @@ -23,8 +24,8 @@ public: AssetManager(Manager* manager); ~AssetManager(); - void createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, Entity::TeamLabel teamLabel); - void createPowerup(Vector2D pos, PowerupType type); + void createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, Entity* owner); + void createPowerup(Vector2D pos, std::function pickupFunc, std::string texturePath); Vector2D calculateSpawnPosition(); PowerupType calculateType(); @@ -35,12 +36,16 @@ public: // sound management void addSoundEffect(std::string id, const char* path); + void addMusic(std::string id, const char* path); + SDL_Texture* getTexture(std::string id); Mix_Chunk* getSound(std::string id); + Mix_Music* getMusic(std::string id); private: Manager* man; std::map textures; std::map soundEffects; + std::map music; }; diff --git a/include/CollisionHandler.h b/include/CollisionHandler.h index 04aafbf..9802f7d 100644 --- a/include/CollisionHandler.h +++ b/include/CollisionHandler.h @@ -50,8 +50,7 @@ public: // temporary function, remove once game.cpp cleaned up std::vector getColliders( std::initializer_list const& groupLabels, - std::initializer_list const& teamLabels = {}, - bool negateTeam = false); + std::initializer_list const& excludedEntities = {}); /*! * @@ -72,8 +71,7 @@ public: Entity* entity, Vector2D posMod = {}, std::initializer_list const& groupLabels = {}, - std::initializer_list const& teamLabels = {}, - bool negateTeam = false); + std::initializer_list const& excludedEntities = {}); void update(); }; \ No newline at end of file diff --git a/include/Component.h b/include/Component.h index 095b370..566fee9 100644 --- a/include/Component.h +++ b/include/Component.h @@ -9,7 +9,6 @@ public: virtual void init() {} virtual void update() {} - virtual void draw() {} virtual ~Component() = default; }; \ No newline at end of file diff --git a/include/Components.h b/include/Components.h deleted file mode 100644 index b6f506f..0000000 --- a/include/Components.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "ECS.h" -#include "Component.h" -#include "Manager.h" -#include "Entity.h" -#include "TransformComponent.h" -#include "SpriteComponent.h" -#include "KeyboardController.h" -#include "ColliderComponent.h" -#include "TileComponent.h" -#include "ProjectileComponent.h" -#include "HealthComponent.h" diff --git a/include/Constants.h b/include/Constants.h index 20ccf44..713f1bd 100644 --- a/include/Constants.h +++ b/include/Constants.h @@ -3,7 +3,6 @@ #include using Group = std::size_t; -using Team = std::size_t; constexpr int CHARACTER_COUNT = 4; @@ -24,7 +23,8 @@ constexpr int MAP_SIZE_Y = 20; constexpr int SPAWN_ATTEMPTS = 20; -constexpr int BUFF_DURATION = 240; +constexpr int PLAY_LOOPED = -1; +constexpr int PLAY_ONCE = 0; -constexpr int BUFF_VALUE = 1; +constexpr int MAX_VOLUME = 128; diff --git a/include/Entity.h b/include/Entity.h index 3b9536e..aa067a4 100644 --- a/include/Entity.h +++ b/include/Entity.h @@ -9,6 +9,12 @@ #include "ECS.h" #include "Constants.h" +// TODO: remove here if possible +// temporary fix: addComponent function template doesnt know TransformComponent -> error undefined type +#include "InputComponent.h" +#include "TransformComponent.h" +#include "SpriteComponent.h" + class Manager; class Component; @@ -47,16 +53,6 @@ public: POWERUPS //!< \todo Document }; - /*! - * \brief Allows grouping entities by team association for hits, win conditions, etc. - */ - enum class TeamLabel - { - NONE, //!< No team, should be skipped in any checks - BLUE, //!< Team blue - RED //!< Team red - }; - /*! * \todo Document */ @@ -64,9 +60,6 @@ public: manager(mManager) { }; void update() const; //!< Call each frame to update all components - //! Call after update to render components. - //! \sa SpriteComponent::draw() - void draw() const; bool isActive() const { return this->active; } //!< \sa destroy() //! Mark for destruction for Manager::refresh() and disables collision @@ -85,9 +78,6 @@ public: //! \sa GroupLabel std::bitset getGroupBitSet(); - void setTeam(TeamLabel teamLabel); //!< \sa TeamLabel - TeamLabel getTeam(); //!< \sa TeamLabel - //! \sa Manager Manager& getManager() { return manager; }; @@ -126,5 +116,4 @@ private: ComponentArray componentArray = {}; ComponentBitSet componentBitSet; GroupBitSet groupBitSet; - TeamLabel teamLabel; }; \ No newline at end of file diff --git a/include/Game.h b/include/Game.h index 9a9bddf..c1b1aa3 100644 --- a/include/Game.h +++ b/include/Game.h @@ -1,49 +1,18 @@ #pragma once -#include -#include -#include -#include +class GameInternal; -#include "Vector2D.h" -#include "Entity.h" - -class AssetManager; -class CollisionHandler; -class TextureManager; -class SoundManager; - -class Game -{ +// TODO: add managers here +class Game { public: - Game(); - ~Game(); + virtual ~Game() {} - void init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen); - void selectCharacters(const char* &playerSprite, const char* &enemySprite); + virtual void init() = 0; + virtual void update() = 0; - void handleEvents(); - void update(); - void render(); - void clean(); - bool running() const; - - static void addTile(unsigned long id, int x, int y); - static SDL_Renderer* renderer; - static SDL_Event event; - static CollisionHandler* collisionHandler; - static AssetManager* assets; - static TextureManager* textureManager; - static SoundManager* soundManager; - - void refreshPlayers(); - Entity::TeamLabel getWinner() const; - -private: - void setWinner(Entity::TeamLabel winningTeam); - - int counter = 0; - bool isRunning = false; - SDL_Window* window; - Entity::TeamLabel winner; + GameInternal* gameInternal; //!< \deprecated }; + + +// game factory include to simplify imports in implementation +#include "GameFactory.h" \ No newline at end of file diff --git a/include/GameFactory.h b/include/GameFactory.h new file mode 100644 index 0000000..b1e88ca --- /dev/null +++ b/include/GameFactory.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Game.h" + +class GameInternal; + +class GameFactory { +public: + using CreateFunc = std::function; + + static GameFactory& instance() { + static GameFactory factory; + return factory; + } + + void registerClass(CreateFunc createFunc) { + this->creatorFunc = createFunc; + } + + Game* create(GameInternal* gameInternal) { + if (this->creatorFunc == nullptr) { + throw std::runtime_error("No game implementation registered!"); + return nullptr; + } + Game* game = (this->creatorFunc)(); + game->gameInternal = gameInternal; + return game; + } + +private: + CreateFunc creatorFunc = nullptr; +}; diff --git a/include/GameInternal.h b/include/GameInternal.h new file mode 100644 index 0000000..44cec2d --- /dev/null +++ b/include/GameInternal.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Manager.h" +#include "Vector2D.h" +#include "Entity.h" +#include "RenderManager.h" + +typedef std::function gamefunction; + +class AssetManager; +class CollisionHandler; +class TextureManager; +class SoundManager; +class Map; +class Game; + +class GameInternal +{ +public: + GameInternal(); + ~GameInternal(); + + void init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen); + + void handleEvents(); + void update(); + void render(); + void clean(); + bool isRunning() const; + void setRunning(bool running); // TODO: should be private/not accesible for game dev + void stopGame(); + + /* static */ SDL_Renderer* renderer = nullptr; + /* static */ SDL_Event event; + /* static */ CollisionHandler* collisionHandler; + /* static */ AssetManager* assets; + /* static */ TextureManager* textureManager; + /* static */ SoundManager* soundManager; + + Manager manager; + RenderManager renderManager; + Map* map; // game specific, might not be needed for all types of games + + std::vector& tiles; + std::vector& players; + std::vector& projectiles; + std::vector& hearts; + std::vector& powerups; + // end moved globals + + void refreshPlayers(); + +private: + + Game* gameInstance; + + int counter = 0; + bool running = true; + SDL_Window* window; +}; diff --git a/include/GameObject.h b/include/GameObject.h deleted file mode 100644 index 1c209ae..0000000 --- a/include/GameObject.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include - -class GameObject -{ -public: - GameObject(const char* texturesheet, int x, int y); - ~GameObject() = default; - - void update(); - void render(); - -private: - int xPos; - int yPos; - - SDL_Texture* objTexture; - SDL_Rect srcRect; - SDL_Rect destRect; -}; - diff --git a/include/GameRegistryHelper.h b/include/GameRegistryHelper.h new file mode 100644 index 0000000..7042047 --- /dev/null +++ b/include/GameRegistryHelper.h @@ -0,0 +1,24 @@ +#pragma once + +#include "GameFactory.h" + +namespace vego { + template + class GameRegistryHelper { + public: + [[deprecated("GameRegistryHelper() does not take a className anymore")]] + GameRegistryHelper(const std::string& className) { + static_assert(std::is_base_of::value, "Your class must inherit from Game"); + GameFactory::instance().registerClass( + []() -> Game* { return new T; } + ); + }; + + GameRegistryHelper() { + static_assert(std::is_base_of::value, "Your class must inherit from Game"); + GameFactory::instance().registerClass( + []() -> Game* { return new T; } + ); + }; + }; +} \ No newline at end of file diff --git a/include/HealthComponent.h b/include/HealthComponent.h index fad7a23..8e72d09 100644 --- a/include/HealthComponent.h +++ b/include/HealthComponent.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "Direction.h" #include "Component.h" @@ -9,7 +12,7 @@ class HealthComponent : public Component { public: - HealthComponent(int health, Direction side) : health(health), side(side) {} + HealthComponent(int health) : health(health) {} ~HealthComponent() {} void modifyHealth(int health = -1); @@ -25,5 +28,4 @@ public: private: int health; - Direction side; }; \ No newline at end of file diff --git a/include/InputComponent.h b/include/InputComponent.h new file mode 100644 index 0000000..9382cf1 --- /dev/null +++ b/include/InputComponent.h @@ -0,0 +1,106 @@ +#pragma once +#include +#include + +#include "Component.h" + +enum class Key +{ + UP, + DOWN, + LEFT, + RIGHT, + SPACE, + ENTER, + ESCAPE, + TAB, + BACKSPACE, + DELETE, + HOME, + END, + PAGE_UP, + PAGE_DOWN, + INSERT, + CAPS_LOCK, + LEFT_SHIFT, + RIGHT_SHIFT, + LEFT_CTRL, + RIGHT_CTRL, + LEFT_ALT, + RIGHT_ALT, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + NUM_0, + NUM_1, + NUM_2, + NUM_3, + NUM_4, + NUM_5, + NUM_6, + NUM_7, + NUM_8, + NUM_9, + LEFT_BRACKET, + RIGHT_BRACKET, + SEMICOLON, + APOSTROPHE, + COMMA, + PERIOD, + SLASH, + BACKSLASH, + GRAVE +}; + +class InputComponent : public Component +{ +public: + InputComponent(); + ~InputComponent(); + + void init() override; + void update() override; + + bool isKeyDown(Key key); + +private: + const Uint8* m_keyStates; + SDL_Scancode mapKeyToSDL(Key key); + std::map m_keyMappings; + void InitKeyMappings(); +}; diff --git a/include/KeyboardController.h b/include/KeyboardController.h deleted file mode 100644 index 9079b73..0000000 --- a/include/KeyboardController.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include - -#include "Component.h" -#include "Vector2D.h" -#include "Constants.h" -#include "SoundManager.h" - -class TransformComponent; -class SpriteComponent; - -class KeyboardController : public Component -{ -public: - TransformComponent* transform; - const uint8_t* keystates = SDL_GetKeyboardState(NULL); - SDL_Scancode up; - SDL_Scancode down; - SDL_Scancode left; - SDL_Scancode right; - SDL_Scancode fire; - - SpriteComponent* sprite; - - //for attack cooldown in between shots - uint32_t lastFireTime = 0; - uint32_t fireCooldown = 1000; //in ms can be adjusted to change possible attack-speed - - KeyboardController() = default; - KeyboardController(SDL_Scancode up, SDL_Scancode down, SDL_Scancode left, SDL_Scancode right, SDL_Scancode fire, Vector2D fireVelocity); - ~KeyboardController() = default; - - void init() override; - void update() override; - - void modifyAtkSpeed(int8_t modifier); - -private: - //for creation of projectiles - TransformComponent* player; //for starting position of projectile - Vector2D fireVelocity; //decide source of projectile and flying direction - // SoundManager* soundEffect = Game::assets->getSound; - //SoundManager* soundEffect = new SoundManager(); -}; diff --git a/include/Manager.h b/include/Manager.h index 1e2fff7..8b9ceb3 100644 --- a/include/Manager.h +++ b/include/Manager.h @@ -1,5 +1,7 @@ #pragma once + #include + #include #include #include @@ -7,6 +9,7 @@ #include "Constants.h" #include "Entity.h" +class GameInternal; /*! * * \brief Is responsible for managing all entities @@ -19,8 +22,9 @@ class Manager { public: + Manager(GameInternal* game) : game(game) {}; + void update(); //!< \sa Entity::update() - void draw(); //!< \sa Entity::draw() //! Disables all functionality of entities marked for destruction //! \sa Entity::destroy() void refresh(); @@ -28,15 +32,14 @@ public: void addToGroup(Entity* mEntity, Group mGroup); //!< \todo `friend` to Entity std::vector& getGroup(Group mGroup); //!< \returns std::vector containing all entities in group Entity::GroupLabel - void addToTeam(Entity* mEntity, Team mTeam); //!< \todo `friend` to Entity - std::vector& getTeam(Team mTeam); //!< \returns std::vector containing all entities in team Entity::TeamLabel - std::vector getAll(); //!< \returns std::vector containing all entities Entity& addEntity(); //!< Creates and returns a new, empty entity + GameInternal* getGame() { return this->game; }; + private: + GameInternal* game; std::vector> entities; std::array, MAX_GROUPS> entitiesByGroup; - std::array, MAX_TEAMS> entitiesByTeam; }; \ No newline at end of file diff --git a/include/Map.h b/include/Map.h index 369cb6c..92b8179 100644 --- a/include/Map.h +++ b/include/Map.h @@ -1,13 +1,16 @@ #pragma once +#include +#include +#include + +class GameInternal; class Map { public: Map() = default; ~Map() = default; - // code comment below is a test for doxygen - do not remove - /*! * * \brief @@ -17,5 +20,17 @@ public: * \return Boolean for success * */ - static bool loadMap(const char* path, int sizeX, int sizeY); + [[deprecated("ID based text files are not supported anymore, use .txm maps instead")]] + static void loadMap(const char* path, int sizeX, int sizeY, GameInternal* game, const std::map>* textureDict /* backreference */); + [[deprecated]] + static void addTile(unsigned long id, int x, int y, GameInternal* game, const std::map>* textureDict); + + /*! + * \brief Loads a .tmx map + * \param path Path to the `.tmx` map file + * + */ + static void loadMapTmx(const char* path); +private: + static void addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, const char* texturePath); }; diff --git a/include/PopupWindow.h b/include/PopupWindow.h deleted file mode 100644 index ccd01f6..0000000 --- a/include/PopupWindow.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include -#include -#include - -#include "Entity.h" - -class Game; - -class PopupWindow { - -public: - PopupWindow(const char* title, const std::string& message); - ~PopupWindow(); - - void handleWinnerEvents(); - bool shouldContinue() const; - - bool interacted; - - void renderWinnerPopup(Entity::TeamLabel winner); - -private: - SDL_Renderer* renderer; - SDL_Window* window; - SDL_Texture* texture; - bool continueGame; -}; \ No newline at end of file diff --git a/include/PowerupComponent.h b/include/PowerupComponent.h index f83ea29..53f365b 100644 --- a/include/PowerupComponent.h +++ b/include/PowerupComponent.h @@ -1,19 +1,16 @@ #pragma once +#include #include "Component.h" -#include "AssetManager.h" class PowerupComponent : public Component { public: - PowerupComponent(PowerupType type); + PowerupComponent(std::function func); ~PowerupComponent() {}; void update() override; - void heartEffect(Entity* player); - void movementSpeedEffect(Entity* player); - void atkSpeedEffect(Entity* player); private: - void (PowerupComponent::*pickupFunc)(Entity* player); + std::function pickupFunc; }; \ No newline at end of file diff --git a/include/ProjectileComponent.h b/include/ProjectileComponent.h index 0e57a69..4db0bfd 100644 --- a/include/ProjectileComponent.h +++ b/include/ProjectileComponent.h @@ -11,7 +11,8 @@ class ProjectileComponent : public Component //can maybe be split in separate .cpp file public: - ProjectileComponent(int range, int speed, Vector2D direction) : range(range), speed(speed), direction(direction) {} + ProjectileComponent(int range, int speed, Vector2D direction, Entity* owner) + : range(range), speed(speed), direction(direction), owner(owner) {} ~ProjectileComponent() {} void init() override; @@ -24,5 +25,7 @@ private: int speed = 0; int distance = 0; + Entity* owner = nullptr; + Vector2D direction; }; \ No newline at end of file diff --git a/include/RenderManager.h b/include/RenderManager.h new file mode 100644 index 0000000..86aa614 --- /dev/null +++ b/include/RenderManager.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "RenderObject.h" + +class RenderManager { +public: + RenderManager() {}; + + /* + * Remove an object from the list of objects to be rendered + * \param renderObject The object to be removed + * \sa RenderObject + */ + void remove(RenderObject* obj); + /* + * Add an object to be rendered + * \param renderObject The object to be rendered + * \sa RenderObject + */ + void add(RenderObject* obj); + + void renderAll(); //!< Render all objects. If the list has been modified, sorts it based on z-index first + +private: + std::vector renderObjects; + bool isSorted; +}; \ No newline at end of file diff --git a/include/RenderObject.h b/include/RenderObject.h new file mode 100644 index 0000000..ba4b929 --- /dev/null +++ b/include/RenderObject.h @@ -0,0 +1,27 @@ +#pragma once + +class RenderManager; + +class RenderObject +{ +public: + virtual void draw() = 0; + + RenderObject(int zIndex, RenderManager& renderManager); + ~RenderObject(); + + int getZIndex() { return this->zIndex; }; + + //! Comparitor to compare two ptr based on z-index + struct ZIndexComparator { + bool operator()(RenderObject const *lhs, RenderObject const *rhs ) const { + return lhs->zIndex < rhs->zIndex; + } + }; + +private: + int zIndex = 0; + +protected: + RenderManager& renderManager; +}; \ No newline at end of file diff --git a/include/SoundManager.h b/include/SoundManager.h index 2b73298..8663d34 100644 --- a/include/SoundManager.h +++ b/include/SoundManager.h @@ -4,14 +4,17 @@ #include #include +#include "ECS.h" #include "TextureManager.h" + +class GameInternal; -enum SoundTypes -{ - STEPS, - THROW_EGG, -}; - +/*! + * + * \brief Handles music and sound. + * \details SoundManager handles loading in music and sound effects from files, playing music and sound effects and toggling the audio volume. + * + */ class SoundManager { public: @@ -20,13 +23,55 @@ class SoundManager for (auto& it : this->sound_cache) { Mix_FreeChunk(it.second); } + + for (auto& it : this->music_cache) { + Mix_FreeMusic(it.second); + } } SoundManager(SoundManager const&) = delete; void operator=(SoundManager const&) = delete; + std::map music_cache; std::map sound_cache; + /*! + * \brief Loads music from a file (mp3) + * \returns a pointer to Mix_Music + * \sa AssetManager::AddMusic(std::string id, const char* path) + */ + Mix_Music* loadMusic(const char* fileName); + /*! + * \brief Loads sound effects from a file (wav) + * \returns a pointer to Mix_Chunk + * \sa AssetManager::AddSound(std::string id, const char* path) + */ Mix_Chunk* loadSound(const char* fileName); - static void playSound(SoundTypes sound); + + /*! + * \brief Handles playing of sound effects + * + * Handles if sounds can overlap, how often they can loop, as well as the volume at which the specified sound effect should play + * and on which channel the soundeffect should play. + */ + static void playSound(GameInternal* game, std::string sound, bool canOverlap, int loops, int volume, int channel); + /*! + * \brief Handles playing of music + * + * Handles how often track can loop, as well as the volume at which the specified track should play and if it fades in. + */ + static void playMusic(GameInternal* game, std::string sound, int loops, int volume, int ms); + + static void setSoundVolume(int volume, int channel); //!< Volume handling for sound effects (either all or on a specific channel) + static void setMusicVolume(int volume); //!< Volume handling for music track + + static void pauseSound(int channel); //!< Handles pausing sound effects (either all or on a specific channel) + static void pauseMusic(); //!< Handles pausing music track + + static void restartSound(int channel); //!< Handles resuming sound effects (either all or on a specific channel) + static void restartMusic(); //!< Handles resuming music track + + static void fadeOutMusic(int ms); //!< Handles fading out a music track + + private: }; \ No newline at end of file diff --git a/include/SpriteComponent.h b/include/SpriteComponent.h index fc310aa..0676549 100644 --- a/include/SpriteComponent.h +++ b/include/SpriteComponent.h @@ -3,34 +3,46 @@ #include #include #include +#include #include "AnimationHandler.h" #include "Component.h" #include "Direction.h" +#include "RenderObject.h" class TransformComponent; -class SpriteComponent : public Component +class SpriteComponent : public Component, public RenderObject { public: int animationIndex = 0; - std::map> animations; + std::map>* animations = nullptr; private: TransformComponent* transform; SDL_Texture* texture; SDL_Rect srcRect, destRect; + const char* texturePath; + bool animated = false; uint8_t frames = 0; uint8_t speed = 100; bool flipped = false; + int textureXOffset; + int textureYOffset; + public: - SpriteComponent() = default; - SpriteComponent(const char* path); - SpriteComponent(const char* path, bool isAnimated); + SpriteComponent(const char* path, int zIndex); + SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex); + SpriteComponent( + const char* path, + bool isAnimated, + std::map>* animationList, + std::string defaultAnimation, + int zIndex); ~SpriteComponent(); void setTexture(const char* path); @@ -38,6 +50,6 @@ public: void init() override; void update() override; void draw() override; - void playAnimation(AnimationType type); + void playAnimation(std::string type); void setDirection(Direction direction); }; diff --git a/include/StatEffectsComponent.h b/include/StatEffectsComponent.h index 9daa19d..bc93f2b 100644 --- a/include/StatEffectsComponent.h +++ b/include/StatEffectsComponent.h @@ -18,9 +18,10 @@ public: void init() override; void update() override; - void modifyStatDur(Stats stat, int duration); + void modifyStatDur(Stats stat, int duration, int value); void modifyStatValue(Stats stat, int modifier); + void resetStatValue(Stats stat); private: std::array buffs = { 0 }; diff --git a/include/TextureDict.h b/include/TextureDict.h deleted file mode 100644 index 0a4b6ed..0000000 --- a/include/TextureDict.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include "AssetManager.h" - -class TextureDict -{ -public: - const std::map tileDictionary = { - {1, "assets/water.png"}, - {2, "assets/dirt.png"}, - {3, "assets/grass.png"}, - {7, "assets/grass_water_left.png"}, - {9, "assets/grass_water_right.png"} - }; - - - std::map powerupDictionary = { - {PowerupType::HEART, "assets/heart_powerup.png"}, - {PowerupType::WALKINGSPEED, "assets/movement_speed_powerup.png"}, - {PowerupType::SHOOTINGSPEED, "assets/atk_speed_powerup.png"} - }; -}; diff --git a/include/TextureManager.h b/include/TextureManager.h index bc1601d..3e4f1c4 100644 --- a/include/TextureManager.h +++ b/include/TextureManager.h @@ -1,5 +1,6 @@ #pragma once +#include "ECS.h" #include #include #include @@ -9,7 +10,7 @@ class TextureManager { public: - TextureManager() {} + TextureManager(Manager* manager) : manager(manager) {} ~TextureManager() { for (auto& it : this->texture_cache) { SDL_DestroyTexture(it.second); @@ -20,5 +21,7 @@ class TextureManager SDL_Texture* loadTexture(const char* fileName); static std::vector splitSpriteSheet(SDL_Texture* spriteSheet, int width, int height, int spritesOnSheet); - static void draw(SDL_Texture* texture, SDL_Rect src, SDL_Rect dest, bool flipped = false); + static void draw(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect src, SDL_Rect dest, bool flipped = false); + private: + Manager* manager; }; \ No newline at end of file diff --git a/include/TileComponent.h b/include/TileComponent.h index c0a9e1b..8844daa 100644 --- a/include/TileComponent.h +++ b/include/TileComponent.h @@ -1,9 +1,10 @@ #pragma once #include +#include +#include #include "Component.h" -#include "TextureDict.h" class SpriteComponent; class TransformComponent; @@ -13,15 +14,20 @@ class TileComponent : public Component public: TransformComponent* transform; SpriteComponent* sprite; - TextureDict textureDict; SDL_Rect tileRect; int tileID; const char* path; TileComponent() = default; - TileComponent(int x, int y, int w, int h, int id); + TileComponent(int x, int y, int w, int h, int id, const std::map>* textureDict); ~TileComponent() = default; void init() override; + + bool hasCollision(){return this->collision;} + std::string getName(){return this->tileName;} +private: + bool collision; + std::string tileName; }; \ No newline at end of file diff --git a/include/TransformComponent.h b/include/TransformComponent.h index 43e6cdc..7a15aa4 100644 --- a/include/TransformComponent.h +++ b/include/TransformComponent.h @@ -7,14 +7,15 @@ class TransformComponent : public Component { public: - Vector2D position; // TODO: change to int to safe CPU time -> possibly subpixel coordinates + Vector2D position; // TODO: change to int to save CPU time -> possibly subpixel coordinates Vector2D direction; int height = 32; int width = 32; int scale = 1; - int speed = 3; + int getSpeed() { return speed + speedMod; }; + void resetSpeedMod() { speedMod = 0; }; TransformComponent(); explicit TransformComponent(int scale); @@ -25,5 +26,10 @@ public: void init() override; /*! TODO: document usage of collision handler */ void update() override; + void setPositionAfterCollision(Vector2D& positionChange); void modifySpeed(int8_t modifier); + +private: + int speed = 3; + int speedMod = 0; }; diff --git a/include/VEGO.h b/include/VEGO.h new file mode 100644 index 0000000..550fd61 --- /dev/null +++ b/include/VEGO.h @@ -0,0 +1,9 @@ +#include "GameInternal.h" + +namespace vego { + extern GameInternal* game; +} + +inline GameInternal& VEGO_Game() { + return *vego::game; +}; \ No newline at end of file diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp index 03a1d6a..25ce8ad 100644 --- a/src/AssetManager.cpp +++ b/src/AssetManager.cpp @@ -2,8 +2,8 @@ #include "TextureManager.h" #include "SoundManager.h" -#include "Components.h" -#include "Game.h" +#include "ProjectileComponent.h" +#include "GameInternal.h" #include "TransformComponent.h" @@ -11,7 +11,6 @@ #include "ColliderComponent.h" #include "Constants.h" #include "Entity.h" -#include "Game.h" #include "Vector2D.h" #include "PowerupComponent.h" #include @@ -21,12 +20,17 @@ AssetManager::AssetManager(Manager* manager) : man(manager) {} AssetManager::~AssetManager() {} void AssetManager::addTexture(std::string id, const char* path) { - textures.emplace(id, Game::textureManager->loadTexture(path)); + textures.emplace(id, this->man->getGame()->textureManager->loadTexture(path)); } void AssetManager::addSoundEffect(std::string id, const char* path) { - soundEffects.emplace(id, Game::soundManager->loadSound(path)); + soundEffects.emplace(id, this->man->getGame()->soundManager->loadSound(path)); +} + +void AssetManager::addMusic(std::string id, const char* path) +{ + music.emplace(id, this->man->getGame()->soundManager->loadMusic(path)); } SDL_Texture* AssetManager::getTexture(std::string id) { @@ -37,36 +41,35 @@ Mix_Chunk* AssetManager::getSound(std::string id) { return soundEffects.at(id); } -void AssetManager::createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, Entity::TeamLabel teamLabel) { +Mix_Music* AssetManager::getMusic(std::string id) +{ + return music.at(id); +} + +void AssetManager::createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, Entity* owner) { auto& projectile(man->addEntity()); projectile.addComponent(pos.x, pos.y, 32, 32, scale); //32x32 is standard size for objects - projectile.addComponent(texturePath); - projectile.addComponent(range, speed, velocity); + projectile.addComponent(texturePath, 4); + projectile.addComponent(range, speed, velocity, owner); projectile.addComponent("projectile", 0.6f); projectile.addGroup((size_t)Entity::GroupLabel::PROJECTILE); - projectile.setTeam(teamLabel); } -void AssetManager::createPowerup(Vector2D pos, PowerupType type) { - TextureDict textureDict; +void AssetManager::createPowerup(Vector2D pos, std::function pickupFunc, std::string texturePath) { auto& powerups(man->addEntity()); powerups.addComponent(pos.x, pos.y, 32, 32, 1); //32x32 is standard size for objects - auto it = textureDict.powerupDictionary.find(type); - if (it == textureDict.powerupDictionary.end()) { - std::cout << "it end" << std::endl; - } try { - powerups.addComponent(it->second.data()); + powerups.addComponent(texturePath.c_str(), 3); } catch (std::runtime_error e) { std::cout << e.what() << std::endl; } powerups.addComponent("powerup", 0.6f); - powerups.addComponent(type); + powerups.addComponent(pickupFunc); powerups.addGroup((size_t)Entity::GroupLabel::POWERUPS); } @@ -81,7 +84,7 @@ Vector2D AssetManager::calculateSpawnPosition() spawnRect.x = rand() % (SCREEN_SIZE_WIDTH - spawnRect.w); spawnRect.y = rand() % (SCREEN_SIZE_HEIGHT - spawnRect.h); conflict = false; - for (auto cc : Game::collisionHandler->getColliders({ Entity::GroupLabel::MAPTILES })) + for (auto cc : this->man->getGame()->collisionHandler->getColliders({ Entity::GroupLabel::MAPTILES })) { if (SDL_HasIntersection(&spawnRect, &cc->collider) && strcmp(cc->tag, "projectile")) { diff --git a/src/ColliderComponent.cpp b/src/ColliderComponent.cpp index 08475d0..2275163 100644 --- a/src/ColliderComponent.cpp +++ b/src/ColliderComponent.cpp @@ -2,7 +2,7 @@ #include "CollisionHandler.h" #include "Entity.h" -#include "Game.h" +#include "GameInternal.h" #include "TransformComponent.h" #include @@ -31,7 +31,6 @@ void ColliderComponent::init() } transform = &entity->getComponent(); - //Game::collisionHandler->add(this); this->update(); } diff --git a/src/CollisionHandler.cpp b/src/CollisionHandler.cpp index 07c5f2c..5477c6c 100644 --- a/src/CollisionHandler.cpp +++ b/src/CollisionHandler.cpp @@ -87,8 +87,7 @@ IntersectionBitSet CollisionHandler::getIntersectionWithBounds(Entity* entity, V std::vector CollisionHandler::getColliders( std::initializer_list const& groupLabels, - std::initializer_list const& teamLabels, - bool negateTeam) + std::initializer_list const& excludedEntities) { std::vector colliders; @@ -97,16 +96,11 @@ std::vector CollisionHandler::getColliders( groupBitSet.set((size_t) groupLabel); } - std::bitset teamBitSet; - for (auto& teamLabel : teamLabels) { - teamBitSet.set((size_t) teamLabel); - } - for (auto& entity : manager.getAll()) { if ((groupBitSet & entity->getGroupBitSet()).none()) continue; - if (teamBitSet.any() && negateTeam != (teamBitSet.test((size_t) entity->getTeam()))) - continue; + if (std::ranges::find(excludedEntities, entity) != excludedEntities.end()) + continue; if (!entity->hasComponent()) continue; colliders.emplace_back(&entity->getComponent()); @@ -131,12 +125,11 @@ IntersectionBitSet CollisionHandler::getAnyIntersection( Entity* entity, Vector2D posMod, std::initializer_list const& groupLabels, - std::initializer_list const& teamLabels, - bool negateTeam) + std::initializer_list const& excludedEntities) { if (!entity->hasComponent()) return std::bitset(); IntersectionBitSet intersections; - for (auto& collider : getColliders(groupLabels, teamLabels)) { + for (auto& collider : getColliders(groupLabels, excludedEntities)) { intersections |= getIntersection(entity, collider->entity, posMod); } return intersections; @@ -154,11 +147,10 @@ Entity* CollisionHandler::getAnyIntersection( Entity* entity, Vector2D posMod, std::initializer_list const& groupLabels, - std::initializer_list const& teamLabels, - bool negateTeam) + std::initializer_list const& excludedEntities) { if (!entity->hasComponent()) return nullptr; - for (auto& collider : getColliders(groupLabels, teamLabels)) { + for (auto& collider : getColliders(groupLabels, excludedEntities)) { SDL_Rect rect = entity->getComponent().collider + posMod; if (SDL_HasIntersection(&rect, &collider->collider)) { return collider->entity; @@ -178,11 +170,10 @@ bool CollisionHandler::getAnyIntersection( Entity* entity, Vector2D posMod, std::initializer_list const& groupLabels, - std::initializer_list const& teamLabels, - bool negateTeam) + std::initializer_list const& excludedEntities) { if (!entity->hasComponent()) return false; - for (auto& collider : getColliders(groupLabels, teamLabels)) { + for (auto& collider : getColliders(groupLabels, excludedEntities)) { SDL_Rect rect = entity->getComponent().collider + posMod; if (SDL_HasIntersection(&rect, &collider->collider)) { return true; diff --git a/src/Entity.cpp b/src/Entity.cpp index 7ed2f32..60455c1 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -9,11 +9,6 @@ void Entity::update() const for (auto const& c : components) c->update(); } -void Entity::draw() const -{ - for (auto const& c : components) c->draw(); -} - bool Entity::hasGroup(Group mGroup) { return groupBitSet[mGroup]; @@ -30,18 +25,7 @@ void Entity::delGroup(Group mGroup) groupBitSet[mGroup] = false; } -std::bitset Entity::getGroupBitSet() +std::bitset Entity::getGroupBitSet() { return groupBitSet; } - -void Entity::setTeam(Entity::TeamLabel teamLabel) -{ - this->teamLabel = teamLabel; - manager.addToTeam(this, (size_t) teamLabel); -} - -Entity::TeamLabel Entity::getTeam() -{ - return teamLabel; -} diff --git a/src/Game.cpp b/src/Game.cpp deleted file mode 100644 index 475ab6f..0000000 --- a/src/Game.cpp +++ /dev/null @@ -1,390 +0,0 @@ -#include "Game.h" - -#include - -#include "CollisionHandler.h" -#include "Components.h" -#include "AssetManager.h" -#include "Direction.h" -#include "Entity.h" -#include "HealthComponent.h" -#include "Map.h" -#include "TextureManager.h" -#include "StatEffectsComponent.h" -#include "Constants.h" - -Map* map; -Manager manager; - -AssetManager* Game::assets = new AssetManager(&manager); -TextureManager* Game::textureManager = new TextureManager(); -SoundManager* Game::soundManager = new SoundManager(); - -CollisionHandler* Game::collisionHandler = new CollisionHandler(manager); - -SDL_Renderer* Game::renderer = nullptr; - -SDL_Event Game::event; - -auto& player1(manager.addEntity()); -auto& player2(manager.addEntity()); - -auto& wall(manager.addEntity()); - -Game::Game() = default; - -Game::~Game() = default; - -void Game::init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen) -{ - int flags = 0; - if (fullscreen) - { - flags = SDL_WINDOW_FULLSCREEN; - } - - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) - { - std::cout << "ERROR. Subsystem couldnt be initialized! " << SDL_GetError() << std::endl; - SDL_ClearError(); - return; - } - - if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) { - std::cout << "ERROR. Subsystem couldnt be initialized!" << std::endl; - return; - } - - window = SDL_CreateWindow(title, xpos, ypos, width, height, flags); - if (!window) - { - std::cout << "ERROR: Window couldnt be created! " << SDL_GetError() << std::endl; - SDL_ClearError(); - return; - } - - SDL_Surface* icon = SDL_LoadBMP("assets/iconImage.bmp"); - if(!icon) - { - std::cout << "ERROR: Couldn't create icon!" << std::endl; - return; - } - - SDL_SetWindowIcon(window, icon); - - renderer = SDL_CreateRenderer(window, -1, 0); - if (!renderer) - { - std::cout << "ERROR: Renderer couldnt be created! " << SDL_GetError() << std::endl; - SDL_ClearError(); - return; - } - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - - SDL_Texture* backgroundTexture = Game::textureManager->loadTexture("assets/startscreen.png"); - - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, backgroundTexture, NULL, NULL); - SDL_RenderPresent(renderer); - - if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) - { - std::cout << "ERROR: Mixer couldnt be initialized! " << SDL_GetError() << std::endl; - SDL_ClearError(); - return; - } - - Mix_Volume(-1, MIX_MAX_VOLUME); - Mix_AllocateChannels(16); - - //SDL_Event event; - bool hasQuit = false; - - while (!hasQuit) - { - SDL_PollEvent(&event); - - if (event.type == SDL_QUIT) - { - hasQuit = true; - break; - } - - if (event.type == SDL_KEYDOWN) - { - if (event.key.keysym.scancode == SDL_SCANCODE_RETURN) - { - std::cout << "Enter pressed > Game start..." << std::endl; - break; - } - - if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) - { - std::cout << "Escape pressed > Game quit..." << std::endl; - hasQuit = true; - } - } - } - - if (hasQuit) - { - this->isRunning = false; - return; - } - - // character selection - const char* player1Sprite; - const char* player2Sprite; - - selectCharacters(player1Sprite, player2Sprite); - if (this->isRunning == false) return; - - map = new Map(); - if (!map->loadMap("assets/SDL_map_test.txt", 25, 20)) { - std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl; - SDL_ClearError(); - }; - - - //adding textures to the library in AssetManager - - /* - assets->addTexture("player1", "assets/chicken_neutral_knight.png"); - assets->addTexture("player2", "assets/chicken_neutral.png"); - assets->addTexture("egg", "assets/egg.png"); - */ - // loading sounds - assets->addSoundEffect("throw_egg", "assets/sound/throw_egg.wav"); - assets->addSoundEffect("steps", "assets/sound/steps.wav"); - - //ecs implementation - - player1.setTeam(Entity::TeamLabel::BLUE); - player1.addComponent(80,80,2); //posx, posy, scale - player1.addComponent(player1Sprite, true); //adds sprite (32x32px), path needed - player1.addComponent(SDL_SCANCODE_W, SDL_SCANCODE_S, SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_E, Vector2D(2, 0));//custom keycontrols can be added - player1.addComponent("player", 0.8f); //adds tag (for further use, reference tag) - player1.addComponent(5, Direction::LEFT); - player1.addComponent(); - player1.addGroup((size_t) Entity::GroupLabel::PLAYERS); //tell programm what group it belongs to for rendering order - - - player2.setTeam(Entity::TeamLabel::RED); - player2.addComponent(600, 500, 2); - player2.addComponent(player2Sprite, true); - player2.addComponent(SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_RCTRL, Vector2D(-2, 0)); - player2.addComponent("enemy", 0.8f); - player2.addComponent(5, Direction::RIGHT); - player2.addComponent(); - player2.addGroup((size_t) Entity::GroupLabel::PLAYERS); -} - -void Game::selectCharacters(const char* &playerSprite, const char* &enemySprite) -{ - // TODO: move this whereever it makes sense (maybe game as a member) - std::map> characterSprites; - characterSprites[0] = std::make_pair("assets/chicken_neutral_knight.png", "assets/chicken_knight_spritesheet.png"); - characterSprites[1] = std::make_pair("assets/chicken_neutral.png", "assets/chicken_spritesheet.png"); - characterSprites[2] = std::make_pair("assets/chicken_neutral_wizard.png", "assets/chicken_wizard_spritesheet.png"); - characterSprites[3] = std::make_pair("assets/chicken_neutral_mlady.png", "assets/chicken_mlady_spritesheet.png"); - - SDL_Rect playerCharacterRects[CHARACTER_COUNT]; - SDL_Rect enemyCharacterRects[CHARACTER_COUNT]; - SDL_Texture* characterTextures[CHARACTER_COUNT]; - - int playerSelection = 0; - int enemySelection = 0; - - // load textures - for (int i = 0; i < CHARACTER_COUNT; ++i) - { - characterTextures[i] = IMG_LoadTexture(renderer, characterSprites.find(i)->second.first); - } - - // set up initial positions for character rects - for (int i = 0; i < CHARACTER_COUNT; ++i) - { - playerCharacterRects[i] = { 134 + (i % 2) * 118, 272 + ((i >= 2) ? 114 : 0), 64, 64 }; - enemyCharacterRects[i] = { 485 + (i % 2) * 118, 273 + ((i >= 2) ? 114 : 0), 64, 64 }; - } - - bool hasQuit = false; - - while (!hasQuit) - { - SDL_PollEvent(&event); - - if (event.type == SDL_QUIT) - { - hasQuit = true; - } - - if (event.type == SDL_KEYDOWN) - { - if (event.key.keysym.scancode == SDL_SCANCODE_RETURN) - { - break; - } - - switch (event.key.keysym.scancode) - { - case SDL_SCANCODE_A: - playerSelection = (playerSelection - 1 + CHARACTER_COUNT) % CHARACTER_COUNT; - break; - case SDL_SCANCODE_D: - playerSelection = (playerSelection + 1) % CHARACTER_COUNT; - break; - - case SDL_SCANCODE_LEFT: - enemySelection = (enemySelection - 1 + CHARACTER_COUNT) % CHARACTER_COUNT; - break; - case SDL_SCANCODE_RIGHT: - enemySelection = (enemySelection + 1) % CHARACTER_COUNT; - break; - - default: - break; - } - } - - SDL_Texture* backgroundTexture = Game::textureManager->loadTexture("assets/characterSelection.png"); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, backgroundTexture, NULL, NULL); - - for (int i = 0; i < CHARACTER_COUNT; ++i) - { - SDL_RenderCopy(renderer, characterTextures[i], nullptr, &playerCharacterRects[i]); - SDL_RenderCopy(renderer, characterTextures[i], nullptr, &enemyCharacterRects[i]); - } - - SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); - SDL_RenderDrawRect(renderer, &playerCharacterRects[playerSelection]); - SDL_RenderDrawRect(renderer, &enemyCharacterRects[enemySelection]); - - SDL_RenderPresent(renderer); - } - - if (hasQuit) - { - this->isRunning = false; - return; - } - - playerSprite = characterSprites.find(playerSelection)->second.second; - enemySprite = characterSprites.find(enemySelection)->second.second; - this->isRunning = true; -} - -auto& tiles(manager.getGroup((size_t)Entity::GroupLabel::MAPTILES)); -auto& players(manager.getGroup((size_t)Entity::GroupLabel::PLAYERS)); -auto& projectiles(manager.getGroup((size_t)Entity::GroupLabel::PROJECTILE)); -auto& hearts(manager.getGroup((size_t)Entity::GroupLabel::HEARTS)); -auto& powerups(manager.getGroup((size_t)Entity::GroupLabel::POWERUPS)); - -void Game::handleEvents() -{ - SDL_PollEvent(&event); - - switch (event.type) - { - case SDL_QUIT: this->isRunning = false; - break; - - default: - break; - } -} - -void Game::update() -{ - Vector2D playerPos = player1.getComponent().position; - Vector2D enemyPos = player2.getComponent().position; - - int powerupSpawn = rand() % 500; - - manager.refresh(); - manager.update(); - - if (powerupSpawn == 0) - { - assets->createPowerup(assets->calculateSpawnPosition(), assets->calculateType()); - } - - // needs to be in game.cpp to have access to internal functions - for (auto& player : manager.getGroup((size_t) Entity::GroupLabel::PLAYERS)) { - if (player->getComponent().getHealth() <= 0) { - this->setWinner(player->getTeam()); - } - } -} - -void Game::render() -{ - SDL_RenderClear(renderer); - for (auto& t : tiles) - t->draw(); - - for (auto& p : powerups) - p->draw(); - - for (auto& p : players) - p->draw(); - - for (auto& p : projectiles) - p->draw(); - - for (auto& h : hearts) - h->draw(); - - SDL_RenderPresent(renderer); -} - -void Game::clean() -{ - delete(textureManager); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - std::cout << "Game Cleaned!" << std::endl; -} - -void Game::addTile(unsigned long id, int x, int y) -{ - auto& tile(manager.addEntity()); - tile.addComponent(x, y, TILE_SIZE, TILE_SIZE, id); - if (id == 1) tile.addComponent("water"); - tile.addGroup((size_t)Entity::GroupLabel::MAPTILES); -} - -bool Game::running() const -{ - return isRunning; -} - -void Game::setWinner(Entity::TeamLabel winningTeam) -{ - this->winner = winningTeam; - this->isRunning = false; -} - -Entity::TeamLabel Game::getWinner() const -{ - return this->winner; -} - -void Game::refreshPlayers() { - - for(auto& p : projectiles) { - p->destroy(); - } - - player1.getComponent().position = Vector2D(80, 80); - player2.getComponent().position = Vector2D(600, 500); - - player1.getComponent().setHealth(5); - player2.getComponent().setHealth(5); - - isRunning = true; - - update(); -} diff --git a/src/GameInternal.cpp b/src/GameInternal.cpp new file mode 100644 index 0000000..7f1ac27 --- /dev/null +++ b/src/GameInternal.cpp @@ -0,0 +1,159 @@ +#include "GameInternal.h" + +#include + +#include "CollisionHandler.h" +#include "AssetManager.h" +#include "RenderManager.h" +#include "SDL_mixer.h" +#include "SoundManager.h" +#include "TileComponent.h" +#include "Direction.h" +#include "Entity.h" +#include "HealthComponent.h" +#include "Map.h" +#include "TextureManager.h" +#include "StatEffectsComponent.h" +#include "Constants.h" +#include "Game.h" +#include "GameFactory.h" + +GameInternal::GameInternal() : + manager(this), + renderManager(), + tiles(manager.getGroup((size_t)Entity::GroupLabel::MAPTILES)), + players(manager.getGroup((size_t)Entity::GroupLabel::PLAYERS)), + projectiles(manager.getGroup((size_t)Entity::GroupLabel::PROJECTILE)), + hearts(manager.getGroup((size_t)Entity::GroupLabel::HEARTS)), + powerups(manager.getGroup((size_t)Entity::GroupLabel::POWERUPS)) +{}; + +GameInternal::~GameInternal() = default; + +void GameInternal::init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen) +{ + GameInternal::assets = new AssetManager(&manager); + GameInternal::textureManager = new TextureManager(&manager); + GameInternal::soundManager = new SoundManager(); + GameInternal::collisionHandler = new CollisionHandler(manager); // why does this use a referrence, but AssetManager a pointer? + + int flags = 0; + if (fullscreen) + { + flags = SDL_WINDOW_FULLSCREEN; + } + + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) + { + std::cout << "ERROR. Subsystem couldnt be initialized! " << SDL_GetError() << std::endl; + SDL_ClearError(); + return; + } + + if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) { + std::cout << "ERROR. Subsystem couldnt be initialized!" << std::endl; + return; + } + + window = SDL_CreateWindow(title, xpos, ypos, width, height, flags); + if (!window) + { + std::cout << "ERROR: Window couldnt be created! " << SDL_GetError() << std::endl; + SDL_ClearError(); + return; + } + + // bad + SDL_Surface* icon; + if((icon = SDL_LoadBMP("assets/iconImage.bmp"))) + { + SDL_SetWindowIcon(window, icon); + } + + SDL_SetWindowIcon(window, icon); + + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) + { + std::cout << "ERROR: Renderer couldnt be created! " << SDL_GetError() << std::endl; + SDL_ClearError(); + return; + } + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) + { + std::cout << "ERROR: Mixer couldnt be initialized! " << SDL_GetError() << std::endl; + SDL_ClearError(); + return; + } + + Mix_Volume(-1, MIX_MAX_VOLUME); + Mix_AllocateChannels(16); + + map = new Map(); + + // loading sounds + // assets->addSoundEffect("throw_egg", "assets/sound/throw_egg.wav"); + // assets->addSoundEffect("steps", "assets/sound/steps.wav"); + + // loading music + // assets->addMusic("background_music", "assets/sound/background_music.mp3"); + + this->gameInstance = GameFactory::instance().create(this); + this->gameInstance->init(); +} + +void GameInternal::handleEvents() +{ + SDL_PollEvent(&event); + + switch (event.type) + { + case SDL_QUIT: this->setRunning(false); + break; + + default: + break; + } +} + +void GameInternal::update() +{ + manager.refresh(); + manager.update(); + + this->gameInstance->update(); // TODO: this might have to be split up into two update functions, before and after manager... +} + +void GameInternal::render() +{ + SDL_RenderClear(renderer); + this->renderManager.renderAll(); + SDL_RenderPresent(renderer); +} + +void GameInternal::clean() +{ + delete(textureManager); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + std::cout << "Game Cleaned!" << std::endl; +} + +bool GameInternal::isRunning() const +{ + return running; +} + +void GameInternal::setRunning(bool running) //TODO: might be depracted +{ + this->running = running; +} + +void GameInternal::stopGame() +{ + this->running = false; +} + diff --git a/src/GameObject.cpp b/src/GameObject.cpp deleted file mode 100644 index fafa17b..0000000 --- a/src/GameObject.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "GameObject.h" - -#include "TextureManager.h" -#include "Game.h" - -GameObject::GameObject(const char* texturesheet, int x, int y) -{ - this->objTexture = Game::textureManager->loadTexture(texturesheet); - this->xPos = x; - this->yPos = y; -} - -void GameObject::update() -{ - xPos++; - yPos++; - - srcRect.h = 32; - srcRect.w = 32; - srcRect.x = 0; - srcRect.y = 0; - - destRect.h = srcRect.h *2; - destRect.w = srcRect.w *2; - destRect.x = xPos; - destRect.y = yPos; -} - -void GameObject::render() -{ - SDL_RenderCopy(Game::renderer, objTexture, &srcRect, &destRect); -} diff --git a/src/HealthComponent.cpp b/src/HealthComponent.cpp index 7bbaa60..ed39629 100644 --- a/src/HealthComponent.cpp +++ b/src/HealthComponent.cpp @@ -1,64 +1,19 @@ #include "HealthComponent.h" -#include "Components.h" #include "Direction.h" #include "Entity.h" -#include "Game.h" +#include "GameInternal.h" #include void HealthComponent::init() -{ - refreshHearts(); -} +{} void HealthComponent::modifyHealth(int health) { this->health += health; - this->refreshHearts(); } void HealthComponent::setHealth(int health) { this->health = health; - this->refreshHearts(); -} - -void HealthComponent::refreshHearts() -{ - // clear hearts if exist - for (auto& heart : this->entity->getManager().getGroup((size_t) Entity::GroupLabel::HEARTS)) { - if (heart->getTeam() == this->entity->getTeam()) { - heart->destroy(); - } - } - - int x; //starting position for first health icon - - if(side == Direction::LEFT) { - x = 10; - } else { - x = 730; - } - - for(int i = 0; i < health; i++) { - - //checks for player side - if (side == Direction::LEFT) { - createHeartComponents(x); - x += 50; - continue; - } - - createHeartComponents(x); - x -= 50; - } -} - -void HealthComponent::createHeartComponents(int x) -{ - auto& heart(this->entity->getManager().addEntity()); - heart.addComponent(x,5,2); - heart.addComponent("assets/heart.png"); - heart.addGroup((size_t)Entity::GroupLabel::HEARTS); - heart.setTeam(this->entity->getTeam()); } \ No newline at end of file diff --git a/src/InputComponent.cpp b/src/InputComponent.cpp new file mode 100644 index 0000000..ac26b32 --- /dev/null +++ b/src/InputComponent.cpp @@ -0,0 +1,117 @@ +#include "InputComponent.h" + +InputComponent::InputComponent() +{ + m_keyStates = SDL_GetKeyboardState(NULL); + InitKeyMappings(); +} + +InputComponent::~InputComponent() = default; + +void InputComponent::init(){} + +void InputComponent::update() +{ + SDL_PumpEvents(); +} + +bool InputComponent::isKeyDown(Key key) +{ + return m_keyStates[mapKeyToSDL(key)]; +} + +SDL_Scancode InputComponent::mapKeyToSDL(Key key) +{ + auto it = m_keyMappings.find(key); + if (it == m_keyMappings.end()) + { + return SDL_SCANCODE_UNKNOWN; + } + return it->second; +} + +void InputComponent::InitKeyMappings() +{ + m_keyMappings = + { + {Key::UP, SDL_SCANCODE_UP}, + {Key::DOWN, SDL_SCANCODE_DOWN}, + {Key::LEFT, SDL_SCANCODE_LEFT}, + {Key::RIGHT, SDL_SCANCODE_RIGHT}, + {Key::SPACE, SDL_SCANCODE_SPACE}, + {Key::ENTER, SDL_SCANCODE_RETURN}, + {Key::ESCAPE, SDL_SCANCODE_ESCAPE}, + {Key::TAB, SDL_SCANCODE_TAB}, + {Key::BACKSPACE, SDL_SCANCODE_BACKSPACE}, + {Key::DELETE, SDL_SCANCODE_DELETE}, + {Key::HOME, SDL_SCANCODE_HOME}, + {Key::END, SDL_SCANCODE_END}, + {Key::PAGE_UP, SDL_SCANCODE_PAGEUP}, + {Key::PAGE_DOWN, SDL_SCANCODE_PAGEDOWN}, + {Key::INSERT, SDL_SCANCODE_INSERT}, + {Key::CAPS_LOCK, SDL_SCANCODE_CAPSLOCK}, + {Key::LEFT_SHIFT, SDL_SCANCODE_LSHIFT}, + {Key::RIGHT_SHIFT, SDL_SCANCODE_RSHIFT}, + {Key::LEFT_CTRL, SDL_SCANCODE_LCTRL}, + {Key::RIGHT_CTRL, SDL_SCANCODE_RCTRL}, + {Key::LEFT_ALT, SDL_SCANCODE_LALT}, + {Key::RIGHT_ALT, SDL_SCANCODE_RALT}, + {Key::F1, SDL_SCANCODE_F1}, + {Key::F2, SDL_SCANCODE_F2}, + {Key::F3, SDL_SCANCODE_F3}, + {Key::F4, SDL_SCANCODE_F4}, + {Key::F5, SDL_SCANCODE_F5}, + {Key::F6, SDL_SCANCODE_F6}, + {Key::F7, SDL_SCANCODE_F7}, + {Key::F8, SDL_SCANCODE_F8}, + {Key::F9, SDL_SCANCODE_F9}, + {Key::F10, SDL_SCANCODE_F10}, + {Key::F11, SDL_SCANCODE_F11}, + {Key::F12, SDL_SCANCODE_F12}, + {Key::A, SDL_SCANCODE_A}, + {Key::B, SDL_SCANCODE_B}, + {Key::C, SDL_SCANCODE_C}, + {Key::D, SDL_SCANCODE_D}, + {Key::E, SDL_SCANCODE_E}, + {Key::F, SDL_SCANCODE_F}, + {Key::G, SDL_SCANCODE_G}, + {Key::H, SDL_SCANCODE_H}, + {Key::I, SDL_SCANCODE_I}, + {Key::J, SDL_SCANCODE_J}, + {Key::K, SDL_SCANCODE_K}, + {Key::L, SDL_SCANCODE_L}, + {Key::M, SDL_SCANCODE_M}, + {Key::N, SDL_SCANCODE_N}, + {Key::O, SDL_SCANCODE_O}, + {Key::P, SDL_SCANCODE_P}, + {Key::Q, SDL_SCANCODE_Q}, + {Key::R, SDL_SCANCODE_R}, + {Key::S, SDL_SCANCODE_S}, + {Key::T, SDL_SCANCODE_T}, + {Key::U, SDL_SCANCODE_U}, + {Key::V, SDL_SCANCODE_V}, + {Key::W, SDL_SCANCODE_W}, + {Key::X, SDL_SCANCODE_X}, + {Key::Y, SDL_SCANCODE_Y}, + {Key::Z, SDL_SCANCODE_Z}, + {Key::NUM_0, SDL_SCANCODE_0}, + {Key::NUM_1, SDL_SCANCODE_1}, + {Key::NUM_2, SDL_SCANCODE_2}, + {Key::NUM_3, SDL_SCANCODE_3}, + {Key::NUM_4, SDL_SCANCODE_4}, + {Key::NUM_5, SDL_SCANCODE_5}, + {Key::NUM_6, SDL_SCANCODE_6}, + {Key::NUM_7, SDL_SCANCODE_7}, + {Key::NUM_8, SDL_SCANCODE_8}, + {Key::NUM_9, SDL_SCANCODE_9}, + {Key::LEFT_BRACKET, SDL_SCANCODE_LEFTBRACKET}, + {Key::RIGHT_BRACKET, SDL_SCANCODE_RIGHTBRACKET}, + {Key::SEMICOLON, SDL_SCANCODE_SEMICOLON}, + {Key::APOSTROPHE, SDL_SCANCODE_APOSTROPHE}, + {Key::COMMA, SDL_SCANCODE_COMMA}, + {Key::PERIOD, SDL_SCANCODE_PERIOD}, + {Key::SLASH, SDL_SCANCODE_SLASH}, + {Key::BACKSLASH, SDL_SCANCODE_BACKSLASH}, + {Key::GRAVE, SDL_SCANCODE_GRAVE} + }; +} diff --git a/src/KeyboardController.cpp b/src/KeyboardController.cpp deleted file mode 100644 index 26324af..0000000 --- a/src/KeyboardController.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "KeyboardController.h" - -#include "Game.h" -#include "Components.h" -#include "AssetManager.h" -#include "SpriteComponent.h" - -KeyboardController::KeyboardController(SDL_Scancode up, SDL_Scancode down, SDL_Scancode left, SDL_Scancode right, SDL_Scancode fire, Vector2D fireVelocity) -{ - this->up = up; - this->down = down; - this->left = left; - this->right = right; - this->fire = fire; - this->fireVelocity = fireVelocity; -} - -void KeyboardController::init() -{ - sprite = &entity->getComponent(); - transform = &entity->getComponent(); -} - -void KeyboardController::update() -{ - transform->direction.x = 0; - transform->direction.y = 0; - sprite->playAnimation(IDLE); - - if (keystates[this->up]) { - transform->direction.y = -1; - sprite->playAnimation(WALK); - SoundManager::playSound(STEPS); - } - if (keystates[this->left]) { - transform->direction.x = -1; - sprite->playAnimation(WALK); - sprite->setDirection(Direction::LEFT); - SoundManager::playSound(STEPS); - } - if (keystates[this->down]) { - transform->direction.y = 1; - sprite->playAnimation(WALK); - SoundManager::playSound(STEPS); - } - if (keystates[this->right]) { - transform->direction.x = 1; - sprite->playAnimation(WALK); - sprite->setDirection(Direction::RIGHT); - SoundManager::playSound(STEPS); - } - - if (keystates[this->fire]) { - - Uint32 currentTicks = SDL_GetTicks(); - - if (currentTicks - lastFireTime >= fireCooldown) { - - player = &entity->getComponent(); - - //checks player source via the firing velocity - //TODO: adding actual projectile textures - if (fireVelocity.x > 0) { - sprite->setDirection(Direction::RIGHT); - Game::assets->createProjectile(Vector2D(player->position.x, player->position.y), fireVelocity, - 1, 180, 2, "assets/egg.png", this->entity->getTeam()); - } - else { - sprite->setDirection(Direction::LEFT); - Game::assets->createProjectile(Vector2D(player->position.x, player->position.y), fireVelocity, - 1, 180, 2, "assets/egg.png", this->entity->getTeam()); - } - - lastFireTime = currentTicks; - } - } -} - -void KeyboardController::modifyAtkSpeed(int8_t modifier) -{ - this->fireCooldown -= modifier * 400; -} \ No newline at end of file diff --git a/src/Manager.cpp b/src/Manager.cpp index f98ebdc..09eea97 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -6,11 +6,6 @@ #include "Constants.h" #include "Entity.h" -void Manager::draw() -{ - for (auto& e : entities) e->draw(); -} - void Manager::refresh() { for (auto i(0u); i < MAX_GROUPS; i++) @@ -24,17 +19,6 @@ void Manager::refresh() }), std::end(v)); } - for (auto i(0u); i < MAX_TEAMS; i++) - { - auto& v(entitiesByTeam[i]); - v.erase( - std::remove_if(std::begin(v), std::end(v), - [i](Entity* mEntity) - { - return !mEntity->isActive() || (size_t)(mEntity->getTeam()) != i; - }), std::end(v)); - } - entities.erase(std::remove_if(std::begin(entities), std::end(entities), [](const std::unique_ptr& mEntity) { @@ -58,16 +42,6 @@ std::vector& Manager::getGroup(Group mGroup) return entitiesByGroup.at(mGroup); } -void Manager::addToTeam(Entity* mEntity, Team mTeam) -{ - entitiesByTeam.at(mTeam).emplace_back(mEntity); // -} - -std::vector& Manager::getTeam(Team mTeam) -{ - return entitiesByTeam.at(mTeam); -} - std::vector Manager::getAll() { std::vector entity_vec; diff --git a/src/Map.cpp b/src/Map.cpp index 7f65563..7204be2 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -1,15 +1,32 @@ #include "Map.h" +#include #include +#include #include #include -#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include #include "Constants.h" -#include "Game.h" -#include "SDL_error.h" +#include "GameInternal.h" +#include "SpriteComponent.h" +#include "TextureManager.h" +#include "TileComponent.h" +#include "VEGO.h" +#include "tmxlite/Types.hpp" -bool Map::loadMap(const char* path, int sizeX, int sizeY) +void Map::loadMap(const char* path, int sizeX, int sizeY, GameInternal* game, const std::map>* textureDict /* backreference */) { std::string tileIDstr; char singleChar = 0; @@ -18,23 +35,24 @@ bool Map::loadMap(const char* path, int sizeX, int sizeY) if (!mapFile.is_open()) { SDL_SetError("Error loading map: Couldn't open map file!"); - return false; + std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl; + SDL_ClearError(); } int x = 0, y = 0; // needed outside for-loop for error handling - bool success = true; for (; !mapFile.eof(); mapFile.get(singleChar)) { if (singleChar == ',' || singleChar == '\n') { if (tileIDstr.empty()) continue; - Game::addTile(std::stoi(tileIDstr), x * TILE_SIZE, y * TILE_SIZE); + Map::addTile(std::stoi(tileIDstr), x * TILE_SIZE, y * TILE_SIZE, game, textureDict); tileIDstr.clear(); x++; if (singleChar == '\n') { if (x != sizeX) { SDL_SetError("Error loading map: specified x size doesn't match map file!"); - success = false; + std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl; + SDL_ClearError(); } x = 0; y++; @@ -47,10 +65,107 @@ bool Map::loadMap(const char* path, int sizeX, int sizeY) } if (y != sizeY) { SDL_SetError("Error loading map: specified y size doesn't match map file!"); - success = false; + std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl; + SDL_ClearError(); } mapFile.close(); - - return success; } + +void Map::addTile(unsigned long id, int x, int y, GameInternal* game, const std::map>* textureDict) // tile entity +{ + auto& tile(game->manager.addEntity()); + tile.addComponent(x, y, TILE_SIZE, TILE_SIZE, id, textureDict); + + if(tile.getComponent().hasCollision()) tile.addComponent("tile"/*tile.getComponent().getName().data()*/); + tile.addGroup((size_t)Entity::GroupLabel::MAPTILES); +} + +void Map::loadMapTmx(const char* path) +{ + tmx::Map map; + if (!map.load(path)) { + // TODO: log to console + } + + const std::vector& tileSets = map.getTilesets(); + + const std::vector& mapLayers = map.getLayers(); + const auto mapSize = map.getTileCount(); + const auto mapTileSize = map.getTileSize(); + + std::vector texturePaths = {}; + + for (auto tileSet : tileSets) { + texturePaths.emplace_back(tileSet.getImagePath()); + } + + for (auto& layer : mapLayers) { + + if (layer->getType() == tmx::Layer::Type::Tile) { + auto& tileLayer = layer->getLayerAs(); + + int zIndex = 0; + + const std::vector& properties = layer->getProperties(); + auto zIndexIterator = std::find_if(properties.begin(), properties.end(), [](const tmx::Property& property) { + return property.getName() == "zIndex"; + }); + + if (zIndexIterator != properties.end() && std::is_nothrow_convertiblegetType()), int>::value) { + zIndex = zIndexIterator->getIntValue(); + } + + const auto& tiles = tileLayer.getTiles(); + + for (auto i = 0u; i < tileSets.size(); i++) { + auto tilesetTexture = VEGO_Game().textureManager->loadTexture(texturePaths.at(i).c_str()); + tmx::Vector2i textureSize; + SDL_QueryTexture(tilesetTexture, nullptr, nullptr, &(textureSize.x), &(textureSize.y)); + + const auto tileCountX = textureSize.x / mapTileSize.x; + const auto tileCountY = textureSize.y / mapTileSize.y; + + for (auto idx = 0ul; idx < mapSize.x * mapSize.y; idx++) { + + if (idx >= tiles.size() || tiles[idx].ID < tileSets.at(i).getFirstGID() + || tiles[idx].ID >= (tileSets.at(i).getFirstGID() + tileSets.at(i).getTileCount())) { + continue; + } + + const auto x = idx % mapSize.x; + const auto y = idx / mapSize.x; + + auto idIndex = (tiles[idx].ID - tileSets.at(i).getFirstGID()); + + int u = idIndex % tileCountX; + int v = idIndex / tileCountY; + u *= mapTileSize.x; //TODO we should be using the tile set size, as this may be different from the map's grid size + v *= mapTileSize.y; + + //normalise the UV + u /= textureSize.x; + v /= textureSize.y; + + //vert pos + const float tilePosX = static_cast(x) * mapTileSize.x; + const float tilePosY = (static_cast(y) * mapTileSize.y); + + Map::addTile(tilePosX, tilePosY, mapTileSize, u, v, zIndex, texturePaths.at(i).c_str()); + } + } + if (layer->getType() == tmx::Layer::Type::Object) { + // spawn objects + continue; + } + } + } +} + +void Map::addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, const char* texturePath) +{ + auto& tile(VEGO_Game().manager.addEntity()); + + tile.addComponent(x, y, mapTileSize.x, mapTileSize.y, 1); + tile.addComponent(texturePath, v, u, zIndex); // why does uv need to be reversed? +} \ No newline at end of file diff --git a/src/PopupWindow.cpp b/src/PopupWindow.cpp deleted file mode 100644 index 5912eaf..0000000 --- a/src/PopupWindow.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include - -#include "Entity.h" -#include "PopupWindow.h" -#include "TextureManager.h" -#include "Game.h" - -PopupWindow::PopupWindow(const char* title, const std::string &message) : -continueGame(false), interacted(false) { - this->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 400, 250, 0); - //font = TTF_OpenFont("assets/Trajan.ttf", 24); // Change the path and size as needed - - this->renderer = SDL_CreateRenderer(window, -1, 0); - SDL_SetRenderDrawColor(this->renderer, 255, 255, 255, 255); - - //SDL_Surface* surface = TTF_RenderText_Blended(font, message.c_str(), {255, 255, 255}); - //texture = SDL_CreateTextureFromSurface(renderer, surface); - - //SDL_FreeSurface(surface); -} - -PopupWindow::~PopupWindow() { - SDL_DestroyTexture(this->texture); - SDL_DestroyRenderer(this->renderer); - SDL_DestroyWindow(this->window); -} - -void PopupWindow::handleWinnerEvents() { - - SDL_Event e; - - while (SDL_PollEvent(&e)) - { - if (e.type == SDL_QUIT) - { - continueGame = false; - interacted = true; - return; - } - - if(e.type != SDL_KEYDOWN) - continue; - - switch (e.key.keysym.sym) { - - case SDLK_q: { - continueGame = false; - interacted = true; - break; - } - case SDLK_c: { - continueGame = true; - interacted = true; - break; - } - } - } -} - -bool PopupWindow::shouldContinue() const { - return continueGame; -} - -void PopupWindow::renderWinnerPopup(Entity::TeamLabel winner) { - - SDL_RenderClear(this->renderer); - - //Maybe use texture manager (changes need to be made that it does not use game::renderer automatically, but receives one instead) - this->texture = winner == Entity::TeamLabel::BLUE ? - IMG_LoadTexture(this->renderer, "assets/Player1Victory.png") : - IMG_LoadTexture(this->renderer, "assets/Player2Victory.png"); - - SDL_RenderCopy(this->renderer, this->texture, NULL, NULL); - - SDL_RenderPresent(this->renderer); - - //Error handling for debugging - const char* sdlError = SDL_GetError(); - if (*sdlError != '\0') { - std::cerr << "SDL Error: " << sdlError << std::endl; - SDL_ClearError(); - } - -} diff --git a/src/PowerupComponent.cpp b/src/PowerupComponent.cpp index 18447f6..5c1df71 100644 --- a/src/PowerupComponent.cpp +++ b/src/PowerupComponent.cpp @@ -1,5 +1,5 @@ #include "PowerupComponent.h" -#include "Game.h" +#include "GameInternal.h" #include "CollisionHandler.h" #include "Entity.h" #include "HealthComponent.h" @@ -7,51 +7,20 @@ #include "Constants.h" #include -PowerupComponent::PowerupComponent(PowerupType type) +PowerupComponent::PowerupComponent(std::function func) { - switch (type) - { - case PowerupType::HEART: - this->pickupFunc = (&PowerupComponent::heartEffect); - break; - case PowerupType::WALKINGSPEED: - this->pickupFunc = (&PowerupComponent::movementSpeedEffect); - break; - case PowerupType::SHOOTINGSPEED: - this->pickupFunc = (&PowerupComponent::atkSpeedEffect); - break; - default: - break; - } + this->pickupFunc = func; } void PowerupComponent::update() { Entity* player; - if ((player = Game::collisionHandler->getAnyIntersection( + if ((player = this->entity->getManager().getGame()->collisionHandler->getAnyIntersection( entity, Vector2D(0, 0), - { Entity::GroupLabel::PLAYERS }, - {}, - true)) != nullptr) + { Entity::GroupLabel::PLAYERS })) != nullptr) { - (this->*pickupFunc)(player); + (this->pickupFunc)(player); this->entity->destroy(); } -} - -void PowerupComponent::heartEffect(Entity* player) -{ - if(player->getComponent().getHealth() < 5) - player->getComponent().modifyHealth(1); -} - -void PowerupComponent::movementSpeedEffect(Entity* player) -{ - player->getComponent().modifyStatDur(Stats::MOVEMENT_SPEED, BUFF_DURATION); -} - -void PowerupComponent::atkSpeedEffect(Entity* player) -{ - player->getComponent().modifyStatDur(Stats::ATTACK_SPEED, BUFF_DURATION); } \ No newline at end of file diff --git a/src/ProjectileComponent.cpp b/src/ProjectileComponent.cpp index f28e366..11599c8 100644 --- a/src/ProjectileComponent.cpp +++ b/src/ProjectileComponent.cpp @@ -1,9 +1,10 @@ #include "ProjectileComponent.h" #include "CollisionHandler.h" -#include "Components.h" +#include "SoundManager.h" +#include "TransformComponent.h" #include "Entity.h" -#include "Game.h" +#include "GameInternal.h" #include "HealthComponent.h" #include "Vector2D.h" #include @@ -13,14 +14,14 @@ void ProjectileComponent::init() { transformComponent = &entity->getComponent(); transformComponent->direction = direction; - SoundManager::playSound(THROW_EGG); + SoundManager::playSound(this->entity->getManager().getGame(), "throw_egg", true, PLAY_ONCE, MAX_VOLUME, -1); } void ProjectileComponent::update() { distance += speed; - IntersectionBitSet boundsIntersection = Game::collisionHandler->getIntersectionWithBounds(entity); + IntersectionBitSet boundsIntersection = this->entity->getManager().getGame()->collisionHandler->getIntersectionWithBounds(entity); if ((boundsIntersection | IntersectionBitSet("1100")).all() || (boundsIntersection | IntersectionBitSet("0011")).all()) { this->entity->destroy(); @@ -31,12 +32,11 @@ void ProjectileComponent::update() } Entity* player; - if ((player = Game::collisionHandler->getAnyIntersection( + if ((player = this->entity->getManager().getGame()->collisionHandler->getAnyIntersection( entity, Vector2D(0,0), {Entity::GroupLabel::PLAYERS}, - {entity->getTeam()}, - true)) != nullptr) { + {this->owner})) != nullptr) { player->getComponent().modifyHealth(); this->entity->destroy(); } diff --git a/src/RenderManager.cpp b/src/RenderManager.cpp new file mode 100644 index 0000000..e2e82af --- /dev/null +++ b/src/RenderManager.cpp @@ -0,0 +1,24 @@ +#include "RenderManager.h" +#include "RenderObject.h" +#include + +void RenderManager::renderAll() +{ + if (!this->isSorted) { + std::ranges::sort(this->renderObjects, RenderObject::ZIndexComparator()); + } + for (RenderObject* obj : this->renderObjects) { + obj->draw(); + } +} + +void RenderManager::add(RenderObject* renderObject) { + this->renderObjects.emplace_back(renderObject); + this->isSorted = false; +} + +void RenderManager::remove(RenderObject* renderObject) +{ + this->renderObjects.erase(std::remove(this->renderObjects.begin(), this->renderObjects.end(), renderObject), this->renderObjects.end()); + this->isSorted = false; +} \ No newline at end of file diff --git a/src/RenderObject.cpp b/src/RenderObject.cpp new file mode 100644 index 0000000..5c21ee9 --- /dev/null +++ b/src/RenderObject.cpp @@ -0,0 +1,10 @@ +#include "RenderObject.h" +#include "RenderManager.h" + +RenderObject::RenderObject(int zIndex, RenderManager& renderManager) : zIndex(zIndex), renderManager(renderManager) { + renderManager.add(this); +} + +RenderObject::~RenderObject() { + this->renderManager.remove(this); +} \ No newline at end of file diff --git a/src/SoundManager.cpp b/src/SoundManager.cpp index 590aa46..2954b9f 100644 --- a/src/SoundManager.cpp +++ b/src/SoundManager.cpp @@ -4,9 +4,29 @@ #include #include -#include "Game.h" +#include "GameInternal.h" #include "AssetManager.h" +Mix_Music* SoundManager::loadMusic(const char* fileName) +{ + auto it = this->music_cache.find(fileName); + + if (it != this->music_cache.end()) { + return it->second; + } + + auto music = Mix_LoadMUS(fileName); + + if (music == NULL) + std::cerr << "Couldn't load music '" << fileName << "'" << std::endl; + + this->music_cache.emplace(fileName, music); + + std::cout << "Loaded music at " << fileName << std::endl; + + return music; +} + Mix_Chunk* SoundManager::loadSound(const char* fileName) { auto it = this->sound_cache.find(fileName); @@ -18,33 +38,98 @@ Mix_Chunk* SoundManager::loadSound(const char* fileName) auto sound = Mix_LoadWAV(fileName); if (sound == NULL) - throw std::runtime_error(std::string("Couldn't load sound '") + fileName + "'"); + std::cerr << "Couldn't load sound '" << fileName << "'" << std::endl; this->sound_cache.emplace(fileName, sound); - printf("Loaded sound at '%s'\n", fileName); + std::cout << "Loaded sound at " << fileName << std::endl; return sound; } -void SoundManager::playSound(SoundTypes sound) +void SoundManager::playSound(GameInternal* game, std::string sound, bool canOverlap, int loops, int volume, int channel) { - switch (sound) + if(!canOverlap) { - case SoundTypes::STEPS: - if (Mix_Playing(-1) != 0) - break; + // dev needs to specify a channel for this check to work, if they set it to -1 and let sdl pick the first available + // channel mix_getchunk() won't work + if (Mix_Playing(channel) != 0 && + Mix_GetChunk(channel) == game->assets->getSound(sound) && + channel != -1) + { + return; + } + + Mix_HaltChannel(channel); + } - if (Mix_PlayChannel(-1, Game::assets->getSound("steps"), 0) == -1) { - std::cerr << "Error playing sound 'steps': " << Mix_GetError() << std::endl; - } - - break; + if(Mix_VolumeChunk(game->assets->getSound(sound), volume) == -1) + { + std::cerr << "Error adjusting volume: " << Mix_GetError() << std::endl; + } - case SoundTypes::THROW_EGG: - if (Mix_PlayChannel(-1, Game::assets->getSound("throw_egg"), 0) == -1) { - std::cerr << "Error playing sound 'throw_egg': " << Mix_GetError() << std::endl; - } - break; + if (Mix_PlayChannel(channel, game->assets->getSound(sound), loops) == -1) + { + std::cerr << "Error playing sound '" << sound << "': " << Mix_GetError() << std::endl; } } + +void SoundManager::playMusic(GameInternal* game, std::string music, int loops, int volume, int ms) +{ + if (Mix_PlayingMusic() != 0 || Mix_Fading() == Mix_Fading::MIX_FADING_IN) + return; + + if(ms > 0) + { + Mix_FadeInMusic(game->assets->getMusic(music), loops, ms); + return; + } + + if(Mix_VolumeMusic(volume) == -1) + { + std::cerr << "Error adjusting volume: " << Mix_GetError() << std::endl; + } + + if (Mix_PlayMusic(game->assets->getMusic(music), loops) == -1) + { + std::cerr << "Error playing music '" << music << "': " << Mix_GetError() << std::endl; + } +} + +void SoundManager::setSoundVolume(int volume, int channel) +{ + Mix_Volume(channel, volume); +} + +void SoundManager::setMusicVolume(int volume) +{ + Mix_VolumeMusic(volume); +} + +void SoundManager::pauseSound(int channel) +{ + Mix_Pause(channel); +} + +void SoundManager::pauseMusic() +{ + Mix_PauseMusic(); +} + +void SoundManager::restartSound(int channel) +{ + Mix_Resume(channel); +} + +void SoundManager::restartMusic() +{ + Mix_ResumeMusic(); +} + +void SoundManager::fadeOutMusic(int ms) +{ + if(Mix_Fading() == Mix_Fading::MIX_FADING_OUT) + return; + + Mix_FadeOutMusic(ms); +} diff --git a/src/SpriteComponent.cpp b/src/SpriteComponent.cpp index bd7e3f0..ae27ea7 100644 --- a/src/SpriteComponent.cpp +++ b/src/SpriteComponent.cpp @@ -1,60 +1,75 @@ #include "SpriteComponent.h" #include +#include #include #include "AnimationHandler.h" #include "Direction.h" +#include "ProjectileComponent.h" +#include "RenderObject.h" #include "TextureManager.h" #include "Entity.h" #include "TransformComponent.h" -#include "Game.h" +#include "GameInternal.h" +#include "Manager.h" +#include "VEGO.h" -SpriteComponent::SpriteComponent(const char* path) +SpriteComponent::SpriteComponent(const char* path, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(0), textureYOffset(0) { - setTexture(path); + this->texturePath = path; } -SpriteComponent::SpriteComponent(const char* path, bool isAnimated) +SpriteComponent::SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(xOffset), textureYOffset(yOffset) +{ + this->texturePath = path; +} + +SpriteComponent::SpriteComponent( + const char* path, + bool isAnimated, + std::map>* animationMap, + std::string defaultAnimation, + int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(0), textureYOffset(0) { animated = isAnimated; - animations.emplace(IDLE, std::make_unique((uint8_t)AnimationType::IDLE, 2, 200)); - animations.emplace(WALK, std::make_unique((uint8_t)AnimationType::WALK, 2, 200)); + animations = animationMap; - playAnimation(IDLE); + playAnimation(defaultAnimation); - setTexture(path); + this->texturePath = path; } -SpriteComponent::~SpriteComponent() -{ - // SDL_DestroyTexture(this->texture); -} +SpriteComponent::~SpriteComponent() {} void SpriteComponent::setTexture(const char* path) { - this->texture = Game::textureManager->loadTexture(path); + this->texture = VEGO_Game().textureManager->loadTexture(path); } void SpriteComponent::init() { + setTexture(this->texturePath); + this->transform = &entity->getComponent(); - this->srcRect.x = this->srcRect.y = 0; this->srcRect.w = transform->width; this->srcRect.h = transform->height; + this->srcRect.x = this->textureXOffset * this->srcRect.w; + this->srcRect.y = this->textureYOffset * this->srcRect.h;; this->update(); } void SpriteComponent::update() { + // This code is not compatible for animated tiles if (animated) { srcRect.x = srcRect.w * static_cast((SDL_GetTicks() / speed) % frames); - } - srcRect.y = animationIndex * transform->height; + srcRect.y = animationIndex * transform->height; + } this->destRect.x = this->transform->position.x; this->destRect.y = this->transform->position.y; @@ -64,17 +79,17 @@ void SpriteComponent::update() void SpriteComponent::draw() { - Game::textureManager->draw(this->texture, this->srcRect, this->destRect, this->animated && this->flipped); + this->entity->getManager().getGame()->textureManager->draw(VEGO_Game().renderer, this->texture, this->srcRect, this->destRect, this->animated && this->flipped); } -void SpriteComponent::playAnimation(AnimationType type) +void SpriteComponent::playAnimation(std::string type) { - this->animationIndex = animations.at(type)->index; - this->frames = animations.at(type)->frames; - this->speed = animations.at(type)->speed; + this->animationIndex = animations->at(type)->index; + this->frames = animations->at(type)->frames; + this->speed = animations->at(type)->speed; } -void SpriteComponent::setDirection(Direction direction) +void SpriteComponent::setDirection(Direction direction) { this->flipped = direction == Direction::RIGHT; } \ No newline at end of file diff --git a/src/StatEffectsComponent.cpp b/src/StatEffectsComponent.cpp index 6c06263..c66e206 100644 --- a/src/StatEffectsComponent.cpp +++ b/src/StatEffectsComponent.cpp @@ -1,7 +1,7 @@ #include "StatEffectsComponent.h" #include "Entity.h" #include "TransformComponent.h" -#include "KeyboardController.h" +// #include "KeyboardController.h" #include #include @@ -15,15 +15,15 @@ void StatEffectsComponent::update() if (this->buffs.at(i) == 0) continue; if (this->buffs.at(i) - 1 == 0) { - this->modifyStatValue((Stats)i, BUFF_VALUE * -1); + this->resetStatValue((Stats)i); } this->buffs.at(i) -= 1; } } -void StatEffectsComponent::modifyStatDur(Stats stat, int duration) +void StatEffectsComponent::modifyStatDur(Stats stat, int duration, int value) { - if(this->buffs.at((uint8_t)stat) == 0) this->modifyStatValue(stat, BUFF_VALUE); + if(this->buffs.at((uint8_t)stat) == 0) this->modifyStatValue(stat, value); this->buffs.at((uint8_t)stat) += duration; } @@ -35,7 +35,21 @@ void StatEffectsComponent::modifyStatValue(Stats stat, int modifier) //modifier this->entity->getComponent().modifySpeed(modifier); break; case Stats::ATTACK_SPEED: - this->entity->getComponent().modifyAtkSpeed(modifier); + // this->entity->getComponent().modifyAtkSpeed(modifier); + break; + default: break; + } +} + +void StatEffectsComponent::resetStatValue(Stats stat) +{ + switch (stat) + { + case Stats::MOVEMENT_SPEED: + this->entity->getComponent().resetSpeedMod(); + break; + case Stats::ATTACK_SPEED: + // this->entity->getComponent().resetAtkSpeedMod(); break; default: break; } diff --git a/src/TextureManager.cpp b/src/TextureManager.cpp index 757952f..3e46d46 100644 --- a/src/TextureManager.cpp +++ b/src/TextureManager.cpp @@ -4,7 +4,7 @@ #include #include -#include "Game.h" +#include "GameInternal.h" SDL_Texture* TextureManager::loadTexture(const char* fileName) { @@ -12,15 +12,15 @@ SDL_Texture* TextureManager::loadTexture(const char* fileName) if (it != this->texture_cache.end()) { return it->second; } - auto texture = IMG_LoadTexture(Game::renderer, fileName); + auto texture = IMG_LoadTexture(this->manager->getGame()->renderer, fileName); if (texture == NULL) throw std::runtime_error(std::string("Couldn't load texture '") + fileName + "'"); this->texture_cache.emplace(std::string(fileName), texture); printf("Loaded texture at '%s'\n", fileName); return texture; } -void TextureManager::draw(SDL_Texture* texture, SDL_Rect src, SDL_Rect dest, bool flipped) +void TextureManager::draw(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect src, SDL_Rect dest, bool flipped) { SDL_RendererFlip flip = flipped ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; - SDL_RenderCopyEx(Game::renderer, texture, &src, &dest, 0, NULL, flip); + SDL_RenderCopyEx(renderer, texture, &src, &dest, 0, NULL, flip); } \ No newline at end of file diff --git a/src/TileComponent.cpp b/src/TileComponent.cpp index c6e5cec..4c33bf7 100644 --- a/src/TileComponent.cpp +++ b/src/TileComponent.cpp @@ -7,7 +7,7 @@ #include "SpriteComponent.h" #include "TileComponent.h" -TileComponent::TileComponent(int x, int y, int w, int h, int id) +TileComponent::TileComponent(int x, int y, int w, int h, int id, const std::map>* textureDict) { this->tileRect.x = x; this->tileRect.y = y; @@ -15,12 +15,15 @@ TileComponent::TileComponent(int x, int y, int w, int h, int id) this->tileRect.h = h; tileID = id; - auto it = textureDict.tileDictionary.find(tileID); //every id has its own distinct texture (in texturedict.h) - if (it == textureDict.tileDictionary.end()) { + auto it = textureDict->find(tileID); //every id has its own distinct texture (in texturedict.h) + if (it == textureDict->end()) { std::cout << "it end" << std::endl; return; } - this->path = it->second.data(); + + this->collision = it->second.second; + this->tileName = it->second.first; + this->path = it->second.first.data(); } void TileComponent::init() @@ -28,7 +31,7 @@ void TileComponent::init() this->entity->addComponent(this->tileRect.x, this->tileRect.y, this->tileRect.w, this->tileRect.h, 1); this->transform = &entity->getComponent(); - this->entity->addComponent(this->path); + this->entity->addComponent(this->path, 0); this->sprite = &entity->getComponent(); } diff --git a/src/TransformComponent.cpp b/src/TransformComponent.cpp index 43407fa..e8e299b 100644 --- a/src/TransformComponent.cpp +++ b/src/TransformComponent.cpp @@ -4,7 +4,7 @@ #include "ColliderComponent.h" #include "Constants.h" #include "Entity.h" -#include "Game.h" +#include "GameInternal.h" #include "Vector2D.h" #include #include @@ -52,33 +52,14 @@ void TransformComponent::init() void TransformComponent::update() { - // if(velocity.x != 0 && velocity.y != 0) - float multiplier = direction.x != 0 && direction.y != 0 ? 0.707 : 1; // normalizes vector; only works if directions are in increments of 45° Vector2D positionChange( - direction.x * speed * multiplier, - direction.y * speed * multiplier + direction.x * this->getSpeed() * multiplier, + direction.y * this->getSpeed() * multiplier ); - // TODO: move to separate functions - - if (this->entity->hasGroup((size_t)Entity::GroupLabel::PLAYERS)) { - - // [getAnyIntersection example code] - IntersectionBitSet intersections = - (CollisionHandler::getIntersectionWithBounds(entity, Vector2D(positionChange.x, 0)) | - (Game::collisionHandler->getAnyIntersection(entity, Vector2D(positionChange.x, 0), { Entity::GroupLabel::MAPTILES, Entity::GroupLabel::COLLIDERS })) & - IntersectionBitSet("0011")) | - (CollisionHandler::getIntersectionWithBounds(entity, Vector2D(0, positionChange.y)) | - (Game::collisionHandler->getAnyIntersection(entity, Vector2D(0, positionChange.y), { Entity::GroupLabel::MAPTILES, Entity::GroupLabel::COLLIDERS })) & - IntersectionBitSet("1100")); - - if (intersections.test((size_t)Direction::LEFT) || intersections.test((size_t)Direction::RIGHT)) - positionChange.x = 0; - - if (intersections.test((size_t)Direction::UP) || intersections.test((size_t)Direction::DOWN)) - positionChange.y = 0; - // [getAnyIntersection example code] + if (this->entity->hasGroup((size_t)Entity::GroupLabel::PLAYERS)){ + this->setPositionAfterCollision(positionChange); } position += positionChange; @@ -86,5 +67,27 @@ void TransformComponent::update() void TransformComponent::modifySpeed(int8_t modifier) { - this->speed += modifier; + this->speedMod += modifier; +} + +void TransformComponent::setPositionAfterCollision(Vector2D& positionChange) +{ + std::initializer_list colliders = { Entity::GroupLabel::MAPTILES, Entity::GroupLabel::COLLIDERS }; + // [getAnyIntersection example code] + IntersectionBitSet intersections = + (CollisionHandler::getIntersectionWithBounds(entity, Vector2D(positionChange.x, 0)) | + (this->entity->getManager() + .getGame()->collisionHandler->getAnyIntersection(entity, Vector2D(positionChange.x, 0), colliders)) & + IntersectionBitSet("0011")) | + (CollisionHandler::getIntersectionWithBounds(entity, Vector2D(0, positionChange.y)) | + (this->entity->getManager() + .getGame()->collisionHandler->getAnyIntersection(entity, Vector2D(0, positionChange.y), colliders)) & + IntersectionBitSet("1100")); + + if (intersections.test((size_t)Direction::LEFT) || intersections.test((size_t)Direction::RIGHT)) + positionChange.x = 0; + + if (intersections.test((size_t)Direction::UP) || intersections.test((size_t)Direction::DOWN)) + positionChange.y = 0; + // [getAnyIntersection example code] } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1b59151..da7f8a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,12 @@ #include #include +#include "VEGO.h" #include "Entity.h" -#include "Game.h" +#include "GameInternal.h" #include "Constants.h" -#include "PopupWindow.h" -Game* game = nullptr; +GameInternal* vego::game = nullptr; int main(int argc, char* argv[]) { @@ -18,16 +18,15 @@ int main(int argc, char* argv[]) Uint32 frameStart; int frameTime; - game = new Game(); + vego::game = new GameInternal(); - game->init("No_Name_Chicken_Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_SIZE_WIDTH, SCREEN_SIZE_HEIGHT, false); - while(playing) { - while (game->running()) { + vego::game->init("No_Name_Chicken_Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_SIZE_WIDTH, SCREEN_SIZE_HEIGHT, false); + while (vego::game->isRunning()) { frameStart = SDL_GetTicks(); - game->handleEvents(); - game->update(); - game->render(); + vego::game->handleEvents(); + vego::game->update(); + vego::game->render(); frameTime = SDL_GetTicks() - frameStart; @@ -35,23 +34,8 @@ int main(int argc, char* argv[]) SDL_Delay(frameDelay - frameTime); } } - Entity::TeamLabel winner = game->getWinner(); - PopupWindow popupWindow("Game over", winner == Entity::TeamLabel::BLUE ? - "Player1 won! Press 'C' to continue or 'Q' to quit." : - "Player2 won! Press 'C' to continue or 'Q' to quit."); - - popupWindow.renderWinnerPopup(winner); - - while (!popupWindow.interacted) { - popupWindow.handleWinnerEvents(); - SDL_Delay(10); - } - playing = popupWindow.shouldContinue(); - game->refreshPlayers(); - } - - game->clean(); + vego::game->clean(); return 0; } \ No newline at end of file