mirror of
https://github.com/Nimac0/SDL_Minigame
synced 2026-01-12 07:53:43 +00:00
Compare commits
53 Commits
2b411c5f6e
...
2ff579dd39
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ff579dd39 | |||
| 7a3c845c40 | |||
| 0695c6cacb | |||
| 3c5d56de6b | |||
| adaed679af | |||
| 325f6e8e8d | |||
| a9e754dd4f | |||
| d66d860cdc | |||
| e0c35aa690 | |||
| 1b795c3732 | |||
| eba3cdb6c8 | |||
|
|
1dc00408de | ||
|
|
e215fbd5b6 | ||
|
|
007538f760 | ||
|
|
9bb9d0fbcc | ||
| 044d957106 | |||
|
|
361687f09f | ||
|
|
9195b19bdb | ||
|
|
201b5c8b5d | ||
|
|
91f15671c0 | ||
|
|
abe018f99b | ||
|
|
84cee5e307 | ||
|
|
55b60624c4 | ||
|
|
1f3cf01419 | ||
|
|
691ea06eb0 | ||
|
|
acdbf29896 | ||
|
|
5e48f4e34f | ||
|
|
9247b8df8a | ||
| 7c50c8d1fb | |||
| 6a0b5197f9 | |||
|
|
ff27a0e55c | ||
|
|
58be6b05f0 | ||
|
|
b490e2dc17 | ||
| 2483b75983 | |||
|
|
dadf846470 | ||
|
|
4ead20ecb7 | ||
|
|
7dbcfe876c | ||
|
|
cbd1993c20 | ||
| b497991975 | |||
| 25414524a0 | |||
| 625ac98a57 | |||
|
|
27a80d9766 | ||
|
|
ac217e931b | ||
|
|
1a8a196e95 | ||
|
|
494ff8aa0a | ||
| a8052b4bbb | |||
| 92dfbacd9b | |||
| 1fbbb39843 | |||
| 70260c01bb | |||
| 7f4b1df833 | |||
| 9e346a719d | |||
|
|
65e00c2314 | ||
|
|
68079d0279 |
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@ -0,0 +1,8 @@
|
||||
# allow game dev to overwrite settings
|
||||
root=false
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
8
.gitmodules
vendored
8
.gitmodules
vendored
@ -1,22 +1,22 @@
|
||||
[submodule "SDL"]
|
||||
path = extern/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
branch = release-2.28.x
|
||||
[submodule "SDL_image"]
|
||||
path = extern/SDL_image
|
||||
url = https://github.com/libsdl-org/SDL_image.git
|
||||
branch = release-2.8.x
|
||||
[submodule "extern/SDL_mixer"]
|
||||
path = extern/SDL_mixer
|
||||
url = https://github.com/libsdl-org/SDL_mixer.git
|
||||
branch = release-2.8.x
|
||||
[submodule "extern/SDL_ttf"]
|
||||
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
|
||||
|
||||
[submodule "extern/nlohmann_json"]
|
||||
path = extern/nlohmann_json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
|
||||
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(engine)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(ENGINE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@ -12,10 +12,10 @@ set(ENGINE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(BUILD_SHARED_LIBS FALSE)
|
||||
|
||||
set(SDL2MIXER_VENDORED ON)
|
||||
set(SDL2TTF_VENDORED ON)
|
||||
set(SDLMIXER_VENDORED ON)
|
||||
set(SDLTTF_VENDORED ON)
|
||||
|
||||
set(SDL2_SOURCE_DIR “${ENGINE_SOURCE_DIR}/extern/SDL”)
|
||||
set(SDL_SOURCE_DIR “${ENGINE_SOURCE_DIR}/extern/SDL”)
|
||||
|
||||
set(TMXLITE_STATIC_LIB TRUE)
|
||||
|
||||
@ -24,6 +24,8 @@ 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)
|
||||
add_subdirectory(extern/nlohmann_json EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
file(GLOB_RECURSE SOURCES ${ENGINE_SOURCE_DIR}/src/*.cpp)
|
||||
add_library(${PROJECT_NAME} ${SOURCES})
|
||||
@ -31,11 +33,11 @@ add_library(${PROJECT_NAME} ${SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${ENGINE_INCLUDE_DIR})
|
||||
|
||||
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
|
||||
SDL3::SDL3-static
|
||||
SDL3_image::SDL3_image-static
|
||||
SDL3_mixer::SDL3_mixer-static
|
||||
SDL3_ttf::SDL3_ttf-static
|
||||
nlohmann_json::nlohmann_json
|
||||
tmxlite
|
||||
)
|
||||
|
||||
|
||||
7
config.json
Normal file
7
config.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullscreen": false,
|
||||
"title": "VGG (Very Good Game)",
|
||||
"screen_height": 600,
|
||||
"screen_width": 800,
|
||||
"icon": "./engine/internalAssets/iconImage.bmp"
|
||||
}
|
||||
@ -2,12 +2,12 @@
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": "."
|
||||
"path": ".",
|
||||
}
|
||||
],
|
||||
],
|
||||
"settings":
|
||||
{
|
||||
"tab_size": 4
|
||||
"tab_size": 4,
|
||||
},
|
||||
"build_systems": [
|
||||
{
|
||||
@ -23,7 +23,7 @@
|
||||
"name": "Release",
|
||||
"shell_cmd": "cmake -DCMAKE_BUILD_TYPE=Release build && cmake --build build",
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Generate CMake",
|
||||
@ -43,7 +43,10 @@
|
||||
"name": "Generate documentation",
|
||||
"shell_cmd": "docker run --rm -v \"$project_path:/source\" -v \"$project_path/docs:/output\" -v \"$project_path/docs/Doxyfile:/Doxyfile\" vego_engine-docker",
|
||||
},
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
],
|
||||
"debugger_configurations":
|
||||
[
|
||||
],
|
||||
}
|
||||
|
||||
2
extern/SDL
vendored
2
extern/SDL
vendored
@ -1 +1 @@
|
||||
Subproject commit 05eb08053d48fea9052ad02b3d619244aeb868d3
|
||||
Subproject commit f6864924f76e1a0b4abaefc76ae2ed22b1a8916e
|
||||
2
extern/SDL_image
vendored
2
extern/SDL_image
vendored
@ -1 +1 @@
|
||||
Subproject commit abcf63aa71b4e3ac32120fa9870a6500ddcdcc89
|
||||
Subproject commit b1c8ec7d75e3d8398940c9e04a8b82886ae6163d
|
||||
2
extern/SDL_mixer
vendored
2
extern/SDL_mixer
vendored
@ -1 +1 @@
|
||||
Subproject commit 5bcd40ad962dc72a3c051084ce128d78f7656566
|
||||
Subproject commit 5e2a70519294bc6ec44f1870b019ecd4760fde7d
|
||||
2
extern/SDL_ttf
vendored
2
extern/SDL_ttf
vendored
@ -1 +1 @@
|
||||
Subproject commit 4a318f8dfaa1bb6f10e0c5e54052e25d3c7f3440
|
||||
Subproject commit 4a8bda9197cc4d6fafd188bc9df6c7e8749a43a2
|
||||
1
extern/nlohmann_json
vendored
Submodule
1
extern/nlohmann_json
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a006a7a48bb30a247f0344b788c62c2806edd90b
|
||||
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
class Vector2D;
|
||||
class Manager;
|
||||
|
||||
enum class PowerupType
|
||||
{
|
||||
HEART,
|
||||
WALKINGSPEED,
|
||||
SHOOTINGSPEED
|
||||
};
|
||||
|
||||
class AssetManager
|
||||
{
|
||||
public:
|
||||
|
||||
AssetManager(Manager* manager);
|
||||
~AssetManager();
|
||||
|
||||
void createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, Entity* owner);
|
||||
void createPowerup(Vector2D pos, std::function<void (Entity*)> pickupFunc, std::string texturePath);
|
||||
|
||||
/*!
|
||||
* \brief Calculates a random spawn position for an object within a given area
|
||||
* \param size The size (collision box) of the object
|
||||
* \param spawnArea The area within which a spawn position will be calculated
|
||||
* \returns Spawn Coordinates for the object
|
||||
*/
|
||||
Vector2D calculateSpawnPosition(Vector2D size, Vector2D spawnArea);
|
||||
template <typename T> [[deprecated]] T calculateRandomType(int amount);
|
||||
|
||||
//texture management
|
||||
void addTexture(std::string id, const char* path);
|
||||
|
||||
// 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<std::string, SDL_Texture*> textures;
|
||||
std::map<std::string, Mix_Chunk*> soundEffects;
|
||||
std::map<std::string, Mix_Music*> music;
|
||||
};
|
||||
3
include/BackgroundMusic.h
Normal file
3
include/BackgroundMusic.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
enum class BackgroundMusic;
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "Component.h"
|
||||
#include "Vector2D.h"
|
||||
@ -22,7 +22,7 @@ public:
|
||||
ColliderComponent(const char* tag, float hitboxScale);
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
void removeCollision();
|
||||
|
||||
void handleCollision(Vector2D& characterPos, SDL_Rect& characterCollider, SDL_Rect& componentCollider);
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
#include "ColliderComponent.h"
|
||||
#include "Constants.h"
|
||||
#include "Entity.h"
|
||||
#include "SDL_rect.h"
|
||||
#include <SDL3/SDL_rect.h>
|
||||
#include "SpriteComponent.h"
|
||||
#include "Vector2D.h"
|
||||
#include "Manager.h"
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Entity;
|
||||
|
||||
class Component
|
||||
@ -8,7 +10,7 @@ public:
|
||||
Entity* entity;
|
||||
|
||||
virtual void init() {}
|
||||
virtual void update() {}
|
||||
virtual void update(uint_fast16_t diffTime) {}
|
||||
|
||||
virtual ~Component() = default;
|
||||
};
|
||||
62
include/ConfigLoader.h
Normal file
62
include/ConfigLoader.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
/*!
|
||||
* \class ConfigLoader
|
||||
* \brief Enables configuration of specific engine variables via a custom JSON file.
|
||||
*
|
||||
* The Config loader is responsible to handling customization for engine parameters like the
|
||||
* window icon, window title, ... through json files.
|
||||
*
|
||||
* It includes a standard config file and the option to add a custom one by overwriting the setConfigFilePath()
|
||||
* function within the implementation of the \ref Game class. Those files get merged, with a priorization on
|
||||
* the parameters set within the custom config file.
|
||||
*
|
||||
*
|
||||
* The currently available config parameters with their default values are:
|
||||
* \include ../config.json
|
||||
*
|
||||
*/
|
||||
|
||||
class ConfigLoader {
|
||||
|
||||
public:
|
||||
ConfigLoader();
|
||||
~ConfigLoader();
|
||||
|
||||
/*!
|
||||
* \brief Creates the final config for the engine
|
||||
*
|
||||
* Loads the default config and then creates the final config by either merging
|
||||
* (if the custom config has been set) or by implementing the standard config (if no custom
|
||||
* config was set).
|
||||
*
|
||||
* \private
|
||||
*/
|
||||
void init();
|
||||
/*!
|
||||
* \brief Sets the customConfigPath variable
|
||||
*
|
||||
* \param path optional variable that should include the path to the custom config JSON file
|
||||
*
|
||||
* \private
|
||||
*/
|
||||
void setCustomConfig(const std::optional<std::string>& path);
|
||||
/*!
|
||||
* \brief Gets final configuration
|
||||
* \return `json` variable containing the final config
|
||||
* \private
|
||||
*/
|
||||
json getFinalConfig();
|
||||
|
||||
private:
|
||||
|
||||
std::optional<std::string> customConfigPath;
|
||||
json finalConfig;
|
||||
|
||||
json loadConfigFromJSON(const std::string& path);
|
||||
json mergeConfigs(json baseConfig, json customConfig); //<! Merges 2 config.json files, prioritising the custom to the base one
|
||||
};
|
||||
@ -11,9 +11,6 @@ constexpr std::size_t MAX_GROUPS = 32;
|
||||
constexpr std::size_t MAX_STATS = 8;
|
||||
constexpr std::size_t MAX_TEAMS = 8;
|
||||
|
||||
constexpr int SCREEN_SIZE_HEIGHT = 640;
|
||||
constexpr int SCREEN_SIZE_WIDTH = 800;
|
||||
|
||||
constexpr int FPS = 60;
|
||||
|
||||
constexpr int TILE_SIZE = 32;
|
||||
|
||||
38
include/DataComponent.h
Normal file
38
include/DataComponent.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <any>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "Component.h"
|
||||
|
||||
class DataComponent : public Component
|
||||
{
|
||||
public:
|
||||
DataComponent() {};
|
||||
~DataComponent() {};
|
||||
/**
|
||||
* @brief Set a key-value pair of any type in the data map
|
||||
* @details e.g. setEntry("speed", 180); in this case the key is "speed" and the value is set to an integer of 180
|
||||
* @param key The name to store the value under
|
||||
* @param value The value to store of type T
|
||||
*/
|
||||
template <typename T>
|
||||
void setEntry(const std::string& key, const T& value) { dataMap.insert_or_assign(key, value); }
|
||||
|
||||
/**
|
||||
* @brief Get a value of type T from the data map
|
||||
* @details e.g. getEntry<int>("speed"); in this case the key is "speed" and the value is returned as an integer
|
||||
* @param key The name to retrieve the value from
|
||||
* @return An optional of type T containing the value if it exists and matches in typeid, otherwise std::nullopt
|
||||
*/
|
||||
template<typename T>
|
||||
std::optional<T> getEntry(std::string key) const {
|
||||
if (!this->dataMap.contains(key)) return std::nullopt;
|
||||
const std::any& value = this->dataMap.at(key);
|
||||
if (value.type() != typeid(T)) { return std::nullopt; }
|
||||
return std::any_cast<T>(value);
|
||||
}
|
||||
private:
|
||||
std::map<std::string, std::any> dataMap;
|
||||
};
|
||||
143
include/Entity.h
143
include/Entity.h
@ -35,85 +35,90 @@ class Entity
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* \brief Used for rendering order (last is highest) or retrieving entities of group
|
||||
* \todo Label used in singular entity shouldn't use plural
|
||||
* \todo HEARTS are rendered above POWERUPS, missleading order
|
||||
* \todo PROJECTILE are rendered above POWERUPS, missleading order
|
||||
* \todo Generalize HEARTS as UI or similar
|
||||
*/
|
||||
enum class GroupLabel
|
||||
{
|
||||
MAPTILES, //!< Entity using TileComponent
|
||||
PLAYERS, //!< Primary entity in player controll
|
||||
ENEMIES, //!< \deprecated All players now grouped as Entity::PLAYERS
|
||||
COLLIDERS, //!< Fixed collider entity, e.g. a wall
|
||||
PROJECTILE, //!< \todo Document
|
||||
HEARTS, //!< \todo Document
|
||||
POWERUPS //!< \todo Document
|
||||
};
|
||||
/*!
|
||||
* \brief Used for rendering order (last is highest) or retrieving entities of group
|
||||
* \todo Label used in singular entity shouldn't use plural
|
||||
* \todo HEARTS are rendered above POWERUPS, missleading order
|
||||
* \todo PROJECTILE are rendered above POWERUPS, missleading order
|
||||
* \todo Generalize HEARTS as UI or similar
|
||||
*/
|
||||
enum class GroupLabel
|
||||
{
|
||||
MAPTILES, //!< Entity using TileComponent
|
||||
PLAYERS, //!< Primary entity in player controll
|
||||
ENEMIES, //!< \deprecated All players now grouped as Entity::PLAYERS
|
||||
COLLIDERS, //!< Fixed collider entity, e.g. a wall
|
||||
PROJECTILE, //!< \todo Document
|
||||
HEARTS, //!< \todo Document
|
||||
POWERUPS //!< \todo Document
|
||||
};
|
||||
|
||||
/*!
|
||||
* \todo Document
|
||||
*/
|
||||
explicit Entity(Manager& mManager) :
|
||||
manager(mManager) { };
|
||||
/*!
|
||||
* \todo Document
|
||||
*/
|
||||
explicit Entity(Manager& mManager) :
|
||||
manager(mManager) { };
|
||||
|
||||
void update() const; //!< Call each frame to update all components
|
||||
void update(uint_fast16_t diffTime) const; //!< Call each frame to update all components
|
||||
|
||||
bool isActive() const { return this->active; } //!< \sa destroy()
|
||||
//! Mark for destruction for Manager::refresh() and disables collision
|
||||
//! \sa ColliderComponent
|
||||
void destroy() {
|
||||
this->active = false;
|
||||
if (this->hasComponent<ColliderComponent>()) {
|
||||
this->getComponent<ColliderComponent>().removeCollision();
|
||||
}
|
||||
}
|
||||
bool isActive() const { return this->active; } //!< \sa destroy()
|
||||
//! Mark for destruction for Manager::refresh() and disables collision
|
||||
//! \sa ColliderComponent
|
||||
void destroy() {
|
||||
this->active = false;
|
||||
if (this->hasComponent<ColliderComponent>()) {
|
||||
this->getComponent<ColliderComponent>().removeCollision();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasGroup(Group mGroup); //!< \sa GroupLabel
|
||||
void addGroup(Group mGroup); //!< \sa GroupLabel
|
||||
void delGroup(Group mGroup); //!< \sa GroupLabel
|
||||
//! \returns bitset with true on position GroupLabel if the entity belongs to group
|
||||
//! \sa GroupLabel
|
||||
std::bitset<MAX_GROUPS> getGroupBitSet();
|
||||
bool hasGroup(Group mGroup); //!< \sa GroupLabel
|
||||
void addGroup(Group mGroup); //!< \sa GroupLabel
|
||||
void delGroup(Group mGroup); //!< \sa GroupLabel
|
||||
//! \returns bitset with true on position GroupLabel if the entity belongs to group
|
||||
//! \sa GroupLabel
|
||||
std::bitset<MAX_GROUPS> getGroupBitSet();
|
||||
|
||||
//! \sa Manager
|
||||
Manager& getManager() { return manager; };
|
||||
//! \sa Manager
|
||||
Manager& getManager() { return manager; };
|
||||
|
||||
template <typename T> bool hasComponent() const //! \sa Component
|
||||
{
|
||||
return componentBitSet[getComponentTypeID<T>()];
|
||||
}
|
||||
template <typename T> bool hasComponent() const //! \sa Component
|
||||
{
|
||||
return componentBitSet[getComponentTypeID<T>()];
|
||||
}
|
||||
|
||||
//! \brief Adds specified type as component and calls Component::init()
|
||||
//! \param mArgs Constructor arguments of component
|
||||
template <typename T, typename...TArgs> T& addComponent(TArgs&&...mArgs)
|
||||
{
|
||||
T* c(new T(std::forward<TArgs>(mArgs)...));
|
||||
c->entity = this;
|
||||
std::unique_ptr<Component> uPtr{ c };
|
||||
this->components.emplace_back(std::move(uPtr));
|
||||
//! \brief Adds specified type as component and calls Component::init()
|
||||
//! \param mArgs Constructor arguments of component
|
||||
template <typename T, typename...TArgs> T& addComponent(TArgs&&...mArgs)
|
||||
{
|
||||
T* c(new T(std::forward<TArgs>(mArgs)...));
|
||||
c->entity = this;
|
||||
std::shared_ptr<Component> uPtr{ c };
|
||||
this->components.at(getComponentTypeID<T>()) = std::move(uPtr);
|
||||
|
||||
componentArray[getComponentTypeID<T>()] = c;
|
||||
componentBitSet[getComponentTypeID<T>()] = true;
|
||||
componentArray[getComponentTypeID<T>()] = c;
|
||||
componentBitSet[getComponentTypeID<T>()] = true;
|
||||
|
||||
c->init();
|
||||
return *c;
|
||||
};
|
||||
|
||||
template <typename T> T& getComponent() const //! \returns Component of type T
|
||||
{
|
||||
auto ptr(componentArray[getComponentTypeID<T>()]);
|
||||
return *static_cast<T*>(ptr);
|
||||
}
|
||||
c->init();
|
||||
return *c;
|
||||
};
|
||||
|
||||
template <typename T> T& getComponent() const //!< \todo: rewrite to use optionals
|
||||
{
|
||||
auto ptr(componentArray[getComponentTypeID<T>()]);
|
||||
return *static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
template <typename T> std::shared_ptr<T> getComponentAsPointer() const
|
||||
{
|
||||
return std::static_pointer_cast<T>(components.at(getComponentTypeID<T>()));
|
||||
}
|
||||
|
||||
private:
|
||||
Manager& manager;
|
||||
bool active = true;
|
||||
std::vector<std::unique_ptr<Component>> components;
|
||||
Manager& manager;
|
||||
bool active = true;
|
||||
std::array<std::shared_ptr<Component>, MAX_COMPONENTS> components;
|
||||
|
||||
ComponentArray componentArray = {};
|
||||
ComponentBitSet componentBitSet;
|
||||
GroupBitSet groupBitSet;
|
||||
ComponentArray componentArray = {};
|
||||
ComponentBitSet componentBitSet;
|
||||
GroupBitSet groupBitSet;
|
||||
};
|
||||
21
include/EventManager.h
Normal file
21
include/EventManager.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
|
||||
typedef std::function<SDL_AppResult(SDL_EventType, SDL_Event* const)> EventListener;
|
||||
|
||||
class EventManager {
|
||||
public:
|
||||
EventManager();
|
||||
|
||||
void registerListener(EventListener listener, std::initializer_list<Uint32> eventTypes);
|
||||
SDL_AppResult handleEvent(SDL_Event* const event);
|
||||
private:
|
||||
std::map<Uint32, std::vector<EventListener>> eventListeners = std::map<Uint32, std::vector<EventListener>>();
|
||||
};
|
||||
@ -8,7 +8,17 @@ public:
|
||||
virtual ~Game() {}
|
||||
|
||||
virtual void init() = 0;
|
||||
virtual void update() = 0;
|
||||
virtual void update(uint_fast16_t diffTime) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Sets the path for a custom config file.
|
||||
*
|
||||
* Virtual function to be overwritten in the implementation to return the path of a custom config JSON file.
|
||||
* \sa Layout of the config file is shown in ConfigLoader
|
||||
*
|
||||
* \return std::optional<std::string>
|
||||
*/
|
||||
virtual std::optional<std::string> setConfigFilePath() {return std::nullopt;}
|
||||
|
||||
GameInternal* gameInternal; //!< \deprecated
|
||||
};
|
||||
|
||||
@ -1,19 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "EventManager.h"
|
||||
#include "InteractionManager.h"
|
||||
#include "Manager.h"
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include "Vector2D.h"
|
||||
#include "Entity.h"
|
||||
#include "InputManager.h"
|
||||
#include "RenderManager.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "PickupManager.h"
|
||||
|
||||
typedef std::function<void()> gamefunction;
|
||||
|
||||
class AssetManager;
|
||||
class CollisionHandler;
|
||||
class TextureManager;
|
||||
class SoundManager;
|
||||
@ -23,44 +30,51 @@ class Game;
|
||||
class GameInternal
|
||||
{
|
||||
public:
|
||||
GameInternal();
|
||||
~GameInternal();
|
||||
GameInternal();
|
||||
~GameInternal();
|
||||
|
||||
void init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen);
|
||||
SDL_AppResult init();
|
||||
|
||||
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();
|
||||
SDL_AppResult handleEvent(SDL_Event* event);
|
||||
void update(Uint64 frameTime);
|
||||
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 */ PickupManager* pickupManager;
|
||||
/* static */ TextureManager* textureManager;
|
||||
/* static */ SoundManager* soundManager;
|
||||
/* static */ InputManager* inputManager;
|
||||
RenderManager* renderManager;
|
||||
EventManager* eventManager;
|
||||
InteractionManager* interactionManager;
|
||||
|
||||
Manager manager;
|
||||
RenderManager renderManager;
|
||||
Map* map; // game specific, might not be needed for all types of games
|
||||
|
||||
std::vector<Entity*>& tiles;
|
||||
std::vector<Entity*>& players;
|
||||
std::vector<Entity*>& projectiles;
|
||||
std::vector<Entity*>& hearts;
|
||||
std::vector<Entity*>& powerups;
|
||||
// end moved globals
|
||||
ConfigLoader* config;
|
||||
|
||||
std::vector<Entity*>& tiles;
|
||||
std::vector<Entity*>& players;
|
||||
std::vector<Entity*>& projectiles;
|
||||
std::vector<Entity*>& hearts;
|
||||
std::vector<Entity*>& powerups;
|
||||
// end moved globals
|
||||
|
||||
void refreshPlayers();
|
||||
|
||||
private:
|
||||
|
||||
Game* gameInstance;
|
||||
Game* gameInstance;
|
||||
|
||||
int counter = 0;
|
||||
bool running = true;
|
||||
SDL_Window* window;
|
||||
int counter = 0;
|
||||
bool running = true;
|
||||
SDL_Window* window;
|
||||
|
||||
Uint64 lastFrameTime = 0;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <map>
|
||||
|
||||
#include "Component.h"
|
||||
@ -94,12 +94,12 @@ public:
|
||||
~InputComponent();
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
|
||||
bool isKeyDown(Key key);
|
||||
|
||||
private:
|
||||
const Uint8* m_keyStates;
|
||||
const bool* m_keyStates;
|
||||
SDL_Scancode mapKeyToSDL(Key key);
|
||||
std::map<Key, SDL_Scancode> m_keyMappings;
|
||||
void InitKeyMappings();
|
||||
|
||||
139
include/InputManager.h
Normal file
139
include/InputManager.h
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
class InputManager {
|
||||
public:
|
||||
enum class EventType {
|
||||
KeyDown,
|
||||
KeyUp
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct InputAction {
|
||||
std::string name;
|
||||
std::vector<Key> bindings;
|
||||
std::function<void(bool)> callback;
|
||||
};
|
||||
|
||||
InputManager();
|
||||
~InputManager();
|
||||
|
||||
void init(); // see if necessary
|
||||
void processEvents();
|
||||
void registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void(bool)> callback, const std::string& context);
|
||||
|
||||
void setActiveContext(const std::string& context);
|
||||
std::string getActiveContext() const;
|
||||
|
||||
//void rebindAction(const std::string& actionName, const std::vector<Key>& newBindings, const std::string& context);
|
||||
//void removeBindings(const std::string& actionName, const std::string& context);
|
||||
//std::vector<Key> getBindings(const std::string& actionName, const std::string& context) const;
|
||||
std::vector<InputAction*> getActionsByKey(const Key key) const;
|
||||
|
||||
SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event);
|
||||
|
||||
void initKeyMap();
|
||||
private:
|
||||
// TODO: flesh this out to avoid loops in process actions
|
||||
// additionally to actionsByContext, not instead
|
||||
std::map<std::string, std::map<Key, std::vector<InputAction*>>> actionsByContextAndKey;
|
||||
|
||||
std::map<Key, SDL_Scancode> keyMap;
|
||||
std::string activeContext;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, InputManager::Key key);
|
||||
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction& action);
|
||||
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction* action);
|
||||
std::ostream& operator<<(std::ostream& os, const std::vector<InputManager::InputAction>& actions);
|
||||
std::ostream& operator<<(std::ostream& os, const std::vector<InputManager::InputAction*>& actions);
|
||||
28
include/InteractionComponent.h
Normal file
28
include/InteractionComponent.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "Component.h"
|
||||
#include "InteractionListener.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class InteractionComponent : public Component, public InteractionListener
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for the InteractionComponent.
|
||||
* @param callback A function to be called when an interaction event is triggered. void* actor, void* data are passed to the callback function from InteractionEventdataStruct.
|
||||
*/
|
||||
InteractionComponent(std::function<void(void*,void*)> callback);
|
||||
|
||||
/**
|
||||
* @brief Internal function to be called when an interaction event is triggered.
|
||||
*/
|
||||
void interact(void* actor, void* data) override;
|
||||
|
||||
/**
|
||||
* @brief Internal function to use as reference for targeting.
|
||||
*/
|
||||
std::shared_ptr<Vector2D> getPosition() override;
|
||||
private:
|
||||
std::function<void(void*,void*)> interactionCallback;
|
||||
};
|
||||
28
include/InteractionEventdataStruct.h
Normal file
28
include/InteractionEventdataStruct.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "Entity.h"
|
||||
#include "InteractionListener.h"
|
||||
#include "InteractionManager.h"
|
||||
#include "Vector2D.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @brief Struct to hold data for interaction events.
|
||||
* This struct is used to pass data to the interaction manager when an interaction event is triggered.
|
||||
*/
|
||||
struct InteractionEventdataStruct {
|
||||
/// Arbitray data to pass to the interaction listener. Can for example be an Entity ptr to represent the actor.
|
||||
void* actor;
|
||||
/// The data to pass to the interaction listener. Can be any type of pointer.
|
||||
void* data;
|
||||
/// The target of the interaction, e.g. InteractionComponent of an Entity. Is required if strategy is set to 0 (none)
|
||||
std::weak_ptr<InteractionListener> target = std::weak_ptr<InteractionListener>();
|
||||
/// Coordinates from which to base targeting on. Is required if strategy is not set to 0 (none)
|
||||
std::shared_ptr<Vector2D> targetingReference = nullptr;
|
||||
/// required without explicit target, defaults to none
|
||||
/// @sa InteractionManager::TargetingStrategy
|
||||
uint8_t strategy = 0; // int since enum would be impossibling user defined targetingStrategies
|
||||
|
||||
void triggerEvent();
|
||||
};
|
||||
17
include/InteractionListener.h
Normal file
17
include/InteractionListener.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "Vector2D.h"
|
||||
#include <memory>
|
||||
|
||||
class InteractionListener {
|
||||
public:
|
||||
InteractionListener() { };
|
||||
virtual ~InteractionListener() { };
|
||||
|
||||
virtual void interact(void* actor, void* data) = 0;
|
||||
virtual std::shared_ptr<Vector2D> getPosition() // required for targeting strategy, return null to only allow explicit targeting
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
35
include/InteractionManager.h
Normal file
35
include/InteractionManager.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "InteractionListener.h"
|
||||
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <ranges>
|
||||
|
||||
// TODO: ranges concept to avoid to<vector> in cpp
|
||||
typedef std::function<std::shared_ptr<InteractionListener>(Vector2D*, std::vector<std::shared_ptr<InteractionListener>>)> TargetingFunc;
|
||||
|
||||
class InteractionManager {
|
||||
public:
|
||||
InteractionManager();
|
||||
InteractionManager (const InteractionManager&) = delete;
|
||||
InteractionManager& operator= (const InteractionManager&) = delete;
|
||||
|
||||
enum class TargetingStrategy : uint8_t {
|
||||
none = 0,
|
||||
closest,
|
||||
manhattenDistance
|
||||
};
|
||||
|
||||
SDL_AppResult handleInteract(SDL_EventType type, SDL_Event* const event);
|
||||
void registerListener(std::weak_ptr<InteractionListener> listener);
|
||||
uint8_t registerTargetingFunc(TargetingFunc func);
|
||||
private:
|
||||
|
||||
std::vector<std::weak_ptr<InteractionListener>> listeners;
|
||||
std::array<TargetingFunc, 256> targetingFuncs;
|
||||
};
|
||||
@ -24,7 +24,7 @@ class Manager
|
||||
public:
|
||||
Manager(GameInternal* game) : game(game) {};
|
||||
|
||||
void update(); //!< \sa Entity::update()
|
||||
void update(uint_fast16_t diffTime); //!< \sa Entity::update()
|
||||
//! Disables all functionality of entities marked for destruction
|
||||
//! \sa Entity::destroy()
|
||||
void refresh();
|
||||
|
||||
@ -1,36 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <tmxlite/Types.hpp>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tmxlite/Map.hpp>
|
||||
#include <tmxlite/Property.hpp>
|
||||
#include <tmxlite/TileLayer.hpp>
|
||||
#include <tmxlite/Types.hpp>
|
||||
|
||||
class GameInternal;
|
||||
class Map
|
||||
{
|
||||
public:
|
||||
Map() = default;
|
||||
~Map() = default;
|
||||
|
||||
/*!
|
||||
*
|
||||
* \brief
|
||||
* This loads a map
|
||||
*
|
||||
* \param path The path to the map file
|
||||
* \return Boolean for success
|
||||
*
|
||||
*/
|
||||
[[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<int, std::pair<std::string, bool>>* textureDict /* backreference */);
|
||||
[[deprecated]]
|
||||
static void addTile(unsigned long id, int x, int y, GameInternal* game, const std::map<int, std::pair<std::string, bool>>* textureDict);
|
||||
|
||||
/*!
|
||||
* \brief Loads a .tmx map
|
||||
* \details Loads a `.tmx` file and extracts all relevant data. Any entities (including tiles) are only spawned once
|
||||
* \param path Path to the `.tmx` map file
|
||||
*
|
||||
* \sa Map::generateTiles()
|
||||
*/
|
||||
static void loadMapTmx(const char* path);
|
||||
Map(const char* path);
|
||||
void generateTiles(); //!< Generates the map based on the loaded definition
|
||||
private:
|
||||
static void addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, const char* texturePath);
|
||||
};
|
||||
// struct required for initialisation
|
||||
struct MapData {
|
||||
const std::vector<tmx::Tileset>* tileSets;
|
||||
const std::vector<tmx::Layer::Ptr>* mapLayers;
|
||||
const tmx::Vector2u* mapSize;
|
||||
const tmx::Vector2u* mapTileSize;
|
||||
const std::vector<std::string>* texturePaths;
|
||||
};
|
||||
|
||||
struct TileSetData {
|
||||
std::string texturePath{};
|
||||
tmx::Vector2f textureSize;
|
||||
uint32_t tileCount{};
|
||||
tmx::Vector2u tileCount2D;
|
||||
uint32_t firstGID{};
|
||||
};
|
||||
|
||||
tmx::Map map;
|
||||
Map::MapData mapData;
|
||||
std::vector<std::function<void()>> tileConstructors;
|
||||
|
||||
void loadTileLayer(const tmx::TileLayer& layer);
|
||||
static void addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, std::string texturePath, bool hasCollision);
|
||||
|
||||
template<typename T>
|
||||
static std::optional<T> getLayerProperty(const std::vector<tmx::Property>& properties, std::string propertyName) { return std::nullopt; };
|
||||
};
|
||||
20
include/PickupComponent.h
Normal file
20
include/PickupComponent.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "Component.h"
|
||||
|
||||
class PickupComponent : public Component
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Powerup Component object
|
||||
* @param func The function to be called when the powerup is picked up
|
||||
*/
|
||||
PickupComponent(std::function<void (Entity*)> func);
|
||||
~PickupComponent() {};
|
||||
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
|
||||
private:
|
||||
std::function<void (Entity*)> pickupFunc;
|
||||
};
|
||||
28
include/PickupManager.h
Normal file
28
include/PickupManager.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "Entity.h"
|
||||
#include "SoundEffects.h"
|
||||
|
||||
class Vector2D;
|
||||
class Manager;
|
||||
|
||||
class PickupManager
|
||||
{
|
||||
public:
|
||||
|
||||
PickupManager(Manager* manager);
|
||||
~PickupManager();
|
||||
|
||||
void createPowerup(Vector2D pos, std::function<void (Entity*)> pickupFunc, Textures texture);
|
||||
|
||||
Vector2D calculateSpawnPosition();
|
||||
|
||||
private:
|
||||
|
||||
Manager* man;
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "Component.h"
|
||||
|
||||
class PowerupComponent : public Component
|
||||
{
|
||||
public:
|
||||
PowerupComponent(std::function<void (Entity*)> func);
|
||||
~PowerupComponent() {};
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
std::function<void (Entity*)> pickupFunc;
|
||||
};
|
||||
@ -3,6 +3,7 @@
|
||||
#include "Component.h"
|
||||
#include "Vector2D.h"
|
||||
#include "Constants.h"
|
||||
#include "SoundEffects.h"
|
||||
|
||||
class TransformComponent;
|
||||
|
||||
@ -11,21 +12,23 @@ class ProjectileComponent : public Component
|
||||
//can maybe be split in separate .cpp file
|
||||
|
||||
public:
|
||||
ProjectileComponent(int range, int speed, Vector2D direction, Entity* owner)
|
||||
: range(range), speed(speed), direction(direction), owner(owner) {}
|
||||
ProjectileComponent(int range, int speed, Vector2D direction, Entity* owner, SoundEffects soundEffect)
|
||||
: range(range), speed(speed), direction(direction), owner(owner), soundEffect(soundEffect) {}
|
||||
~ProjectileComponent() {}
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
|
||||
private:
|
||||
TransformComponent* transformComponent;
|
||||
|
||||
int range = 0;
|
||||
int speed = 0;
|
||||
int distance = 0;
|
||||
float speed = 0;
|
||||
float distance = 0;
|
||||
|
||||
Entity* owner = nullptr;
|
||||
|
||||
Vector2D direction;
|
||||
|
||||
SoundEffects soundEffect;
|
||||
};
|
||||
@ -7,7 +7,7 @@ class RenderObject
|
||||
public:
|
||||
virtual void draw() = 0;
|
||||
|
||||
RenderObject(int zIndex, RenderManager& renderManager);
|
||||
RenderObject(int zIndex, RenderManager* renderManager);
|
||||
~RenderObject();
|
||||
|
||||
int getZIndex() { return this->zIndex; };
|
||||
@ -23,5 +23,5 @@ private:
|
||||
int zIndex = 0;
|
||||
|
||||
protected:
|
||||
RenderManager& renderManager;
|
||||
RenderManager* renderManager;
|
||||
};
|
||||
3
include/SoundEffects.h
Normal file
3
include/SoundEffects.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
enum class SoundEffects;
|
||||
@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "ECS.h"
|
||||
#include "TextureManager.h"
|
||||
#include "BackgroundMusic.h"
|
||||
#include "SoundEffects.h"
|
||||
|
||||
class GameInternal;
|
||||
|
||||
@ -17,8 +19,17 @@ class GameInternal;
|
||||
*/
|
||||
class SoundManager
|
||||
{
|
||||
|
||||
public:
|
||||
SoundManager() {}
|
||||
|
||||
SoundManager() {
|
||||
if (this_instance == nullptr) {
|
||||
this_instance = this;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("SoundManager instance already exists!");
|
||||
}
|
||||
}
|
||||
~SoundManager() {
|
||||
for (auto& it : this->sound_cache) {
|
||||
Mix_FreeChunk(it.second);
|
||||
@ -32,35 +43,32 @@ class SoundManager
|
||||
SoundManager(SoundManager const&) = delete;
|
||||
void operator=(SoundManager const&) = delete;
|
||||
|
||||
std::map<const char*, Mix_Music*> music_cache;
|
||||
std::map<const char*, Mix_Chunk*> 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);
|
||||
|
||||
Mix_Chunk* loadSound(const char* fileName);
|
||||
*/
|
||||
/*!
|
||||
* \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);
|
||||
static void playSound(SoundEffects 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 playMusic(BackgroundMusic sound, int loops, int volume, int milliseconds);
|
||||
|
||||
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
|
||||
@ -73,5 +81,29 @@ class SoundManager
|
||||
|
||||
static void fadeOutMusic(int ms); //!< Handles fading out a music track
|
||||
|
||||
/*!
|
||||
* \brief Initializes sound-effects and adds them to a cache
|
||||
*
|
||||
*/
|
||||
static void addSoundEffects(const std::map<SoundEffects, const char*> &effects);
|
||||
|
||||
/*!
|
||||
* \brief Initializes background-music and adds them to a cache
|
||||
*
|
||||
*/
|
||||
static void addBackgroundMusic(const std::map<BackgroundMusic, const char*> &backgroundMusic);
|
||||
|
||||
static SoundManager* getInstance() {
|
||||
return this_instance;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::map<BackgroundMusic, Mix_Music*> music_cache;
|
||||
std::map<SoundEffects, Mix_Chunk*> sound_cache;
|
||||
static SoundManager* this_instance;
|
||||
|
||||
static void addSingleBackgroundMusic(BackgroundMusic backgroundMusic, const char* path);
|
||||
static void addSingleSoundEffect(SoundEffects soundEffect, const char* path);
|
||||
};
|
||||
@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <SDL_render.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Textures.h"
|
||||
#include "AnimationHandler.h"
|
||||
#include "Component.h"
|
||||
#include "Direction.h"
|
||||
@ -22,9 +23,9 @@ public:
|
||||
private:
|
||||
TransformComponent* transform;
|
||||
SDL_Texture* texture;
|
||||
SDL_Rect srcRect, destRect;
|
||||
SDL_FRect srcRect, destRect;
|
||||
|
||||
const char* texturePath;
|
||||
Textures textureEnum;
|
||||
|
||||
bool animated = false;
|
||||
uint8_t frames = 0;
|
||||
@ -34,21 +35,32 @@ private:
|
||||
int textureXOffset;
|
||||
int textureYOffset;
|
||||
|
||||
//there should be a better solution as this variable is only used for the loading of the tmx map
|
||||
//TODO: improve this in the future and also remove it from the scope of the developer
|
||||
const char* path; //!< empty string if texture has a texture enum value, otherwise the path of the texture
|
||||
|
||||
public:
|
||||
SpriteComponent(const char* path, int zIndex);
|
||||
//debug
|
||||
Textures getTexture() { return this->textureEnum; }
|
||||
|
||||
|
||||
|
||||
SpriteComponent(Textures texture, int zIndex);
|
||||
SpriteComponent(Textures texture, int xOffset, int yOffset, int zIndex);
|
||||
SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex);
|
||||
SpriteComponent(
|
||||
const char* path,
|
||||
Textures texture,
|
||||
bool isAnimated,
|
||||
std::map<std::string, std::unique_ptr<Animation>>* animationList,
|
||||
std::string defaultAnimation,
|
||||
int zIndex);
|
||||
~SpriteComponent();
|
||||
|
||||
void setTexture(const char* path);
|
||||
void setTexture(Textures texture);
|
||||
void setMapTileTexture(const char* path);
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
void draw() override;
|
||||
void playAnimation(std::string type);
|
||||
void setDirection(Direction direction);
|
||||
|
||||
@ -3,11 +3,15 @@
|
||||
#include "Constants.h"
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
enum class Stats
|
||||
{
|
||||
MOVEMENT_SPEED,
|
||||
ATTACK_SPEED
|
||||
/**
|
||||
* @brief Struct to hold the duration, reset function and start time of a stat effect
|
||||
*/
|
||||
struct StatEffect {
|
||||
uint32_t duration; //!< Duration of the effect in milliseconds
|
||||
std::function<void()> resetFunction; //!< Function to reset the effect, will be called on expiry of duration
|
||||
uint32_t startTime;
|
||||
};
|
||||
|
||||
class StatEffectsComponent : public Component{
|
||||
@ -16,13 +20,14 @@ public:
|
||||
~StatEffectsComponent() {};
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
|
||||
void modifyStatDur(Stats stat, int duration, int value);
|
||||
|
||||
void modifyStatValue(Stats stat, int modifier);
|
||||
void resetStatValue(Stats stat);
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
/**
|
||||
* @brief Add a stat effect to the entity
|
||||
* @param duration The duration of the effect in milliseconds
|
||||
* @param resetFunction The function to reset the effect, will be called on expiry of duration
|
||||
*/
|
||||
void addEffect(uint32_t duration, std::function<void()> resetFunction);
|
||||
|
||||
private:
|
||||
std::array<int, MAX_STATS> buffs = { 0 };
|
||||
std::vector<StatEffect> effects = {};
|
||||
};
|
||||
@ -1,11 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "ECS.h"
|
||||
#include <SDL_render.h>
|
||||
#include "SDL3/SDL_surface.h"
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Textures.h"
|
||||
|
||||
/*!
|
||||
* \class TextureManager
|
||||
* \brief A manager for loading, caching, and drawing textures.
|
||||
*
|
||||
* The `TextureManager` class is responsible for handling texture loading, caching,
|
||||
* and rendering in the engine. It provides functions to add, load, and draw textures
|
||||
* from files, as well as manage sprite sheets.
|
||||
*
|
||||
* \sa Textures are used to identify textures within the engine.
|
||||
* It is expected that they are implemented within the games scope.
|
||||
*/
|
||||
|
||||
class TextureManager
|
||||
{
|
||||
@ -15,13 +29,69 @@ class TextureManager
|
||||
for (auto& it : this->texture_cache) {
|
||||
SDL_DestroyTexture(it.second);
|
||||
}
|
||||
for (auto& it : this->mapTile_texture_cache) {
|
||||
SDL_DestroyTexture(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, SDL_Texture*> texture_cache;
|
||||
/*!
|
||||
* \brief Adds a single texture to the cache.
|
||||
* \param texture The texture identifier.
|
||||
* \param filePath The file path to the texture file.
|
||||
* \throws std::runtime_error Is thrown if the texture could not be loaded correctly
|
||||
*
|
||||
* This function loads the texture from the specified file and stores it in
|
||||
* a cache.
|
||||
*/
|
||||
void addSingleTexture(Textures texture, const char* filePath);
|
||||
|
||||
SDL_Texture* loadTexture(const char* fileName);
|
||||
/*!
|
||||
* \brief Adds multiple textures to the cache.
|
||||
* \param textures A map of texture identifiers and corresponding file paths.
|
||||
*
|
||||
* This function iterates over the provided map of textures and loads each
|
||||
* texture using `addSingleTexture`. It allows for several
|
||||
* textures to be added at once.
|
||||
*/
|
||||
void addTextures(const std::map<Textures, const char*>& textures);
|
||||
|
||||
/*!
|
||||
* \brief Loads a texture from the cache.
|
||||
* \param texture The texture identifier.
|
||||
* \return A pointer to the `SDL_Texture` if found, or `nullptr` if not found.
|
||||
*
|
||||
* This function looks up a texture within the cache and returns the
|
||||
* corresponding `SDL_Texture*`. If the texture is not found, it logs an error
|
||||
* message and returns `nullptr`.
|
||||
*/
|
||||
SDL_Texture* loadTexture(Textures texture);
|
||||
static std::vector<SDL_Rect> splitSpriteSheet(SDL_Texture* spriteSheet, int width, int height, int spritesOnSheet);
|
||||
static void draw(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect src, SDL_Rect dest, bool flipped = false);
|
||||
static void draw(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FRect src, SDL_FRect dest, bool flipped = false);
|
||||
|
||||
void setScaleMode(SDL_ScaleMode scaleMode) { this->scaleMode = scaleMode; }
|
||||
|
||||
/*!
|
||||
* \brief Loads a map tile texture from the file system and caches it.
|
||||
* \param path The file path to the texture.
|
||||
* \return `SDL_Texture*` representing the map tile.
|
||||
* \throws std::runtime_error Is thrown if the texture could not be loaded correctly
|
||||
*
|
||||
* This function checks if the map tile texture is already cached. If not, it
|
||||
* loads the texture from the file system and stores it in the cache.
|
||||
*
|
||||
* \todo should not be usable for the developer and only be accessed by the map class
|
||||
*/
|
||||
SDL_Texture* loadMapTileTexture(const char* path);
|
||||
|
||||
std::string getTexturePath(Textures texture) {
|
||||
return this->texture_references.at(texture);
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_ScaleMode scaleMode = SDL_SCALEMODE_NEAREST;
|
||||
Manager* manager;
|
||||
std::map<Textures, SDL_Texture*> texture_cache;
|
||||
std::map<std::string, SDL_Texture*> mapTile_texture_cache;
|
||||
|
||||
std::map<Textures, std::string> texture_references;
|
||||
};
|
||||
14
include/Textures.h
Normal file
14
include/Textures.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* \class Textures
|
||||
* \brief Forward declaration of the \c Textures enum class.
|
||||
*
|
||||
* The \c Textures enum class is intended to be implemented within the game scope.
|
||||
* This allows for customized texture entries to be defined based on the specific needs of the project.
|
||||
* The base declaration ensures that the enum class can be referenced and used consistently throughout
|
||||
* the engine while leaving the details of the texture identifiers up to the implementation.
|
||||
* \sa \ref TextureManager "TextureManager" for how the enum is used.
|
||||
*/
|
||||
|
||||
enum class Textures;
|
||||
@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "Component.h"
|
||||
#include "Textures.h"
|
||||
|
||||
class SpriteComponent;
|
||||
class TransformComponent;
|
||||
@ -17,17 +18,19 @@ public:
|
||||
|
||||
SDL_Rect tileRect;
|
||||
int tileID;
|
||||
const char* path;
|
||||
Textures texture;
|
||||
|
||||
TileComponent() = default;
|
||||
TileComponent(int x, int y, int w, int h, int id, const std::map<int, std::pair<std::string, bool>>* textureDict);
|
||||
TileComponent(int x, int y, int w, int h, int id, const std::map<int, std::pair<Textures, bool>>* textureDict);
|
||||
~TileComponent() = default;
|
||||
|
||||
void init() override;
|
||||
|
||||
bool hasCollision(){return this->collision;}
|
||||
std::string getName(){return this->tileName;}
|
||||
bool hasCollision() {
|
||||
return this->collision;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
bool collision;
|
||||
std::string tileName;
|
||||
};
|
||||
@ -3,6 +3,7 @@
|
||||
#include "Component.h"
|
||||
#include "Vector2D.h"
|
||||
#include "Constants.h"
|
||||
#include "DataComponent.h"
|
||||
|
||||
class TransformComponent : public Component
|
||||
{
|
||||
@ -14,22 +15,14 @@ public:
|
||||
int width = 32;
|
||||
int scale = 1;
|
||||
|
||||
int getSpeed() { return speed + speedMod; };
|
||||
void resetSpeedMod() { speedMod = 0; };
|
||||
|
||||
TransformComponent();
|
||||
explicit TransformComponent(int scale);
|
||||
TransformComponent(float x, float y);
|
||||
TransformComponent(float x, float y, int scale);
|
||||
TransformComponent(float x, float y, int w, int h, int scale);
|
||||
explicit TransformComponent(int scale = 1);
|
||||
TransformComponent(float x, float y, int scale = 1);
|
||||
TransformComponent(float x, float y, int w, int h, int scale = 1);
|
||||
|
||||
void init() override;
|
||||
/*! TODO: document usage of collision handler */
|
||||
void update() override;
|
||||
void update(uint_fast16_t diffTime) override;
|
||||
void setPositionAfterCollision(Vector2D& positionChange);
|
||||
void modifySpeed(int8_t modifier);
|
||||
int getSpeed();
|
||||
|
||||
private:
|
||||
int speed = 3;
|
||||
int speedMod = 0;
|
||||
};
|
||||
|
||||
7
include/VEGO_Event.h
Normal file
7
include/VEGO_Event.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
|
||||
namespace vego {
|
||||
extern Uint32 VEGO_Event_Interaction;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_rect.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_rect.h>
|
||||
|
||||
class Vector2D
|
||||
{
|
||||
|
||||
BIN
internalAssets/iconImage.bmp
Normal file
BIN
internalAssets/iconImage.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@ -1,112 +0,0 @@
|
||||
#include "AssetManager.h"
|
||||
|
||||
#include "TextureManager.h"
|
||||
#include "SoundManager.h"
|
||||
#include "ProjectileComponent.h"
|
||||
#include "GameInternal.h"
|
||||
|
||||
#include "TransformComponent.h"
|
||||
|
||||
#include "CollisionHandler.h"
|
||||
#include "ColliderComponent.h"
|
||||
#include "Constants.h"
|
||||
#include "Entity.h"
|
||||
#include "Vector2D.h"
|
||||
#include "PowerupComponent.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
AssetManager::AssetManager(Manager* manager) : man(manager) {}
|
||||
|
||||
AssetManager::~AssetManager() {}
|
||||
|
||||
void AssetManager::addTexture(std::string id, const char* path) {
|
||||
textures.emplace(id, this->man->getGame()->textureManager->loadTexture(path));
|
||||
}
|
||||
|
||||
void AssetManager::addSoundEffect(std::string id, const char* 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) {
|
||||
return textures.at(id);
|
||||
}
|
||||
|
||||
Mix_Chunk* AssetManager::getSound(std::string id) {
|
||||
return soundEffects.at(id);
|
||||
}
|
||||
|
||||
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<TransformComponent>(pos.x, pos.y, 32, 32, scale); //32x32 is standard size for objects
|
||||
projectile.addComponent<SpriteComponent>(texturePath, 4);
|
||||
projectile.addComponent<ProjectileComponent>(range, speed, velocity, owner);
|
||||
projectile.addComponent<ColliderComponent>("projectile", 0.6f);
|
||||
projectile.addGroup((size_t)Entity::GroupLabel::PROJECTILE);
|
||||
}
|
||||
|
||||
void AssetManager::createPowerup(Vector2D pos, std::function<void (Entity*)> pickupFunc, std::string texturePath) {
|
||||
|
||||
auto& powerups(man->addEntity());
|
||||
powerups.addComponent<TransformComponent>(pos.x, pos.y, 32, 32, 1); //32x32 is standard size for objects
|
||||
|
||||
try {
|
||||
powerups.addComponent<SpriteComponent>(texturePath.c_str(), 3);
|
||||
}
|
||||
catch (std::runtime_error e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
powerups.addComponent<ColliderComponent>("powerup", 0.6f);
|
||||
powerups.addComponent<PowerupComponent>(pickupFunc);
|
||||
powerups.addGroup((size_t)Entity::GroupLabel::POWERUPS);
|
||||
}
|
||||
|
||||
|
||||
Vector2D AssetManager::calculateSpawnPosition(Vector2D size, Vector2D spawnArea)
|
||||
{
|
||||
Vector2D spawnPos = Vector2D(-1, -1);
|
||||
|
||||
for(int i = 0; i <= SPAWN_ATTEMPTS; i++)
|
||||
{
|
||||
|
||||
SDL_Rect spawnRect = {
|
||||
rand() % (int)(spawnArea.x - size.x),
|
||||
rand() % (int)(spawnArea.y - size.y),
|
||||
size.x,
|
||||
size.y
|
||||
};
|
||||
|
||||
std::vector<ColliderComponent*> colliders = this->man->getGame()->collisionHandler->getColliders({Entity::GroupLabel::MAPTILES});
|
||||
bool conflict = std::any_of(colliders.begin(), colliders.end(),
|
||||
[&](const auto& cc) {
|
||||
return SDL_HasIntersection(&spawnRect, &cc->collider);} );
|
||||
|
||||
if(!conflict)
|
||||
{
|
||||
spawnPos = Vector2D(spawnRect.x, spawnRect.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return spawnPos;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T AssetManager::calculateRandomType(int amount)
|
||||
{
|
||||
T type = T(rand() % amount);
|
||||
return type;
|
||||
}
|
||||
@ -31,10 +31,10 @@ void ColliderComponent::init()
|
||||
}
|
||||
|
||||
transform = &entity->getComponent<TransformComponent>();
|
||||
this->update();
|
||||
this->update(0);
|
||||
}
|
||||
|
||||
void ColliderComponent::update()
|
||||
void ColliderComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
collider.x = transform->position.x - (transform->width - transform->width * transform->scale * this->hitboxScale) / 2;
|
||||
collider.y = transform->position.y - (transform->width - transform->width * transform->scale * this->hitboxScale) / 2;
|
||||
|
||||
@ -6,10 +6,11 @@
|
||||
#include "Manager.h"
|
||||
#include "Vector2D.h"
|
||||
|
||||
#include <SDL_rect.h>
|
||||
#include <SDL3/SDL_rect.h>
|
||||
#include <bitset>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <VEGO.h>
|
||||
|
||||
IntersectionBitSet CollisionHandler::getIntersection(Entity* entityA, Entity* entityB, Vector2D posModA, Vector2D posModB)
|
||||
{
|
||||
@ -26,7 +27,7 @@ IntersectionBitSet CollisionHandler::getIntersection(Entity* entityA, Entity* en
|
||||
colliderB.x += posModB.x;
|
||||
colliderB.y += posModB.y;
|
||||
|
||||
if (!SDL_HasIntersection(
|
||||
if (!SDL_HasRectIntersection(
|
||||
&colliderA,
|
||||
&colliderB))
|
||||
return std::bitset<DIRECTION_C>();
|
||||
@ -66,20 +67,20 @@ IntersectionBitSet CollisionHandler::getIntersectionWithBounds(Entity* entity, V
|
||||
|
||||
// all 4 directions and both sides to allow checking for fully out of bounds
|
||||
if (collider->x + posMod.x < 0 ||
|
||||
collider->x + posMod.x > SCREEN_SIZE_WIDTH) {
|
||||
collider->x + posMod.x > VEGO_Game().config->getFinalConfig().at("screen_width")) {
|
||||
intersections.set((size_t) Direction::LEFT);
|
||||
}
|
||||
|
||||
if (collider->x + collider->w + posMod.x < 0 ||
|
||||
collider->x + collider->w + posMod.x > SCREEN_SIZE_WIDTH)
|
||||
collider->x + collider->w + posMod.x > VEGO_Game().config->getFinalConfig().at("screen_width"))
|
||||
intersections.set((size_t) Direction::RIGHT);
|
||||
|
||||
if (collider->y + posMod.y < 0 ||
|
||||
collider->y + posMod.y > SCREEN_SIZE_HEIGHT)
|
||||
collider->y + posMod.y > VEGO_Game().config->getFinalConfig().at("screen_height"))
|
||||
intersections.set((size_t) Direction::UP);
|
||||
|
||||
if (collider->y + collider->h + posMod.y < 0 ||
|
||||
collider->y + collider->h + posMod.y > SCREEN_SIZE_HEIGHT)
|
||||
collider->y + collider->h + posMod.y > VEGO_Game().config->getFinalConfig().at("screen_height"))
|
||||
intersections.set((size_t) Direction::DOWN);
|
||||
|
||||
return intersections;
|
||||
@ -152,7 +153,7 @@ Entity* CollisionHandler::getAnyIntersection<Entity*>(
|
||||
if (!entity->hasComponent<ColliderComponent>()) return nullptr;
|
||||
for (auto& collider : getColliders(groupLabels, excludedEntities)) {
|
||||
SDL_Rect rect = entity->getComponent<ColliderComponent>().collider + posMod;
|
||||
if (SDL_HasIntersection(&rect, &collider->collider)) {
|
||||
if (SDL_HasRectIntersection(&rect, &collider->collider)) {
|
||||
return collider->entity;
|
||||
}
|
||||
}
|
||||
@ -175,7 +176,7 @@ bool CollisionHandler::getAnyIntersection<bool>(
|
||||
if (!entity->hasComponent<ColliderComponent>()) return false;
|
||||
for (auto& collider : getColliders(groupLabels, excludedEntities)) {
|
||||
SDL_Rect rect = entity->getComponent<ColliderComponent>().collider + posMod;
|
||||
if (SDL_HasIntersection(&rect, &collider->collider)) {
|
||||
if (SDL_HasRectIntersection(&rect, &collider->collider)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
51
src/ConfigLoader.cpp
Normal file
51
src/ConfigLoader.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "ConfigLoader.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
ConfigLoader::ConfigLoader() {}
|
||||
|
||||
ConfigLoader::~ConfigLoader() {}
|
||||
|
||||
void ConfigLoader::init() {
|
||||
//TODO: look into adaptive paths for better handling as this requires the implemented game
|
||||
// to have ./engine in the root folder (very low prio)
|
||||
const json baseConfig = loadConfigFromJSON("./engine/config.json");
|
||||
|
||||
if (!customConfigPath.has_value()) {
|
||||
finalConfig = baseConfig;
|
||||
return;
|
||||
}
|
||||
|
||||
finalConfig = mergeConfigs(baseConfig, loadConfigFromJSON(customConfigPath.value()));
|
||||
}
|
||||
|
||||
json ConfigLoader::loadConfigFromJSON(const std::string& path) {
|
||||
std::ifstream config_file(path);
|
||||
json config;
|
||||
|
||||
if (!config_file.is_open()) {
|
||||
throw std::runtime_error(std::string("Could not load config file at: " + path));
|
||||
}
|
||||
|
||||
config_file >> config;
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
void ConfigLoader::setCustomConfig(const std::optional<std::string>& path) {
|
||||
customConfigPath = path;
|
||||
}
|
||||
|
||||
json ConfigLoader::mergeConfigs(json baseConfig, json customConfig) {
|
||||
for (auto& entry : customConfig.items()) {
|
||||
baseConfig[entry.key()] = entry.value();
|
||||
}
|
||||
return baseConfig;
|
||||
}
|
||||
|
||||
json ConfigLoader::getFinalConfig() {
|
||||
return finalConfig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
#include "Component.h"
|
||||
#include <cstddef>
|
||||
|
||||
void Entity::update() const
|
||||
void Entity::update(uint_fast16_t diffTime) const
|
||||
{
|
||||
for (auto const& c : components) c->update();
|
||||
for (auto const& c : components)
|
||||
if (c)
|
||||
c->update(diffTime);
|
||||
}
|
||||
|
||||
bool Entity::hasGroup(Group mGroup)
|
||||
|
||||
44
src/EventManager.cpp
Normal file
44
src/EventManager.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "EventManager.h"
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include "SDL3/SDL_stdinc.h"
|
||||
#include "VEGO_Event.h"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
Uint32 vego::VEGO_Event_Interaction;
|
||||
|
||||
EventManager::EventManager()
|
||||
{
|
||||
/// \TODO: from c++26 you (should be able to) can get the amount of name values in an enum
|
||||
vego::VEGO_Event_Interaction = SDL_RegisterEvents(1); // TODO: error handling
|
||||
}
|
||||
|
||||
|
||||
void EventManager::registerListener(EventListener listener, std::initializer_list<Uint32> eventTypes)
|
||||
{
|
||||
std::ranges::for_each(eventTypes.begin(), eventTypes.end(), [this, &listener](const Uint32& eventType) {
|
||||
if (!this->eventListeners.contains(eventType)) {
|
||||
this->eventListeners.insert({eventType, std::vector<EventListener>()});
|
||||
}
|
||||
this->eventListeners.at(eventType).emplace_back(listener);
|
||||
});
|
||||
}
|
||||
|
||||
SDL_AppResult EventManager::handleEvent(SDL_Event* event)
|
||||
{
|
||||
SDL_EventType type = (SDL_EventType) event->type;
|
||||
if (this->eventListeners.contains(type)) {
|
||||
std::vector<SDL_AppResult> results;
|
||||
for (auto& listener : this->eventListeners.at(type)) {
|
||||
results.emplace_back(listener((SDL_EventType) event->type, event));
|
||||
}
|
||||
if (std::ranges::contains(results, SDL_APP_FAILURE))
|
||||
return SDL_APP_FAILURE;
|
||||
if (std::ranges::contains(results, SDL_APP_SUCCESS))
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
@ -1,159 +1,182 @@
|
||||
#include "GameInternal.h"
|
||||
|
||||
#include <SDL_error.h>
|
||||
|
||||
#include "CollisionHandler.h"
|
||||
#include "AssetManager.h"
|
||||
#include "EventManager.h"
|
||||
#include "InputManager.h"
|
||||
#include "InteractionManager.h"
|
||||
#include "RenderManager.h"
|
||||
#include "SDL_mixer.h"
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include "SDL3/SDL_oldnames.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"
|
||||
|
||||
#include <VEGO.h>
|
||||
#include <VEGO_Event.h>
|
||||
#include <functional>
|
||||
|
||||
#include "ConfigLoader.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))
|
||||
manager(this),
|
||||
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)
|
||||
SDL_AppResult GameInternal::init()
|
||||
{
|
||||
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;
|
||||
}
|
||||
config = new ConfigLoader();
|
||||
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
|
||||
{
|
||||
std::cout << "ERROR. Subsystem couldnt be initialized! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
return;
|
||||
}
|
||||
this->gameInstance = GameFactory::instance().create(this);
|
||||
config->setCustomConfig(this->gameInstance->setConfigFilePath());
|
||||
config->init();
|
||||
|
||||
if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) {
|
||||
std::cout << "ERROR. Subsystem couldnt be initialized!" << std::endl;
|
||||
return;
|
||||
}
|
||||
json finalConfig = config->getFinalConfig();
|
||||
|
||||
GameInternal::pickupManager = new PickupManager(&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?
|
||||
GameInternal::inputManager = new InputManager();
|
||||
GameInternal::inputManager->initKeyMap();
|
||||
|
||||
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;
|
||||
}
|
||||
GameInternal::renderManager = new RenderManager();
|
||||
GameInternal::eventManager = new EventManager();
|
||||
GameInternal::interactionManager = new InteractionManager();
|
||||
|
||||
this->eventManager->registerListener(std::bind_front(&InputManager::handleEvent, this->inputManager), { SDL_EVENT_KEY_DOWN, SDL_EVENT_KEY_UP });
|
||||
this->eventManager->registerListener(std::bind_front(&InteractionManager::handleInteract, VEGO_Game().interactionManager), { vego::VEGO_Event_Interaction });
|
||||
|
||||
int flags = 0;
|
||||
if (finalConfig.at("fullscreen"))
|
||||
{
|
||||
flags = SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
|
||||
if (!SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO))
|
||||
{
|
||||
std::cout << "ERROR. Subsystem couldnt be initialized! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) {
|
||||
std::cout << "ERROR. Subsystem couldnt be initialized!" << std::endl;
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow(finalConfig.at("title").get<std::string>().c_str(),
|
||||
finalConfig.at("screen_width"), finalConfig.at("screen_height"), flags);
|
||||
|
||||
if (!window)
|
||||
{
|
||||
std::cout << "ERROR: Window couldnt be created! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
// bad
|
||||
SDL_Surface* icon;
|
||||
if((icon = SDL_LoadBMP("assets/iconImage.bmp")))
|
||||
if((icon = SDL_LoadBMP(finalConfig.at("icon").get<std::string>().c_str())))
|
||||
{
|
||||
SDL_SetWindowIcon(window, icon);
|
||||
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);
|
||||
renderer = SDL_CreateRenderer(window, NULL);
|
||||
if (!renderer)
|
||||
{
|
||||
std::cout << "ERROR: Renderer couldnt be created! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (!Mix_OpenAudio(0, NULL))
|
||||
{
|
||||
std::cout << "ERROR: Mixer couldnt be initialized! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
Mix_Volume(-1, MIX_MAX_VOLUME);
|
||||
Mix_AllocateChannels(16);
|
||||
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 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");
|
||||
|
||||
// loading music
|
||||
// assets->addMusic("background_music", "assets/sound/background_music.mp3");
|
||||
this->gameInstance->init();
|
||||
|
||||
this->gameInstance = GameFactory::instance().create(this);
|
||||
this->gameInstance->init();
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void GameInternal::handleEvents()
|
||||
{
|
||||
SDL_PollEvent(&event);
|
||||
SDL_AppResult GameInternal::handleEvent(SDL_Event* event) {
|
||||
SDL_AppResult result = this->eventManager->handleEvent(event);
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT: this->setRunning(false);
|
||||
break;
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
this->clean();
|
||||
return result == SDL_APP_FAILURE ? SDL_APP_FAILURE : SDL_APP_SUCCESS;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void GameInternal::update()
|
||||
void GameInternal::update(Uint64 frameTime)
|
||||
{
|
||||
manager.refresh();
|
||||
manager.update();
|
||||
manager.refresh();
|
||||
|
||||
this->gameInstance->update(); // TODO: this might have to be split up into two update functions, before and after manager...
|
||||
uint_fast16_t diffTime = frameTime - this->lastFrameTime;
|
||||
manager.update(diffTime);
|
||||
|
||||
this->gameInstance->update(diffTime); // TODO: this might have to be split up into two update functions, before and after manager...
|
||||
|
||||
this->lastFrameTime = frameTime;
|
||||
}
|
||||
|
||||
void GameInternal::render()
|
||||
{
|
||||
SDL_RenderClear(renderer);
|
||||
this->renderManager.renderAll();
|
||||
SDL_RenderPresent(renderer);
|
||||
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;
|
||||
delete(textureManager);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
std::cout << "Game Cleaned!" << std::endl;
|
||||
}
|
||||
|
||||
bool GameInternal::isRunning() const
|
||||
{
|
||||
return running;
|
||||
return running;
|
||||
}
|
||||
|
||||
void GameInternal::setRunning(bool running) //TODO: might be depracted
|
||||
{
|
||||
this->running = running;
|
||||
this->running = running;
|
||||
}
|
||||
|
||||
void GameInternal::stopGame()
|
||||
{
|
||||
this->running = false;
|
||||
this->running = false;
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ InputComponent::~InputComponent() = default;
|
||||
|
||||
void InputComponent::init(){}
|
||||
|
||||
void InputComponent::update()
|
||||
void InputComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
SDL_PumpEvents();
|
||||
}
|
||||
|
||||
303
src/InputManager.cpp
Normal file
303
src/InputManager.cpp
Normal file
@ -0,0 +1,303 @@
|
||||
#include "InputManager.h"
|
||||
#include "InteractionEventdataStruct.h"
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include <iostream>
|
||||
#include "SDL3/SDL_stdinc.h"
|
||||
#include "VEGO.h"
|
||||
#include "VEGO_Event.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, InputManager::Key key) {
|
||||
static const std::unordered_map<InputManager::Key, std::string> keyToString {
|
||||
{InputManager::Key::UP, "UP"},
|
||||
{InputManager::Key::DOWN, "DOWN"},
|
||||
{InputManager::Key::LEFT, "LEFT"},
|
||||
{InputManager::Key::RIGHT, "RIGHT"},
|
||||
{InputManager::Key::SPACE, "SPACE"},
|
||||
{InputManager::Key::ENTER, "ENTER"},
|
||||
{InputManager::Key::ESCAPE, "ESCAPE"},
|
||||
{InputManager::Key::TAB, "TAB"},
|
||||
{InputManager::Key::BACKSPACE, "BACKSPACE"},
|
||||
{InputManager::Key::DELETE, "DELETE"},
|
||||
{InputManager::Key::HOME, "HOME"},
|
||||
{InputManager::Key::END, "END"},
|
||||
{InputManager::Key::PAGE_UP, "PAGE_UP"},
|
||||
{InputManager::Key::PAGE_DOWN, "PAGE_DOWN"},
|
||||
{InputManager::Key::INSERT, "INSERT"},
|
||||
{InputManager::Key::CAPS_LOCK, "CAPS_LOCK"},
|
||||
{InputManager::Key::LEFT_SHIFT, "LEFT_SHIFT"},
|
||||
{InputManager::Key::RIGHT_SHIFT, "RIGHT_SHIFT"},
|
||||
{InputManager::Key::LEFT_CTRL, "LEFT_CTRL"},
|
||||
{InputManager::Key::RIGHT_CTRL, "RIGHT_CTRL"},
|
||||
{InputManager::Key::LEFT_ALT, "LEFT_ALT"},
|
||||
{InputManager::Key::RIGHT_ALT, "RIGHT_ALT"},
|
||||
{InputManager::Key::F1, "F1"},
|
||||
{InputManager::Key::F2, "F2"},
|
||||
{InputManager::Key::F3, "F3"},
|
||||
{InputManager::Key::F4, "F4"},
|
||||
{InputManager::Key::F5, "F5"},
|
||||
{InputManager::Key::F6, "F6"},
|
||||
{InputManager::Key::F7, "F7"},
|
||||
{InputManager::Key::F8, "F8"},
|
||||
{InputManager::Key::F9, "F9"},
|
||||
{InputManager::Key::F10, "F10"},
|
||||
{InputManager::Key::F11, "F11"},
|
||||
{InputManager::Key::F12, "F12"},
|
||||
{InputManager::Key::A, "A"},
|
||||
{InputManager::Key::B, "B"},
|
||||
{InputManager::Key::C, "C"},
|
||||
{InputManager::Key::D, "D"},
|
||||
{InputManager::Key::E, "E"},
|
||||
{InputManager::Key::F, "F"},
|
||||
{InputManager::Key::G, "G"},
|
||||
{InputManager::Key::H, "H"},
|
||||
{InputManager::Key::I, "I"},
|
||||
{InputManager::Key::J, "J"},
|
||||
{InputManager::Key::K, "K"},
|
||||
{InputManager::Key::L, "L"},
|
||||
{InputManager::Key::M, "M"},
|
||||
{InputManager::Key::N, "N"},
|
||||
{InputManager::Key::O, "O"},
|
||||
{InputManager::Key::P, "P"},
|
||||
{InputManager::Key::Q, "Q"},
|
||||
{InputManager::Key::R, "R"},
|
||||
{InputManager::Key::S, "S"},
|
||||
{InputManager::Key::T, "T"},
|
||||
{InputManager::Key::U, "U"},
|
||||
{InputManager::Key::V, "V"},
|
||||
{InputManager::Key::W, "W"},
|
||||
{InputManager::Key::X, "X"},
|
||||
{InputManager::Key::Y, "Y"},
|
||||
{InputManager::Key::Z, "Z"},
|
||||
{InputManager::Key::NUM_0, "NUM_0"},
|
||||
{InputManager::Key::NUM_1, "NUM_1"},
|
||||
{InputManager::Key::NUM_2, "NUM_2"},
|
||||
{InputManager::Key::NUM_3, "NUM_3"},
|
||||
{InputManager::Key::NUM_4, "NUM_4"},
|
||||
{InputManager::Key::NUM_5, "NUM_5"},
|
||||
{InputManager::Key::NUM_6, "NUM_6"},
|
||||
{InputManager::Key::NUM_7, "NUM_7"},
|
||||
{InputManager::Key::NUM_8, "NUM_8"},
|
||||
{InputManager::Key::NUM_9, "NUM_9"},
|
||||
{InputManager::Key::LEFT_BRACKET, "LEFT_BRACKET"},
|
||||
{InputManager::Key::RIGHT_BRACKET, "RIGHT_BRACKET"},
|
||||
{InputManager::Key::SEMICOLON, "SEMICOLON"},
|
||||
{InputManager::Key::APOSTROPHE, "APOSTROPHE"},
|
||||
{InputManager::Key::COMMA, "COMMA"},
|
||||
{InputManager::Key::PERIOD, "PERIOD"},
|
||||
{InputManager::Key::SLASH, "SLASH"},
|
||||
{InputManager::Key::BACKSLASH, "BACKSLASH"},
|
||||
{InputManager::Key::GRAVE, "GRAVE"}
|
||||
};
|
||||
|
||||
auto it = keyToString.find(key);
|
||||
if (it != keyToString.end()) {
|
||||
os << it->second;
|
||||
} else {
|
||||
os << "UNKNOWN_KEY";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction& action) {
|
||||
os << action.name << " with binding(s): ";
|
||||
for (auto& binding : action.bindings) {
|
||||
os << binding << ", ";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction* action) {
|
||||
if (action) {
|
||||
os << *action;
|
||||
} else {
|
||||
os << "NULL_ACTION";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const std::vector<InputManager::InputAction>& actions) {
|
||||
os << "Actions: ";
|
||||
if (actions.empty()) {
|
||||
os << "None";
|
||||
} else {
|
||||
for (size_t i = 0; i < actions.size(); ++i) {
|
||||
os << actions[i];
|
||||
if (i < actions.size() - 1) {
|
||||
os << " | ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
// TODO: find out why it doesnt work??
|
||||
std::ostream& operator<<(std::ostream& os, const std::vector<InputManager::InputAction*>& actions) {
|
||||
os << "Actions: ";
|
||||
if (actions.empty()) {
|
||||
os << "None";
|
||||
} else {
|
||||
for (size_t i = 0; i < actions.size(); ++i) {
|
||||
if (actions[i]) {
|
||||
os << *actions[i];
|
||||
} else {
|
||||
os << "NULL_ACTION";
|
||||
}
|
||||
if (i < actions.size() - 1) {
|
||||
os << " | ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// overloads end --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
InputManager::InputManager() : activeContext("Default") {}
|
||||
|
||||
InputManager::~InputManager() {}
|
||||
|
||||
void InputManager::initKeyMap() {
|
||||
keyMap = {
|
||||
{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}
|
||||
};
|
||||
}
|
||||
|
||||
void InputManager::registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void(bool)> callback, const std::string& context) {
|
||||
InputAction* storedAction = new InputAction{actionName, keys, callback};
|
||||
for (const auto& key : keys) {
|
||||
actionsByContextAndKey[context][key].emplace_back(storedAction);
|
||||
}
|
||||
std::cout << "Registered action: " << storedAction << " in context: " << context << std::endl;
|
||||
}
|
||||
|
||||
std::vector<InputManager::InputAction*> InputManager::getActionsByKey(const Key key) const {
|
||||
std::vector<InputAction*> result;
|
||||
|
||||
for (const auto& [context, keyMap] : actionsByContextAndKey) {
|
||||
auto it = keyMap.find(key);
|
||||
if (it != keyMap.end()) {
|
||||
result.insert(result.end(), it->second.begin(), it->second.end());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void InputManager::setActiveContext(const std::string& context) {
|
||||
activeContext = context;
|
||||
std::cout << "Active context set to: " << activeContext << std::endl;
|
||||
}
|
||||
|
||||
std::string InputManager::getActiveContext() const {
|
||||
return activeContext;
|
||||
}
|
||||
|
||||
SDL_AppResult InputManager::handleEvent(SDL_EventType type, SDL_Event* const event) {
|
||||
if (event->key.repeat) {
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
auto keyIt = std::ranges::find_if(keyMap, [&](const auto& pair)
|
||||
{ return pair.second == event->key.scancode; });
|
||||
|
||||
if (keyIt != keyMap.end()) {
|
||||
std::cout << "in != keymap.end" << std::endl;
|
||||
|
||||
Key pressedKey = keyIt->first;
|
||||
auto keyActions = actionsByContextAndKey[activeContext];
|
||||
auto it = keyActions.find(pressedKey);
|
||||
if (it != keyActions.end()) {
|
||||
for (auto& action : it->second) {
|
||||
std::cout << "Action triggered: " << action->name << " in context: " << activeContext << std::endl;
|
||||
action->callback(type == SDL_EVENT_KEY_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
22
src/InteractionComponent.cpp
Normal file
22
src/InteractionComponent.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "InteractionComponent.h"
|
||||
|
||||
#include "VEGO.h"
|
||||
|
||||
InteractionComponent::InteractionComponent(std::function<void(void*,void*)> callback) : interactionCallback(callback)
|
||||
{
|
||||
VEGO_Game().interactionManager->registerListener(this->entity->getComponentAsPointer<InteractionComponent>());
|
||||
}
|
||||
|
||||
void InteractionComponent::interact(void* actor, void* data)
|
||||
{
|
||||
if (interactionCallback) {
|
||||
interactionCallback(actor, data);
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Vector2D> InteractionComponent::getPosition() // required for targeting strategy, return null to only allow explicit targeting
|
||||
{
|
||||
if (entity->hasComponent<TransformComponent>()) {
|
||||
return std::make_shared<Vector2D>(entity->getComponent<TransformComponent>().position);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
16
src/InteractionEventdataStruct.cpp
Normal file
16
src/InteractionEventdataStruct.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "InteractionEventdataStruct.h"
|
||||
|
||||
#include "VEGO.h"
|
||||
#include "VEGO_Event.h"
|
||||
|
||||
void InteractionEventdataStruct::triggerEvent()
|
||||
{
|
||||
// TODO: if target is null && strategy is 0, error
|
||||
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = vego::VEGO_Event_Interaction;
|
||||
event.user.data1 = this;
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
90
src/InteractionManager.cpp
Normal file
90
src/InteractionManager.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "InteractionManager.h"
|
||||
|
||||
#include "InteractionEventdataStruct.h"
|
||||
#include "InteractionListener.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include "VEGO_Event.h"
|
||||
#include "Vector2D.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
InteractionManager::InteractionManager()
|
||||
{
|
||||
this->targetingFuncs.fill(nullptr);
|
||||
this->targetingFuncs.at(static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::closest)) = [](Vector2D* reference, std::vector<std::shared_ptr<InteractionListener>> input) {
|
||||
return std::shared_ptr<InteractionListener>();
|
||||
};
|
||||
this->targetingFuncs.at(static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::closest)) = [](Vector2D* reference, std::vector<std::shared_ptr<InteractionListener>> input) {
|
||||
auto min = std::ranges::min_element(input, [&reference](std::shared_ptr<InteractionListener>& a, std::shared_ptr<InteractionListener>& b) {
|
||||
std::shared_ptr<Vector2D> coordA = a->getPosition();
|
||||
std::shared_ptr<Vector2D> coordB = b->getPosition();
|
||||
|
||||
if (coordB == nullptr) return true;
|
||||
if (coordA == nullptr) return false;
|
||||
|
||||
return std::sqrt(std::pow(coordA->x - reference->x, 2) + std::pow(coordA->y - reference->y, 2)) < std::sqrt(std::pow(coordB->x - reference->x, 2) + std::pow(coordB->y - reference->y, 2));
|
||||
});
|
||||
return min == std::ranges::end(input) ? nullptr : *min;
|
||||
};
|
||||
this->targetingFuncs.at(static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::manhattenDistance)) = [](Vector2D* reference, std::vector<std::shared_ptr<InteractionListener>> input) {
|
||||
auto min = std::ranges::min_element(input, [&reference](std::shared_ptr<InteractionListener>& a, std::shared_ptr<InteractionListener>& b) {
|
||||
std::shared_ptr<Vector2D> coordA = a->getPosition();
|
||||
std::shared_ptr<Vector2D> coordB = b->getPosition();
|
||||
|
||||
if (coordB == nullptr) return true;
|
||||
if (coordA == nullptr) return false;
|
||||
|
||||
return (std::abs(coordA->x - reference->x) + std::abs(coordA->y - reference->y)) < (std::abs(coordB->x - reference->x) + std::abs(coordB->y - reference->y));
|
||||
});
|
||||
return min == std::ranges::end(input) ? nullptr : *min;
|
||||
};
|
||||
}
|
||||
|
||||
SDL_AppResult InteractionManager::handleInteract(SDL_EventType type, SDL_Event* const event)
|
||||
{
|
||||
if (type != vego::VEGO_Event_Interaction) { // error handling
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
InteractionEventdataStruct* data = static_cast<InteractionEventdataStruct*>(event->user.data1);
|
||||
|
||||
std::shared_ptr<InteractionListener> listener = data->target.lock();
|
||||
|
||||
if (data->strategy != static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::none)) {
|
||||
listener = this->targetingFuncs.at(data->strategy)(
|
||||
data->targetingReference.get(),
|
||||
this->listeners
|
||||
| std::views::transform(&std::weak_ptr<InteractionListener>::lock)
|
||||
| std::views::filter(&std::shared_ptr<InteractionListener>::operator bool)
|
||||
| std::ranges::to<std::vector>()
|
||||
);
|
||||
}
|
||||
|
||||
if (listener) {
|
||||
listener->interact(data->actor, data->data);
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void InteractionManager::registerListener(std::weak_ptr<InteractionListener> listener)
|
||||
{
|
||||
this->listeners.emplace_back(listener);
|
||||
}
|
||||
|
||||
uint8_t InteractionManager::registerTargetingFunc(TargetingFunc func)
|
||||
{
|
||||
auto it = std::ranges::find_if(this->targetingFuncs, [](const auto& func) {
|
||||
return !func;
|
||||
});
|
||||
(*it) = func;
|
||||
return std::distance(this->targetingFuncs.begin(), it);
|
||||
}
|
||||
@ -27,9 +27,9 @@ void Manager::refresh()
|
||||
std::end(entities));
|
||||
}
|
||||
|
||||
void Manager::update()
|
||||
void Manager::update(uint_fast16_t diffTime)
|
||||
{
|
||||
for (auto& e : entities) e->update();
|
||||
for (auto& e : entities) e->update(diffTime);
|
||||
}
|
||||
|
||||
void Manager::addToGroup(Entity* mEntity, Group mGroup)
|
||||
|
||||
272
src/Map.cpp
272
src/Map.cpp
@ -2,170 +2,184 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL_error.h>
|
||||
#include <SDL_render.h>
|
||||
#include <SDL3/SDL_error.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
|
||||
#include <tmxlite/Layer.hpp>
|
||||
#include <tmxlite/Map.hpp>
|
||||
#include <tmxlite/Tileset.hpp>
|
||||
#include <tmxlite/Property.hpp>
|
||||
#include <tmxlite/TileLayer.hpp>
|
||||
#include <tmxlite/Types.hpp>
|
||||
|
||||
#include "Constants.h"
|
||||
#include "ColliderComponent.h"
|
||||
#include "GameInternal.h"
|
||||
#include "SpriteComponent.h"
|
||||
#include "TextureManager.h"
|
||||
#include "TileComponent.h"
|
||||
#include "VEGO.h"
|
||||
#include "tmxlite/Types.hpp"
|
||||
|
||||
void Map::loadMap(const char* path, int sizeX, int sizeY, GameInternal* game, const std::map<int, std::pair<std::string, bool>>* textureDict /* backreference */)
|
||||
{
|
||||
std::string tileIDstr;
|
||||
char singleChar = 0;
|
||||
std::ifstream mapFile;
|
||||
mapFile.open(path);
|
||||
|
||||
if (!mapFile.is_open()) {
|
||||
SDL_SetError("Error loading map: Couldn't open map file!");
|
||||
std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
}
|
||||
template<> std::optional<bool> Map::getLayerProperty(const std::vector<tmx::Property>& properties, std::string propertyName) {
|
||||
auto zIndexIterator = std::ranges::find_if(properties, [propertyName](const tmx::Property& property) {
|
||||
return property.getName().compare(propertyName) == 0;
|
||||
});
|
||||
|
||||
int x = 0, y = 0; // needed outside for-loop for error handling
|
||||
for (; !mapFile.eof(); mapFile.get(singleChar))
|
||||
{
|
||||
if (singleChar == ',' || singleChar == '\n') {
|
||||
if (tileIDstr.empty())
|
||||
continue;
|
||||
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!");
|
||||
std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
}
|
||||
x = 0;
|
||||
y++;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!std::isdigit(singleChar)) continue;
|
||||
tileIDstr += singleChar;
|
||||
}
|
||||
if (y != sizeY) {
|
||||
SDL_SetError("Error loading map: specified y size doesn't match map file!");
|
||||
std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl;
|
||||
SDL_ClearError();
|
||||
}
|
||||
|
||||
mapFile.close();
|
||||
}
|
||||
|
||||
void Map::addTile(unsigned long id, int x, int y, GameInternal* game, const std::map<int, std::pair<std::string, bool>>* textureDict) // tile entity
|
||||
{
|
||||
auto& tile(game->manager.addEntity());
|
||||
tile.addComponent<TileComponent>(x, y, TILE_SIZE, TILE_SIZE, id, textureDict);
|
||||
|
||||
if(tile.getComponent<TileComponent>().hasCollision()) tile.addComponent<ColliderComponent>("tile"/*tile.getComponent<TileComponent>().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
|
||||
if (zIndexIterator != properties.end() && zIndexIterator->getType() == tmx::Property::Type::Boolean) {
|
||||
return zIndexIterator->getBoolValue();
|
||||
}
|
||||
|
||||
const std::vector<tmx::Tileset>& tileSets = map.getTilesets();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::vector<tmx::Layer::Ptr>& mapLayers = map.getLayers();
|
||||
const auto mapSize = map.getTileCount();
|
||||
const auto mapTileSize = map.getTileSize();
|
||||
template<> std::optional<int> Map::getLayerProperty(const std::vector<tmx::Property>& properties, std::string propertyName)
|
||||
{
|
||||
auto zIndexIterator = std::ranges::find_if(properties, [propertyName](const tmx::Property& property) {
|
||||
return property.getName().compare(propertyName) == 0;
|
||||
});
|
||||
|
||||
if (zIndexIterator != properties.end() && zIndexIterator->getType() == tmx::Property::Type::Int) {
|
||||
return zIndexIterator->getIntValue();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Map::Map(const char* path)
|
||||
{
|
||||
if (!this->map.load(path)) {
|
||||
// TODO: log to console
|
||||
// TODO: error handling
|
||||
}
|
||||
|
||||
std::vector<std::string> texturePaths = {};
|
||||
|
||||
for (auto tileSet : tileSets) {
|
||||
for (const auto& tileSet : map.getTilesets()) {
|
||||
texturePaths.emplace_back(tileSet.getImagePath());
|
||||
}
|
||||
|
||||
for (auto& layer : mapLayers) {
|
||||
this->mapData = {
|
||||
&map.getTilesets(),
|
||||
&map.getLayers(),
|
||||
&map.getTileCount(),
|
||||
&map.getTileSize(),
|
||||
&texturePaths
|
||||
};
|
||||
|
||||
|
||||
for (auto& layer : *this->mapData.mapLayers) {
|
||||
|
||||
if (layer->getType() == tmx::Layer::Type::Tile) {
|
||||
auto& tileLayer = layer->getLayerAs<tmx::TileLayer>();
|
||||
|
||||
int zIndex = 0;
|
||||
|
||||
const std::vector<tmx::Property>& 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_convertible<decltype(zIndexIterator->getType()), 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<float>(x) * mapTileSize.x;
|
||||
const float tilePosY = (static_cast<float>(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;
|
||||
}
|
||||
loadTileLayer(layer->getLayerAs<tmx::TileLayer>());
|
||||
continue;
|
||||
}
|
||||
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)
|
||||
void Map::loadTileLayer(const tmx::TileLayer& layer)
|
||||
{
|
||||
const std::vector<tmx::Property>& properties = layer.getProperties();
|
||||
int zIndex = getLayerProperty<int>(properties, "zIndex").value_or(0);
|
||||
bool collision = getLayerProperty<bool>(properties, "collision").value_or(false);
|
||||
|
||||
const auto& tiles = layer.getTiles();
|
||||
|
||||
// for each tile set
|
||||
auto tileConstructorRange = std::views::iota(0)
|
||||
| std::views::take(this->mapData.tileSets->size())
|
||||
// return the tile set metadata
|
||||
| std::views::transform([&](uint16_t i) {
|
||||
const char* texturePath = this->mapData.texturePaths->at(i).c_str();
|
||||
|
||||
tmx::Vector2f textureSize;
|
||||
SDL_GetTextureSize(
|
||||
VEGO_Game().textureManager->loadMapTileTexture(texturePath),
|
||||
&(textureSize.x),
|
||||
&(textureSize.y)
|
||||
);
|
||||
|
||||
tmx::Vector2u tileCount2D = { static_cast<unsigned int>(textureSize.x / this->mapData.mapTileSize->x), static_cast<unsigned int>(textureSize.y / this->mapData.mapTileSize->y) };
|
||||
|
||||
uint32_t tileCount = this->mapData.tileSets->at(i).getTileCount();
|
||||
uint32_t firstGID = this->mapData.tileSets->at(i).getFirstGID();
|
||||
|
||||
return TileSetData { texturePath, textureSize, tileCount, tileCount2D, firstGID };
|
||||
})
|
||||
| std::views::transform([=, this](const TileSetData& data) {
|
||||
// for each tile on the tile set
|
||||
return std::views::iota(0)
|
||||
| std::views::take(this->mapData.mapSize->x * this->mapData.mapSize->y)
|
||||
// only take tiles that are on the ID range of the tile set
|
||||
| std::views::filter([=](uint16_t idx) {
|
||||
return
|
||||
idx < tiles.size()
|
||||
&& tiles[idx].ID >= data.firstGID
|
||||
&& tiles[idx].ID < (data.firstGID + data.tileCount);
|
||||
})
|
||||
// extract tile data
|
||||
| std::views::transform([=, this](uint16_t idx) {
|
||||
const auto x = idx % this->mapData.mapSize->x;
|
||||
const auto y = idx / this->mapData.mapSize->x;
|
||||
|
||||
const auto idIndex = (tiles[idx].ID - data.firstGID);
|
||||
|
||||
uint32_t u = idIndex % data.tileCount2D.x;
|
||||
uint32_t v = idIndex / data.tileCount2D.y;
|
||||
u *= this->mapData.mapTileSize->x; // TODO: we should be using the tile set size, as this may be different from the map's grid size
|
||||
v *= this->mapData.mapTileSize->y;
|
||||
|
||||
// normalise the UV
|
||||
u /= data.textureSize.x;
|
||||
v /= data.textureSize.y;
|
||||
|
||||
// vert pos
|
||||
const float tilePosX = static_cast<float>(x) * this->mapData.mapTileSize->x;
|
||||
const float tilePosY = (static_cast<float>(y) * this->mapData.mapTileSize->y);
|
||||
|
||||
// return tile data as a function to spawn said tile
|
||||
return std::function<void()>(
|
||||
[tilePosX, tilePosY, capture0 = *this->mapData.mapTileSize, u, v, zIndex, capture1 = data.texturePath, collision] {
|
||||
Map::addTile(tilePosX, tilePosY, capture0, u, v, zIndex, capture1, collision);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
// 2D view to 1D vector; might be better keep as view with scene management
|
||||
| std::views::join
|
||||
| std::ranges::to<std::vector>();
|
||||
|
||||
this->tileConstructors.insert(this->tileConstructors.end(), tileConstructorRange.begin(), tileConstructorRange.end());
|
||||
}
|
||||
|
||||
void Map::addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, std::string texturePath, bool hasCollision)
|
||||
{
|
||||
auto& tile(VEGO_Game().manager.addEntity());
|
||||
|
||||
tile.addComponent<TransformComponent>(x, y, mapTileSize.x, mapTileSize.y, 1);
|
||||
tile.addComponent<SpriteComponent>(texturePath, v, u, zIndex); // why does uv need to be reversed?
|
||||
tile.addComponent<SpriteComponent>(texturePath.c_str(), v, u, zIndex); // why does uv need to be reversed?
|
||||
//TODO: also implement updated map stuff for this
|
||||
|
||||
if (hasCollision) {
|
||||
// tag currently does not have a clear purposes, TODO: figure out appropriate tag name
|
||||
tile.addComponent<ColliderComponent>("hello I am a collider of a tile!");
|
||||
tile.addGroup((size_t)Entity::GroupLabel::MAPTILES);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::generateTiles()
|
||||
{
|
||||
std::ranges::for_each(this->tileConstructors, [](auto& function) {
|
||||
function();
|
||||
});
|
||||
}
|
||||
@ -1,18 +1,22 @@
|
||||
#include "PowerupComponent.h"
|
||||
#include "PickupComponent.h"
|
||||
#include "GameInternal.h"
|
||||
#include "CollisionHandler.h"
|
||||
#include "Entity.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "SpriteComponent.h"
|
||||
#include "StatEffectsComponent.h"
|
||||
#include "Constants.h"
|
||||
#include "TextureManager.h"
|
||||
#include "TransformComponent.h"
|
||||
#include <cstdint>
|
||||
#include "VEGO.h"
|
||||
|
||||
PowerupComponent::PowerupComponent(std::function<void (Entity*)> func)
|
||||
PickupComponent::PickupComponent(std::function<void (Entity*)> func)
|
||||
{
|
||||
this->pickupFunc = func;
|
||||
}
|
||||
|
||||
void PowerupComponent::update()
|
||||
void PickupComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
Entity* player;
|
||||
if ((player = this->entity->getManager().getGame()->collisionHandler->getAnyIntersection<Entity*>(
|
||||
65
src/PickupManager.cpp
Normal file
65
src/PickupManager.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include "PickupManager.h"
|
||||
|
||||
#include "TextureManager.h"
|
||||
#include "SoundManager.h"
|
||||
#include "ProjectileComponent.h"
|
||||
#include "GameInternal.h"
|
||||
|
||||
#include "TransformComponent.h"
|
||||
|
||||
#include "CollisionHandler.h"
|
||||
#include "ColliderComponent.h"
|
||||
#include "Constants.h"
|
||||
#include "Entity.h"
|
||||
#include "Vector2D.h"
|
||||
#include "PickupComponent.h"
|
||||
#include <iostream>
|
||||
#include <VEGO.h>
|
||||
|
||||
#include "Textures.h"
|
||||
|
||||
PickupManager::PickupManager(Manager* manager) : man(manager) {}
|
||||
|
||||
PickupManager::~PickupManager() {}
|
||||
|
||||
void PickupManager::createPowerup(Vector2D pos, std::function<void (Entity*)> pickupFunc, Textures texture) {
|
||||
|
||||
auto& powerups(man->addEntity());
|
||||
powerups.addComponent<TransformComponent>(pos.x, pos.y, 32, 32, 1); //32x32 is standard size for objects
|
||||
|
||||
try {
|
||||
powerups.addComponent<SpriteComponent>(texture, 3);
|
||||
}
|
||||
catch (std::runtime_error e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
powerups.addComponent<ColliderComponent>("powerup", 0.6f);
|
||||
powerups.addComponent<PickupComponent>(pickupFunc);
|
||||
powerups.addGroup((size_t)Entity::GroupLabel::POWERUPS);
|
||||
}
|
||||
|
||||
Vector2D PickupManager::calculateSpawnPosition()
|
||||
{
|
||||
Vector2D spawnPos = Vector2D(-1, -1);
|
||||
bool conflict = false;
|
||||
for (int i = 0; i <= SPAWN_ATTEMPTS; i++)
|
||||
{
|
||||
SDL_Rect spawnRect;
|
||||
spawnRect.h = spawnRect.w = 32;
|
||||
spawnRect.x = rand() % (VEGO_Game().config->getFinalConfig().at("screen_width").get<int>() - spawnRect.w);
|
||||
spawnRect.y = rand() % (VEGO_Game().config->getFinalConfig().at("screen_height").get<int>() - spawnRect.h);
|
||||
conflict = false;
|
||||
for (auto cc : this->man->getGame()->collisionHandler->getColliders({ Entity::GroupLabel::MAPTILES }))
|
||||
{
|
||||
if (SDL_HasRectIntersection(&spawnRect, &cc->collider) && strcmp(cc->tag, "projectile"))
|
||||
{
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conflict) continue;
|
||||
spawnPos = Vector2D(spawnRect.x, spawnRect.y);
|
||||
}
|
||||
return spawnPos;
|
||||
}
|
||||
@ -14,12 +14,12 @@ void ProjectileComponent::init()
|
||||
{
|
||||
transformComponent = &entity->getComponent<TransformComponent>();
|
||||
transformComponent->direction = direction;
|
||||
SoundManager::playSound(this->entity->getManager().getGame(), "throw_egg", true, PLAY_ONCE, MAX_VOLUME, -1);
|
||||
SoundManager::playSound(this->soundEffect, true, PLAY_ONCE, MAX_VOLUME, -1);
|
||||
}
|
||||
|
||||
void ProjectileComponent::update()
|
||||
void ProjectileComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
distance += speed;
|
||||
distance += speed * diffTime * (1.f/1000);
|
||||
|
||||
IntersectionBitSet boundsIntersection = this->entity->getManager().getGame()->collisionHandler->getIntersectionWithBounds(entity);
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#include "RenderObject.h"
|
||||
#include "RenderManager.h"
|
||||
|
||||
RenderObject::RenderObject(int zIndex, RenderManager& renderManager) : zIndex(zIndex), renderManager(renderManager) {
|
||||
renderManager.add(this);
|
||||
RenderObject::RenderObject(int zIndex, RenderManager* renderManager) : zIndex(zIndex), renderManager(renderManager) {
|
||||
renderManager->add(this);
|
||||
}
|
||||
|
||||
RenderObject::~RenderObject() {
|
||||
this->renderManager.remove(this);
|
||||
this->renderManager->remove(this);
|
||||
}
|
||||
@ -1,15 +1,16 @@
|
||||
#include "SoundManager.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "GameInternal.h"
|
||||
#include "AssetManager.h"
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
|
||||
#include "GameInternal.h"
|
||||
|
||||
/*
|
||||
Mix_Music* SoundManager::loadMusic(const char* fileName)
|
||||
{
|
||||
auto it = this->music_cache.find(fileName);
|
||||
//auto it = this->music_cache.find(fileName);
|
||||
|
||||
if (it != this->music_cache.end()) {
|
||||
return it->second;
|
||||
@ -47,14 +48,21 @@ Mix_Chunk* SoundManager::loadSound(const char* fileName)
|
||||
return sound;
|
||||
}
|
||||
|
||||
void SoundManager::playSound(GameInternal* game, std::string sound, bool canOverlap, int loops, int volume, int channel)
|
||||
*/
|
||||
|
||||
void SoundManager::playSound(SoundEffects sound, bool canOverlap, int loops, int volume, int channel)
|
||||
{
|
||||
if (!this_instance->sound_cache.contains(sound)) {
|
||||
std::cerr << "Error playing Sound-Effect: sound effect not found" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!canOverlap)
|
||||
{
|
||||
// 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) &&
|
||||
Mix_GetChunk(channel) == this_instance->sound_cache.at(sound) &&
|
||||
channel != -1)
|
||||
{
|
||||
return;
|
||||
@ -63,36 +71,36 @@ void SoundManager::playSound(GameInternal* game, std::string sound, bool canOver
|
||||
Mix_HaltChannel(channel);
|
||||
}
|
||||
|
||||
if(Mix_VolumeChunk(game->assets->getSound(sound), volume) == -1)
|
||||
if(Mix_VolumeChunk(this_instance->sound_cache.at(sound), volume) == -1)
|
||||
{
|
||||
std::cerr << "Error adjusting volume: " << Mix_GetError() << std::endl;
|
||||
std::cerr << "Error adjusting volume: " << SDL_GetError() << std::endl;
|
||||
}
|
||||
|
||||
if (Mix_PlayChannel(channel, game->assets->getSound(sound), loops) == -1)
|
||||
if (Mix_PlayChannel(channel, this_instance->sound_cache.at(sound), loops) == -1)
|
||||
{
|
||||
std::cerr << "Error playing sound '" << sound << "': " << Mix_GetError() << std::endl;
|
||||
std::cerr << "Error playing sound " << ": " << SDL_GetError() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::playMusic(GameInternal* game, std::string music, int loops, int volume, int ms)
|
||||
void SoundManager::playMusic(BackgroundMusic music, int loops, int volume, int milliseconds)
|
||||
{
|
||||
if (!this_instance->music_cache.contains(music)) {
|
||||
std::cerr << "Error playing music: music not found" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mix_PlayingMusic() != 0 || Mix_Fading() == Mix_Fading::MIX_FADING_IN)
|
||||
return;
|
||||
|
||||
if(ms > 0)
|
||||
if(milliseconds > 0)
|
||||
{
|
||||
Mix_FadeInMusic(game->assets->getMusic(music), loops, ms);
|
||||
Mix_FadeInMusic(this_instance->music_cache.at(music), loops, milliseconds);
|
||||
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;
|
||||
std::cerr << "Error adjusting volume: " << SDL_GetError() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,3 +141,51 @@ void SoundManager::fadeOutMusic(int ms)
|
||||
|
||||
Mix_FadeOutMusic(ms);
|
||||
}
|
||||
|
||||
void SoundManager::addSingleSoundEffect(SoundEffects soundEffect, const char *path) {
|
||||
if (this_instance->sound_cache.contains(soundEffect)) {
|
||||
std::cerr << "Error when adding Sound-Effect: sound-effect with that key already in cache" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Mix_Chunk* sound = Mix_LoadWAV(path);
|
||||
|
||||
if (sound == nullptr) {
|
||||
std::cerr << "Error when loading Sound-Effect: could not load sound effect from " << path << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
this_instance->sound_cache.emplace(soundEffect, sound);
|
||||
}
|
||||
|
||||
void SoundManager::addSingleBackgroundMusic(BackgroundMusic backgroundMusic, const char *path) {
|
||||
if (this_instance->music_cache.contains(backgroundMusic)) {
|
||||
std::cerr << "Error when adding Sound-Effect: sound-effect with that key already in cache" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Mix_Music* music = Mix_LoadMUS(path);
|
||||
|
||||
if (music == nullptr) {
|
||||
std::cerr << "Error when loading Sound-Effect: could not load sound effect from " << path << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
this_instance->music_cache.emplace(backgroundMusic, music);
|
||||
}
|
||||
|
||||
void SoundManager::addSoundEffects(const std::map<SoundEffects, const char *> &effects) {
|
||||
for (auto effect : effects)
|
||||
addSingleSoundEffect(effect.first, effect.second);
|
||||
}
|
||||
|
||||
void SoundManager::addBackgroundMusic(const std::map<BackgroundMusic, const char *> &backgroundMusic) {
|
||||
for (auto track : backgroundMusic)
|
||||
addSingleBackgroundMusic(track.first, track.second);
|
||||
}
|
||||
|
||||
SoundManager* SoundManager::this_instance = nullptr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "SpriteComponent.h"
|
||||
|
||||
#include <SDL_timer.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
@ -15,18 +15,25 @@
|
||||
#include "Manager.h"
|
||||
#include "VEGO.h"
|
||||
|
||||
SpriteComponent::SpriteComponent(const char* path, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(0), textureYOffset(0)
|
||||
SpriteComponent::SpriteComponent(Textures texture, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(0), textureYOffset(0)
|
||||
{
|
||||
this->texturePath = path;
|
||||
this->textureEnum = texture;
|
||||
this->path = "";
|
||||
}
|
||||
|
||||
SpriteComponent::SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(xOffset), textureYOffset(yOffset)
|
||||
SpriteComponent::SpriteComponent(Textures texture, int xOffset, int yOffset, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(xOffset), textureYOffset(yOffset)
|
||||
{
|
||||
this->texturePath = path;
|
||||
this->textureEnum = texture;
|
||||
this->path = "";
|
||||
}
|
||||
|
||||
SpriteComponent::SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(xOffset), textureYOffset(yOffset) {
|
||||
|
||||
this->path = path;
|
||||
}
|
||||
|
||||
SpriteComponent::SpriteComponent(
|
||||
const char* path,
|
||||
Textures texture,
|
||||
bool isAnimated,
|
||||
std::map<std::string, std::unique_ptr<Animation>>* animationMap,
|
||||
std::string defaultAnimation,
|
||||
@ -38,19 +45,26 @@ SpriteComponent::SpriteComponent(
|
||||
|
||||
playAnimation(defaultAnimation);
|
||||
|
||||
this->texturePath = path;
|
||||
this->textureEnum = texture;
|
||||
|
||||
this->path = "";
|
||||
}
|
||||
|
||||
SpriteComponent::~SpriteComponent() {}
|
||||
|
||||
void SpriteComponent::setTexture(const char* path)
|
||||
void SpriteComponent::setTexture(Textures texture)
|
||||
{
|
||||
this->texture = VEGO_Game().textureManager->loadTexture(path);
|
||||
this->texture = VEGO_Game().textureManager->loadTexture(texture);
|
||||
}
|
||||
|
||||
void SpriteComponent::init()
|
||||
{
|
||||
setTexture(this->texturePath);
|
||||
if (this->path == "") {
|
||||
setTexture(this->textureEnum);
|
||||
}
|
||||
else {
|
||||
setMapTileTexture(this->path);
|
||||
}
|
||||
|
||||
this->transform = &entity->getComponent<TransformComponent>();
|
||||
|
||||
@ -59,14 +73,14 @@ void SpriteComponent::init()
|
||||
this->srcRect.x = this->textureXOffset * this->srcRect.w;
|
||||
this->srcRect.y = this->textureYOffset * this->srcRect.h;;
|
||||
|
||||
this->update();
|
||||
this->update(0);
|
||||
}
|
||||
|
||||
void SpriteComponent::update()
|
||||
void SpriteComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
// This code is not compatible for animated tiles
|
||||
if (animated) {
|
||||
srcRect.x = srcRect.w * static_cast<int>((SDL_GetTicks() / speed) % frames);
|
||||
srcRect.x = srcRect.w * static_cast<int>((SDL_GetTicks() / speed) % frames); // TODO: should not call SDL_GetTicks() but use diffTime
|
||||
|
||||
srcRect.y = animationIndex * transform->height;
|
||||
}
|
||||
@ -92,4 +106,8 @@ void SpriteComponent::playAnimation(std::string type)
|
||||
void SpriteComponent::setDirection(Direction direction)
|
||||
{
|
||||
this->flipped = direction == Direction::RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteComponent::setMapTileTexture(const char *path) {
|
||||
this->texture = VEGO_Game().textureManager->loadMapTileTexture(path);
|
||||
}
|
||||
|
||||
@ -1,56 +1,26 @@
|
||||
#include "StatEffectsComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "TransformComponent.h"
|
||||
// #include "KeyboardController.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
void StatEffectsComponent::init()
|
||||
{}
|
||||
|
||||
void StatEffectsComponent::update()
|
||||
void StatEffectsComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
for (int i = 0; i < MAX_STATS; i++)
|
||||
{
|
||||
if (this->buffs.at(i) == 0) continue;
|
||||
if (this->buffs.at(i) - 1 == 0)
|
||||
{
|
||||
this->resetStatValue((Stats)i);
|
||||
for (auto it = effects.begin(); it != effects.end(); ) {
|
||||
it->duration -= diffTime;
|
||||
|
||||
if (it->duration <= 0) {
|
||||
it->resetFunction();
|
||||
it = effects.erase(it);
|
||||
continue;
|
||||
}
|
||||
this->buffs.at(i) -= 1;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void StatEffectsComponent::modifyStatDur(Stats stat, int duration, int value)
|
||||
{
|
||||
if(this->buffs.at((uint8_t)stat) == 0) this->modifyStatValue(stat, value);
|
||||
this->buffs.at((uint8_t)stat) += duration;
|
||||
}
|
||||
|
||||
void StatEffectsComponent::modifyStatValue(Stats stat, int modifier) //modifier is basically there so the modifyfuncs in the components know if stats should be increased or decreased
|
||||
{
|
||||
switch (stat)
|
||||
{
|
||||
case Stats::MOVEMENT_SPEED:
|
||||
this->entity->getComponent<TransformComponent>().modifySpeed(modifier);
|
||||
break;
|
||||
case Stats::ATTACK_SPEED:
|
||||
// this->entity->getComponent<KeyboardController>().modifyAtkSpeed(modifier);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void StatEffectsComponent::resetStatValue(Stats stat)
|
||||
{
|
||||
switch (stat)
|
||||
{
|
||||
case Stats::MOVEMENT_SPEED:
|
||||
this->entity->getComponent<TransformComponent>().resetSpeedMod();
|
||||
break;
|
||||
case Stats::ATTACK_SPEED:
|
||||
// this->entity->getComponent<KeyboardController>().resetAtkSpeedMod();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
void StatEffectsComponent::addEffect(uint32_t duration, std::function<void()> resetFunction) {
|
||||
effects.push_back({duration, resetFunction});
|
||||
}
|
||||
@ -1,26 +1,62 @@
|
||||
#include "TextureManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <VEGO.h>
|
||||
|
||||
#include "GameInternal.h"
|
||||
|
||||
SDL_Texture* TextureManager::loadTexture(const char* fileName)
|
||||
{
|
||||
auto it = this->texture_cache.find(fileName);
|
||||
if (it != this->texture_cache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
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::addSingleTexture(Textures texture, const char* filePath) {
|
||||
auto sdlTexture = IMG_LoadTexture(VEGO_Game().renderer, filePath);
|
||||
|
||||
if (sdlTexture == nullptr)
|
||||
throw std::runtime_error(std::string("Couldn't load texture '") + filePath + "'");
|
||||
|
||||
SDL_SetTextureScaleMode(sdlTexture, this->scaleMode); // linear scaling results in blurry images
|
||||
this->texture_cache.emplace(texture, sdlTexture);
|
||||
if (filePath != nullptr) {
|
||||
this->texture_references.emplace(texture, std::string(filePath));
|
||||
}
|
||||
std::cout << "Loaded texture at " << filePath << std::endl;
|
||||
}
|
||||
|
||||
void TextureManager::draw(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect src, SDL_Rect dest, bool flipped)
|
||||
void TextureManager::addTextures(const std::map<Textures, const char*> &textures) {
|
||||
for (auto texture : textures) {
|
||||
addSingleTexture(texture.first, texture.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SDL_Texture* TextureManager::loadTexture(Textures texture) {
|
||||
auto it = this->texture_cache.find(texture);
|
||||
|
||||
if (it != this->texture_cache.end())
|
||||
return it->second;
|
||||
|
||||
std::cout << "ERROR: Couldn't load texture!" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TextureManager::draw(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FRect src, SDL_FRect dest, bool flipped)
|
||||
{
|
||||
SDL_RendererFlip flip = flipped ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
||||
SDL_RenderCopyEx(renderer, texture, &src, &dest, 0, NULL, flip);
|
||||
}
|
||||
SDL_FlipMode flip = flipped ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
||||
SDL_RenderTextureRotated(renderer, texture, &src, &dest, 0, NULL, flip);
|
||||
}
|
||||
|
||||
SDL_Texture* TextureManager::loadMapTileTexture(const char *path) {
|
||||
|
||||
//returns tile if it exists already
|
||||
if(mapTile_texture_cache.contains(std::string(path)))
|
||||
return mapTile_texture_cache.find(std::string(path))->second;
|
||||
|
||||
auto newTexture = IMG_LoadTexture(VEGO_Game().renderer, path);
|
||||
|
||||
if (newTexture == nullptr)
|
||||
throw std::runtime_error(std::string("Couldn't load texture '") + path + "'");
|
||||
|
||||
SDL_SetTextureScaleMode(newTexture, this->scaleMode); // linear scaling results in blurry images
|
||||
this->mapTile_texture_cache.emplace(std::string(path), newTexture);
|
||||
|
||||
return newTexture;
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
#include "Entity.h"
|
||||
#include "TransformComponent.h"
|
||||
#include "SpriteComponent.h"
|
||||
#include "TileComponent.h"
|
||||
|
||||
TileComponent::TileComponent(int x, int y, int w, int h, int id, const std::map<int, std::pair<std::string, bool>>* textureDict)
|
||||
|
||||
TileComponent::TileComponent(int x, int y, int w, int h, int id, const std::map<int, std::pair<Textures, bool>>* textureDict)
|
||||
{
|
||||
this->tileRect.x = x;
|
||||
this->tileRect.y = y;
|
||||
@ -22,8 +22,7 @@ TileComponent::TileComponent(int x, int y, int w, int h, int id, const std::map<
|
||||
}
|
||||
|
||||
this->collision = it->second.second;
|
||||
this->tileName = it->second.first;
|
||||
this->path = it->second.first.data();
|
||||
this->texture = it->second.first;
|
||||
}
|
||||
|
||||
void TileComponent::init()
|
||||
@ -31,7 +30,7 @@ void TileComponent::init()
|
||||
this->entity->addComponent<TransformComponent>(this->tileRect.x, this->tileRect.y, this->tileRect.w, this->tileRect.h, 1);
|
||||
this->transform = &entity->getComponent<TransformComponent>();
|
||||
|
||||
this->entity->addComponent<SpriteComponent>(this->path, 0);
|
||||
this->entity->addComponent<SpriteComponent>(this->texture, 0);
|
||||
this->sprite = &entity->getComponent<SpriteComponent>();
|
||||
}
|
||||
|
||||
|
||||
@ -9,26 +9,16 @@
|
||||
#include <cstdio>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
#include "SoundManager.h"
|
||||
|
||||
TransformComponent::TransformComponent()
|
||||
{
|
||||
position.zero();
|
||||
}
|
||||
|
||||
TransformComponent::TransformComponent(int scale)
|
||||
{
|
||||
position.zero();
|
||||
this->scale = scale;
|
||||
}
|
||||
|
||||
TransformComponent::TransformComponent(float x, float y)
|
||||
{
|
||||
this->position.x = x;
|
||||
this->position.y = y;
|
||||
}
|
||||
|
||||
TransformComponent::TransformComponent(float x, float y, int scale)
|
||||
{
|
||||
this->position.x = x;
|
||||
@ -50,12 +40,15 @@ void TransformComponent::init()
|
||||
direction.zero();
|
||||
}
|
||||
|
||||
void TransformComponent::update()
|
||||
void TransformComponent::update(uint_fast16_t diffTime)
|
||||
{
|
||||
direction.x = direction.x > 0 ? 1 : direction.x < 0 ? -1 : 0;
|
||||
direction.y = direction.y > 0 ? 1 : direction.y < 0 ? -1 : 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 * this->getSpeed() * multiplier,
|
||||
direction.y * this->getSpeed() * multiplier
|
||||
direction.x * this->getSpeed() * multiplier * diffTime * (1.f/1000),
|
||||
direction.y * this->getSpeed() * multiplier * diffTime * (1.f/1000)
|
||||
);
|
||||
|
||||
if (this->entity->hasGroup((size_t)Entity::GroupLabel::PLAYERS)){
|
||||
@ -65,9 +58,11 @@ void TransformComponent::update()
|
||||
position += positionChange;
|
||||
}
|
||||
|
||||
void TransformComponent::modifySpeed(int8_t modifier)
|
||||
{
|
||||
this->speedMod += modifier;
|
||||
int TransformComponent::getSpeed()
|
||||
{
|
||||
return (this->entity->hasComponent<DataComponent>()
|
||||
? this->entity->getComponent<DataComponent>().getEntry<int>("speed").value_or(0)
|
||||
: 0);
|
||||
}
|
||||
|
||||
void TransformComponent::setPositionAfterCollision(Vector2D& positionChange)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include "Vector2D.h"
|
||||
#include "SDL_rect.h"
|
||||
#include <SDL3/SDL_rect.h>
|
||||
|
||||
Vector2D::Vector2D()
|
||||
{
|
||||
|
||||
49
src/_Init.cpp
Normal file
49
src/_Init.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include <cstdint>
|
||||
#define SDL_MAIN_USE_CALLBACKS
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
#include "VEGO.h"
|
||||
#include "Entity.h"
|
||||
#include "GameInternal.h"
|
||||
#include "Constants.h"
|
||||
|
||||
GameInternal* vego::game = nullptr;
|
||||
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
srand(time(NULL));
|
||||
bool playing = true;
|
||||
|
||||
*appstate = vego::game = new GameInternal();
|
||||
|
||||
return vego::game->init();
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void *appstate) {
|
||||
if (!vego::game->isRunning()) {
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
|
||||
//vego::game->handleEvents(); // bad
|
||||
|
||||
Uint64 frameStart = SDL_GetTicks();
|
||||
|
||||
vego::game->update(frameStart);
|
||||
vego::game->render();
|
||||
|
||||
int frameTime = SDL_GetTicks() - frameStart;
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
// triggers upon every event
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
||||
return vego::game->handleEvent(event);
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
|
||||
vego::game->clean();
|
||||
}
|
||||
41
src/main.cpp
41
src/main.cpp
@ -1,41 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
#include "VEGO.h"
|
||||
#include "Entity.h"
|
||||
#include "GameInternal.h"
|
||||
#include "Constants.h"
|
||||
|
||||
GameInternal* vego::game = nullptr;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
srand(time(NULL));
|
||||
bool playing = true;
|
||||
|
||||
const int frameDelay = 1000 / FPS;
|
||||
|
||||
Uint32 frameStart;
|
||||
int frameTime;
|
||||
|
||||
vego::game = new GameInternal();
|
||||
|
||||
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();
|
||||
|
||||
vego::game->handleEvents();
|
||||
vego::game->update();
|
||||
vego::game->render();
|
||||
|
||||
frameTime = SDL_GetTicks() - frameStart;
|
||||
|
||||
if (frameDelay > frameTime) {
|
||||
SDL_Delay(frameDelay - frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
vego::game->clean();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user