diff options
Diffstat (limited to 'src/crepe/api')
56 files changed, 1554 insertions, 729 deletions
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index 464b0fd..45f67f6 100644 --- a/src/crepe/api/Animator.cpp +++ b/src/crepe/api/Animator.cpp @@ -14,11 +14,14 @@ Animator::Animator(game_object_id_t id, Sprite & ss, int row, int col, int col_a col(col) { dbg_trace(); - animator_rect = spritesheet.sprite_rect; - animator_rect.h /= col; - animator_rect.w /= row; - animator_rect.x = 0; - animator_rect.y = col_animator * animator_rect.h; + this->spritesheet.mask.h /= col; + this->spritesheet.mask.w /= row; + this->spritesheet.mask.x = 0; + this->spritesheet.mask.y = col_animator * this->spritesheet.mask.h; this->active = false; + + // need to do this for to get the aspect ratio for a single clipping in the spritesheet + this->spritesheet.aspect_ratio + = static_cast<double>(this->spritesheet.mask.w) / this->spritesheet.mask.h; } Animator::~Animator() { dbg_trace(); } diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h index 53f4b91..6c506aa 100644 --- a/src/crepe/api/Animator.h +++ b/src/crepe/api/Animator.h @@ -40,10 +40,6 @@ public: Animator(uint32_t id, Sprite & spritesheet, int row, int col, int col_animate); ~Animator(); // dbg_trace - Animator(const Animator &) = delete; - Animator(Animator &&) = delete; - Animator & operator=(const Animator &) = delete; - Animator & operator=(Animator &&) = delete; private: //! A reference to the Sprite sheet containing the animation frames. @@ -61,8 +57,6 @@ private: //! The current row being animated. int curr_row = 0; - Rect animator_rect; - //TODO: Is this necessary? //int fps; diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..e148367 --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,54 @@ +#include <filesystem> +#include <stdexcept> +#include <whereami.h> + +#include "api/Config.h" + +#include "Asset.h" + +using namespace crepe; +using namespace std; + +Asset::Asset(const string & src) : src(find_asset(src)) {} +Asset::Asset(const char * src) : src(find_asset(src)) {} + +const string & Asset::get_path() const noexcept { return this->src; } + +string Asset::find_asset(const string & src) const { + auto & cfg = Config::get_instance(); + string & root_pattern = cfg.asset.root_pattern; + + // if root_pattern is empty, find_asset must return all paths as-is + if (root_pattern.empty()) return src; + + // absolute paths do not need to be resolved, only canonicalized + filesystem::path path = src; + if (path.is_absolute()) return filesystem::canonical(path); + + // find directory matching root_pattern + filesystem::path root = this->whereami(); + while (1) { + if (filesystem::exists(root / root_pattern)) break; + if (!root.has_parent_path()) + throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); + root = root.parent_path(); + } + + // join path to root (base directory) and canonicalize + return filesystem::canonical(root / path); +} + +string Asset::whereami() const noexcept { + string path; + size_t path_length = wai_getExecutablePath(NULL, 0, NULL); + path.resize(path_length + 1); // wai writes null byte + wai_getExecutablePath(path.data(), path_length, NULL); + path.resize(path_length); + return path; +} + +bool Asset::operator==(const Asset & other) const noexcept { return this->src == other.src; } + +size_t std::hash<const Asset>::operator()(const Asset & asset) const noexcept { + return std::hash<string>{}(asset.get_path()); +}; diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..bfd0ac7 --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,84 @@ +#pragma once + +#include <string> + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate game asset files, and should *always* be used + * instead of reading file paths directly. + */ +class Asset { +public: + /** + * \param src Unique identifier to asset + */ + Asset(const std::string & src); + /** + * \param src Unique identifier to asset + */ + Asset(const char * src); + +public: + /** + * \brief Get the path to this asset + * \return path to this asset + */ + const std::string & get_path() const noexcept; + + /** + * \brief Comparison operator + * \param other Possibly different instance of \c Asset to test equality against + * \return True if \c this and \c other are equal + */ + bool operator==(const Asset & other) const noexcept; + +private: + //! path to asset + const std::string src; + +private: + /** + * \brief Locate asset path, or throw exception if it cannot be found + * + * This function resolves asset locations relative to crepe::Config::root_pattern if it is + * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized. + * This function only returns if the file can be found. + * + * \param src Arbitrary path to resource file + * + * \returns \p src if crepe::Config::root_pattern is empty + * \returns Canonical path to \p src + * + * \throws std::runtime_error if root_pattern cannot be found + * \throws std::filesystem::filesystem_error if the resolved path does not exist + * \throws std::filesystem::filesystem_error if the path cannot be canonicalized + */ + std::string find_asset(const std::string & src) const; + /** + * \returns The path to the current executable + */ + std::string whereami() const noexcept; +}; + +} // namespace crepe + +namespace std { + +//! Hash helper struct +template <> +struct hash<const crepe::Asset> { + /** + * \brief Hash operator for crepe::Asset + * + * This function hashes a crepe::Asset instance, allowing it to be used as a key in an \c + * std::unordered_map. + * + * \returns Hash value + */ + size_t operator()(const crepe::Asset & asset) const noexcept; +}; + +} // namespace std diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index 7bbace0..d22afdf 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -4,12 +4,12 @@ using namespace crepe; -BehaviorScript::BehaviorScript(game_object_id_t id, ComponentManager & mgr) +BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator) : Component(id), - component_manager(mgr) {} + mediator(mediator) {} template <> BehaviorScript & GameObject::add_component<BehaviorScript>() { ComponentManager & mgr = this->component_manager; - return mgr.add_component<BehaviorScript>(this->id, mgr); + return mgr.add_component<BehaviorScript>(this->id, mgr.mediator); } diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index f7d484a..3909b96 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -15,13 +15,21 @@ class Script; /** * \brief Script component * - * This class acts as a (component) wrapper around an instance of (a class - * derivatived from) \c Script. \c BehaviorScript is the only ECS component - * that stores member function implementations as data. + * This class acts as a (component) wrapper around an instance of (a class derivatived from) \c + * Script. \c BehaviorScript is the only ECS component that stores member function + * implementations as data. */ class BehaviorScript : public Component { protected: - BehaviorScript(game_object_id_t id, ComponentManager & component_manager); + /** + * \param id Parent \c GameObject id + * \param mediator Mediator reference + * + * \note Calls to this constructor (should) always pass through \c GameObject::add_component, + * which has an exception for this specific component type. This was done so the user does + * not have to pass references used within \c Script to each \c BehaviorScript instance. + */ + BehaviorScript(game_object_id_t id, Mediator & mediator); //! Only ComponentManager is allowed to instantiate BehaviorScript friend class ComponentManager; @@ -30,34 +38,33 @@ public: * \brief Set the concrete script of this component * * \tparam T Concrete script type (derived from \c crepe::Script) + * \tparam Args Arguments for concrete script constructor + * + * \param args Arguments for concrete script constructor (forwarded using perfect forwarding) * * \returns Reference to BehaviorScript component (`*this`) */ - template <class T> - BehaviorScript & set_script(); + template <class T, typename... Args> + BehaviorScript & set_script(Args &&... args); protected: - //! ScriptSystem needs direct access to the script instance - friend class ScriptSystem; - //! Flag to indicate if script->init() has been called already - bool initialized = false; //! Script instance std::unique_ptr<Script> script = nullptr; - //! Reference to component manager - ComponentManager & component_manager; - //! Script accesses the component manager directly via its parent - // (BehaviorScript) reference - friend class Script; + //! ScriptSystem needs direct access to the script instance + friend class ScriptSystem; + +protected: + //! Reference mediator + Mediator & mediator; }; /** * \brief Add a BehaviorScript component to this game object * - * The \c BehaviorScript class is the only exception to the ECS harmony, and - * requires a reference to the component manager passed to its constructor in - * order to function normally. This is because the \c BehaviorScript (and \c - * Script) classes are the only component-related classes that store - * implemented member functions as data. + * The \c BehaviorScript class is the only exception to the ECS harmony, and requires a + * reference to the component manager passed to its constructor in order to function normally. + * This is because the \c BehaviorScript (and \c Script) classes are the only component-related + * classes that store implemented member functions as data. */ template <> BehaviorScript & GameObject::add_component<BehaviorScript>(); diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index eec26da..b9bb1e2 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -9,14 +9,16 @@ namespace crepe { -template <class T> -BehaviorScript & BehaviorScript::set_script() { +template <class T, typename... Args> +BehaviorScript & BehaviorScript::set_script(Args &&... args) { dbg_trace(); static_assert(std::is_base_of<Script, T>::value); - Script * s = new T(); - s->parent_ref = this; - s->component_manager_ref = &this->component_manager; - this->script = std::unique_ptr<Script>(s); + this->script = std::unique_ptr<Script>(new T(std::forward<Args>(args)...)); + + this->script->game_object_id = this->game_object_id; + this->script->active = this->active; + this->script->mediator = this->mediator; + return *this; } diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp new file mode 100644 index 0000000..76f74f0 --- /dev/null +++ b/src/crepe/api/Button.cpp @@ -0,0 +1,11 @@ +#include "Button.h" + +namespace crepe { + +Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, + const std::function<void()> & on_click, bool is_toggle) + : UIObject(id, dimensions, offset), + is_toggle(is_toggle), + on_click(on_click) {} + +} // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h new file mode 100644 index 0000000..26e7526 --- /dev/null +++ b/src/crepe/api/Button.h @@ -0,0 +1,67 @@ +#pragma once + +#include <functional> + +#include "UIObject.h" + +namespace crepe { + +//! Represents a clickable UI button, derived from the UiObject class. +class Button : public UIObject { +public: + /** + * \brief Constructs a Button with the specified game object ID and dimensions. + * + * \param id The unique ID of the game object associated with this button. + * \param dimensions The width and height of the UIObject + * \param offset The offset relative this GameObjects Transform + * \param is_toggle Optional flag to indicate if the button is a toggle button. Defaults to false. + * \param on_click callback function that will be invoked when the button is clicked. + */ + Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, + const std::function<void()> & on_click, bool is_toggle = false); + + /** + * \brief Indicates if the button is a toggle button (can be pressed and released). + * + * A toggle button allows for a pressed/released state, whereas a regular button + * typically only has an on-click state. + */ + bool is_toggle = false; + // TODO: create separate toggle button class + /** + * \brief The callback function to be executed when the button is clicked. + * + * This function is invoked whenever the button is clicked. It can be set to any + * function that matches the signature `void()`. + */ + std::function<void()> on_click = nullptr; + + /** + * \brief Callback function to be executed when the mouse enters the button's boundaries. + * + * This function is triggered when the mouse cursor moves over the button, allowing + * custom actions like visual effects, highlighting, or sound effects. + */ + std::function<void()> on_mouse_enter = nullptr; + + /** + * \brief Callback function to be executed when the mouse exits the button's boundaries. + * + * This function is triggered when the mouse cursor moves out of the button's area, + * allowing custom actions like resetting visual effects or playing exit-related effects. + */ + std::function<void()> on_mouse_exit = nullptr; + +private: + //! friend relation for is_pressed and hover variables + friend class InputSystem; + //! Indicates whether the toggle button is pressed + bool is_pressed = false; + //! Indicates whether the mouse is currently hovering over the button + bool hover = false; + +public: +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index f9b370f..ac8f301 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -9,16 +9,19 @@ target_sources(crepe PUBLIC Texture.cpp AssetManager.cpp Sprite.cpp - SaveManager.cpp Config.cpp Metadata.cpp - Scene.cpp - SceneManager.cpp - Vector2.cpp Camera.cpp Animator.cpp + IKeyListener.cpp + IMouseListener.cpp LoopManager.cpp LoopTimer.cpp + Asset.cpp + EventHandler.cpp + Script.cpp + Button.cpp + UIObject.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -32,17 +35,23 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Rigidbody.h Sprite.h Vector2.h + Vector2.hpp Color.h Texture.h AssetManager.h AssetManager.hpp - SaveManager.h Scene.h Metadata.h - SceneManager.h - SceneManager.hpp Camera.h Animator.h + EventHandler.h + EventHandler.hpp + Event.h + IKeyListener.h + IMouseListener.h LoopManager.h LoopTimer.h + Asset.h + Button.h + UIObject.h ) diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp index 5835bdd..39d8ab0 100644 --- a/src/crepe/api/Camera.cpp +++ b/src/crepe/api/Camera.cpp @@ -1,3 +1,4 @@ +#include "types.h" #include "util/Log.h" #include "Camera.h" @@ -6,9 +7,14 @@ using namespace crepe; -Camera::Camera(game_object_id_t id, const Color & bg_color) +Camera::Camera(game_object_id_t id, const Color & bg_color, const ivec2 & screen, + const vec2 & viewport_size, const double & zoom, const vec2 & offset) : Component(id), - bg_color(bg_color) { + bg_color(bg_color), + offset(offset), + screen(screen), + viewport_size(viewport_size), + zoom(zoom) { dbg_trace(); } diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h index e0cda34..2d8fa48 100644 --- a/src/crepe/api/Camera.h +++ b/src/crepe/api/Camera.h @@ -2,6 +2,7 @@ #include "Color.h" #include "Component.h" +#include "types.h" namespace crepe { @@ -20,33 +21,31 @@ public: * \param id Unique identifier for the camera component. * \param bg_color Background color for the camera view. */ - Camera(game_object_id_t id, const Color & bg_color); + Camera(game_object_id_t id, const Color & bg_color, const ivec2 & screen, + const vec2 & viewport_size, const double & zoom, const vec2 & offset = {0, 0}); ~Camera(); // dbg_trace only public: //! Background color of the camera view. - Color bg_color; + const Color bg_color; - //! Aspect ratio height for the camera. - double aspect_height = 480; + //! offset postion from the game object transform component + vec2 offset; - //! Aspect ratio width for the camera. - double aspect_width = 640; + //! screen the display size in pixels ( output resolution ) + const ivec2 screen; - //! X-coordinate of the camera position. - double x = 0.0; - - //! Y-coordinate of the camera position. - double y = 0.0; + //! viewport is the area of the world visible through the camera (in world units) + const vec2 viewport_size; //! Zoom level of the camera view. - double zoom = 1.0; + const double zoom; public: /** * \brief Gets the maximum number of camera instances allowed. * \return Maximum instance count as an integer. */ - virtual int get_instances_max() const { return 10; } + virtual int get_instances_max() const { return 1; } }; } // namespace crepe diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp index 9e5f187..29bd77a 100644 --- a/src/crepe/api/Color.cpp +++ b/src/crepe/api/Color.cpp @@ -2,32 +2,11 @@ using namespace crepe; -Color Color::white = Color(255, 255, 255, 0); -Color Color::red = Color(255, 0, 0, 0); -Color Color::green = Color(0, 255, 0, 0); -Color Color::blue = Color(0, 0, 255, 0); -Color Color::black = Color(0, 0, 0, 0); -Color Color::cyan = Color(0, 255, 255, 0); -Color Color::yellow = Color(255, 255, 0, 0); -Color Color::magenta = Color(255, 0, 255, 0); - -Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { - this->a = alpha; - this->r = red; - this->g = green; - this->b = blue; -}; - -const Color & Color::get_white() { return Color::white; }; - -const Color & Color::get_red() { return Color::red; }; -const Color & Color::get_green() { return Color::green; }; -const Color & Color::get_blue() { return Color::blue; }; - -const Color & Color::get_black() { return Color::black; }; - -const Color & Color::get_cyan() { return Color::cyan; }; - -const Color & Color::get_yellow() { return Color::yellow; }; - -const Color & Color::get_magenta() { return Color::magenta; }; +const Color Color::WHITE{0xff, 0xff, 0xff}; +const Color Color::RED{0xff, 0x00, 0x00}; +const Color Color::GREEN{0x00, 0xff, 0x00}; +const Color Color::BLUE{0x00, 0x00, 0xff}; +const Color Color::BLACK{0x00, 0x00, 0x00}; +const Color Color::CYAN{0x00, 0xff, 0xff}; +const Color Color::YELLOW{0xff, 0xff, 0x00}; +const Color Color::MAGENTA{0xff, 0x00, 0xff}; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h index aa47bf4..84edb5c 100644 --- a/src/crepe/api/Color.h +++ b/src/crepe/api/Color.h @@ -4,41 +4,20 @@ namespace crepe { -// TODO: make Color a struct w/o constructors/destructors -class Color { - - // FIXME: can't these colors be defined as a `static constexpr const Color` - // instead? - -public: - Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); - static const Color & get_white(); - static const Color & get_red(); - static const Color & get_green(); - static const Color & get_blue(); - static const Color & get_cyan(); - static const Color & get_magenta(); - static const Color & get_yellow(); - static const Color & get_black(); - -private: - // TODO: why are these private!? - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - - static Color white; - static Color red; - static Color green; - static Color blue; - static Color cyan; - static Color magenta; - static Color yellow; - static Color black; - -private: - friend class SDLContext; +struct Color { + uint8_t r = 0x00; + uint8_t g = 0x00; + uint8_t b = 0x00; + uint8_t a = 0xff; + + static const Color WHITE; + static const Color RED; + static const Color GREEN; + static const Color BLUE; + static const Color CYAN; + static const Color MAGENTA; + static const Color YELLOW; + static const Color BLACK; }; } // namespace crepe diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 3ab877a..225e9b9 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -1,6 +1,8 @@ #pragma once #include "../util/Log.h" +#include "types.h" +#include <string> namespace crepe { @@ -11,19 +13,18 @@ namespace crepe { * modified *before* execution is handed over from the game programmer to the engine (i.e. the * main loop is started). */ -class Config { +class Config final { public: //! Retrieve handle to global Config instance static Config & get_instance(); private: Config() = default; - - // singleton - Config(const Config &) = delete; - Config(Config &&) = delete; - Config & operator=(const Config &) = delete; - Config & operator=(Config &&) = delete; + ~Config() = default; + Config(const Config &) = default; + Config(Config &&) = default; + Config & operator=(const Config &) = default; + Config & operator=(Config &&) = default; public: //! Logging-related settings @@ -62,6 +63,28 @@ public: */ double gravity = 1; } physics; + + //! default window settings + struct { + //TODO make this constexpr because this will never change + ivec2 default_size = {1080, 720}; + std::string window_title = "Jetpack joyride clone"; + + } window_settings; + + //! Asset loading options + struct { + /** + * \brief Pattern to match for Asset base directory + * + * All non-absolute paths resolved using \c Asset will be made relative to + * the first parent directory relative to the calling executable where + * appending this pattern results in a path that exists. If this string is + * empty, path resolution is disabled, and Asset will return all paths + * as-is. + */ + std::string root_pattern = ".crepe-root"; + } asset; }; } // namespace crepe diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h new file mode 100644 index 0000000..6298118 --- /dev/null +++ b/src/crepe/api/Event.h @@ -0,0 +1,134 @@ +#pragma once +// TODO discussing the location of these events + +#include <string> + +#include "KeyCodes.h" + +namespace crepe { + +/** + * \brief Base class for all event types in the system. + */ +class Event {}; + +/** + * \brief Event triggered when a key is pressed. + */ +class KeyPressEvent : public Event { +public: + //! false if first time press, true if key is repeated + bool repeat = false; + + //! The key that was pressed. + Keycode key = Keycode::NONE; +}; + +/** + * \brief Event triggered when a key is released. + */ +class KeyReleaseEvent : public Event { +public: + //! The key that was released. + Keycode key = Keycode::NONE; +}; + +/** + * \brief Event triggered when a mouse button is pressed. + */ +class MousePressEvent : public Event { +public: + //! X-coordinate of the mouse position at the time of the event. + int mouse_x = 0; + + //! Y-coordinate of the mouse position at the time of the event. + int mouse_y = 0; + + //! The mouse button that was pressed. + MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when a mouse button is clicked (press and release). + */ +class MouseClickEvent : public Event { +public: + //! X-coordinate of the mouse position at the time of the event. + int mouse_x = 0; + + //! Y-coordinate of the mouse position at the time of the event. + int mouse_y = 0; + + //! The mouse button that was clicked. + MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when a mouse button is released. + */ +class MouseReleaseEvent : public Event { +public: + //! X-coordinate of the mouse position at the time of the event. + int mouse_x = 0; + + //! Y-coordinate of the mouse position at the time of the event. + int mouse_y = 0; + + //! The mouse button that was released. + MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when the mouse is moved. + */ +class MouseMoveEvent : public Event { +public: + //! X-coordinate of the mouse position at the time of the event. + int mouse_x = 0; + + //! Y-coordinate of the mouse position at the time of the event. + int mouse_y = 0; + + // Movement since last event in x + int delta_x = 0; + + // Movement since last event in y + int delta_y = 0; +}; + +/** + * \brief Event triggered when the mouse is moved. + */ +class MouseScrollEvent : public Event { +public: + //! X-coordinate of the mouse position at the time of the event. + int mouse_x = 0; + + //! Y-coordinate of the mouse position at the time of the event. + int mouse_y = 0; + + //! scroll direction (-1 = down, 1 = up) + int scroll_direction = 0; + //! scroll amount in y axis (from and away from the person). + float scroll_delta = 0; +}; +/** + * \brief Event triggered during a collision between objects. + */ +class CollisionEvent : public Event {}; + +/** + * \brief Event triggered when text is submitted, e.g., from a text input. + */ +class TextSubmitEvent : public Event { +public: + //! The submitted text. + std::string text = ""; +}; + +/** + * \brief Event triggered to indicate the application is shutting down. + */ +class ShutDownEvent : public Event {}; + +} // namespace crepe diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp new file mode 100644 index 0000000..4dc232f --- /dev/null +++ b/src/crepe/api/EventHandler.cpp @@ -0,0 +1,5 @@ +#include "EventHandler.h" + +using namespace crepe; + +bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); } diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h new file mode 100644 index 0000000..7bdd9a3 --- /dev/null +++ b/src/crepe/api/EventHandler.h @@ -0,0 +1,96 @@ +#pragma once + +#include <functional> +#include <string> + +#include "Event.h" + +namespace crepe { +/** + * \brief A type alias for an event handler function. + * + * The EventHandler is a std::function that takes an EventType reference and returns a boolean value + * indicating whether the event is handled. + * + * \tparam EventType The type of event this handler will handle. + * + * Returning \c false from an event handler results in the event being propogated to other listeners for the same event type, while returning \c true stops propogation altogether. + */ +template <typename EventType> +using EventHandler = std::function<bool(const EventType & e)>; + +/** + * \class IEventHandlerWrapper + * \brief An abstract base class for event handler wrappers. + * + * This class provides the interface for handling events. Derived classes must implement the + * `call()` method to process events + */ +class IEventHandlerWrapper { +public: + /** + * \brief Virtual destructor for IEventHandlerWrapper. + */ + virtual ~IEventHandlerWrapper() = default; + + /** + * \brief Executes the handler with the given event. + * + * This method calls the `call()` method of the derived class, passing the event to the handler. + * + * \param e The event to be processed. + * \return A boolean value indicating whether the event is handled. + */ + bool exec(const Event & e); + +private: + /** + * \brief The method responsible for handling the event. + * + * This method is implemented by derived classes to process the event. + * + * \param e The event to be processed. + * \return A boolean value indicating whether the event is handled. + */ + virtual bool call(const Event & e) = 0; +}; + +/** + * \class EventHandlerWrapper + * \brief A wrapper for event handler functions. + * + * This class wraps an event handler function of a specific event type. It implements the + * `call()` and `get_type()` methods to allow the handler to be executed and its type to be + * queried. + * + * \tparam EventType The type of event this handler will handle. + */ +template <typename EventType> +class EventHandlerWrapper : public IEventHandlerWrapper { +public: + /** + * \brief Constructs an EventHandlerWrapper with a given handler. + * + * The constructor takes an event handler function and stores it in the wrapper. + * + * \param handler The event handler function. + */ + explicit EventHandlerWrapper(const EventHandler<EventType> & handler); + +private: + /** + * \brief Calls the stored event handler with the event. + * + * This method casts the event to the appropriate type and calls the handler. + * + * \param e The event to be handled. + * \return A boolean value indicating whether the event is handled. + */ + bool call(const Event & e) override; + //! The event handler function. + EventHandler<EventType> handler; +}; + +} // namespace crepe + +#include "EventHandler.hpp" diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp new file mode 100644 index 0000000..050e57e --- /dev/null +++ b/src/crepe/api/EventHandler.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <typeindex> + +#include "EventHandler.h" + +namespace crepe { + +// Implementation of EventHandlerWrapper constructor +template <typename EventType> +EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler) + : handler(handler) {} + +// Implementation of EventHandlerWrapper::call +template <typename EventType> +bool EventHandlerWrapper<EventType>::call(const Event & e) { + return this->handler(static_cast<const EventType &>(e)); +} + +} //namespace crepe diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index 287e81d..9ef4682 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -9,7 +9,7 @@ using namespace std; GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id, const std::string & name, const std::string & tag, - const Vector2 & position, double rotation, double scale) + const vec2 & position, double rotation, double scale) : id(id), component_manager(component_manager) { @@ -23,12 +23,16 @@ void GameObject::set_parent(const GameObject & parent) { ComponentManager & mgr = this->component_manager; // Set parent on own Metadata component - vector<reference_wrapper<Metadata>> this_metadata - = mgr.get_components_by_id<Metadata>(this->id); + RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id); this_metadata.at(0).get().parent = parent.id; // Add own id to children list of parent's Metadata component - vector<reference_wrapper<Metadata>> parent_metadata - = mgr.get_components_by_id<Metadata>(parent.id); + RefVector<Metadata> parent_metadata = mgr.get_components_by_id<Metadata>(parent.id); parent_metadata.at(0).get().children.push_back(this->id); } + +void GameObject::set_persistent(bool persistent) { + ComponentManager & mgr = this->component_manager; + + mgr.set_persistent(this->id, persistent); +} diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index 34ef8bb..4cd2bc0 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -2,7 +2,6 @@ #include <string> -#include "Vector2.h" #include "types.h" namespace crepe { @@ -30,7 +29,7 @@ private: * \param scale The scale of the GameObject */ GameObject(ComponentManager & component_manager, game_object_id_t id, - const std::string & name, const std::string & tag, const Vector2 & position, + const std::string & name, const std::string & tag, const vec2 & position, double rotation, double scale); //! ComponentManager instances GameObject friend class ComponentManager; @@ -59,6 +58,15 @@ public: */ template <typename T, typename... Args> T & add_component(Args &&... args); + /** + * \brief Components will not be deleted if this method is called + * + * This method sets the persistent flag of the GameObject to true. If the persistent + * flag is set to true, the GameObject will not be deleted when the scene is changed. + * + * \param persistent The persistent flag + */ + void set_persistent(bool persistent = true); public: //! The id of the GameObject diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp index 17b17d7..a6b45b0 100644 --- a/src/crepe/api/GameObject.hpp +++ b/src/crepe/api/GameObject.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../ComponentManager.h" +#include "../manager/ComponentManager.h" #include "GameObject.h" diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp new file mode 100644 index 0000000..8642655 --- /dev/null +++ b/src/crepe/api/IKeyListener.cpp @@ -0,0 +1,19 @@ +#include "IKeyListener.h" + +using namespace crepe; + +// Constructor with specified channel +IKeyListener::IKeyListener(event_channel_t channel) + : event_manager(EventManager::get_instance()) { + this->press_id = event_manager.subscribe<KeyPressEvent>( + [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); + this->release_id = event_manager.subscribe<KeyReleaseEvent>( + [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, + channel); +} + +// Destructor, unsubscribe events +IKeyListener::~IKeyListener() { + event_manager.unsubscribe(this->press_id); + event_manager.unsubscribe(this->release_id); +} diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h new file mode 100644 index 0000000..180a0a6 --- /dev/null +++ b/src/crepe/api/IKeyListener.h @@ -0,0 +1,50 @@ +#pragma once + +#include "../manager/EventManager.h" + +#include "Event.h" +#include "EventHandler.h" + +namespace crepe { + +/** + * \class IKeyListener + * \brief Interface for keyboard event handling in the application. + */ +class IKeyListener { +public: + /** + * \brief Constructs an IKeyListener with a specified channel. + * \param channel The channel ID for event handling. + */ + IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL); + virtual ~IKeyListener(); + IKeyListener(const IKeyListener &) = delete; + IKeyListener & operator=(const IKeyListener &) = delete; + IKeyListener & operator=(IKeyListener &&) = delete; + IKeyListener(IKeyListener &&) = delete; + + /** + * \brief Pure virtual function to handle key press events. + * \param event The key press event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_key_pressed(const KeyPressEvent & event) = 0; + + /** + * \brief Pure virtual function to handle key release events. + * \param event The key release event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_key_released(const KeyReleaseEvent & event) = 0; + +private: + //! Key press event id + subscription_t press_id = -1; + //! Key release event id + subscription_t release_id = -1; + //! EventManager reference + EventManager & event_manager; +}; + +} // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp new file mode 100644 index 0000000..989aeb3 --- /dev/null +++ b/src/crepe/api/IMouseListener.cpp @@ -0,0 +1,29 @@ +#include "IMouseListener.h" + +using namespace crepe; + +IMouseListener::IMouseListener(event_channel_t channel) + : event_manager(EventManager::get_instance()) { + this->click_id = event_manager.subscribe<MouseClickEvent>( + [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, + channel); + + this->press_id = event_manager.subscribe<MousePressEvent>( + [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, + channel); + + this->release_id = event_manager.subscribe<MouseReleaseEvent>( + [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, + channel); + + this->move_id = event_manager.subscribe<MouseMoveEvent>( + [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel); +} + +IMouseListener::~IMouseListener() { + // Unsubscribe event handlers + event_manager.unsubscribe(this->click_id); + event_manager.unsubscribe(this->press_id); + event_manager.unsubscribe(this->release_id); + event_manager.unsubscribe(this->move_id); +} diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h new file mode 100644 index 0000000..e19897d --- /dev/null +++ b/src/crepe/api/IMouseListener.h @@ -0,0 +1,73 @@ +#pragma once + +#include "../manager/EventManager.h" + +#include "Event.h" +#include "EventHandler.h" + +namespace crepe { + +/** + * \class IMouseListener + * \brief Interface for mouse event handling in the application. + */ +class IMouseListener { +public: + /** + * \brief Constructs an IMouseListener with a specified channel. + * \param channel The channel ID for event handling. + */ + IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL); + virtual ~IMouseListener(); + IMouseListener & operator=(const IMouseListener &) = delete; + IMouseListener(const IMouseListener &) = delete; + IMouseListener & operator=(const IMouseListener &&) = delete; + IMouseListener(IMouseListener &&) = delete; + + /** + * \brief Move assignment operator (deleted). + */ + IMouseListener & operator=(IMouseListener &&) = delete; + + /** + * \brief Handles a mouse click event. + * \param event The mouse click event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0; + + /** + * \brief Handles a mouse press event. + * \param event The mouse press event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_pressed(const MousePressEvent & event) = 0; + + /** + * \brief Handles a mouse release event. + * \param event The mouse release event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0; + + /** + * \brief Handles a mouse move event. + * \param event The mouse move event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; + +private: + //! Mouse click event id + subscription_t click_id = -1; + //! Mouse press event id + subscription_t press_id = -1; + //! Mouse release event id + subscription_t release_id = -1; + //! Mouse move event id + subscription_t move_id = -1; + //! EventManager reference + EventManager & event_manager; +}; + +} //namespace crepe diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h new file mode 100644 index 0000000..fcfc080 --- /dev/null +++ b/src/crepe/api/KeyCodes.h @@ -0,0 +1,154 @@ +#pragma once +namespace crepe { +//! Enumeration for mouse button inputs, including standard and extended buttons. +enum class MouseButton { + NONE = 0, //!< No mouse button input. + LEFT_MOUSE = 1, //!< Left mouse button. + RIGHT_MOUSE = 2, //!< Right mouse button. + MIDDLE_MOUSE = 3, //!< Middle mouse button (scroll wheel press). + X1_MOUSE = 4, //!< First extended mouse button. + X2_MOUSE = 5, //!< Second extended mouse button. + SCROLL_UP = 6, //!< Scroll wheel upward movement. + SCROLL_DOWN = 7, //!< Scroll wheel downward movement. +}; + +//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. +enum class Keycode { + NONE = 0, //!< No key input. + SPACE = 32, //!< Spacebar. + APOSTROPHE = 39, //!< Apostrophe ('). + COMMA = 44, //!< Comma (,). + MINUS = 45, //!< Minus (-). + PERIOD = 46, //!< Period (.). + SLASH = 47, //!< Slash (/). + D0 = 48, //!< Digit 0. + D1 = 49, //!< Digit 1. + D2 = 50, //!< Digit 2. + D3 = 51, //!< Digit 3. + D4 = 52, //!< Digit 4. + D5 = 53, //!< Digit 5. + D6 = 54, //!< Digit 6. + D7 = 55, //!< Digit 7. + D8 = 56, //!< Digit 8. + D9 = 57, //!< Digit 9. + SEMICOLON = 59, //!< Semicolon (;). + EQUAL = 61, //!< Equal sign (=). + A = 65, //!< Key 'A'. + B = 66, //!< Key 'B'. + C = 67, //!< Key 'C'. + D = 68, //!< Key 'D'. + E = 69, //!< Key 'E'. + F = 70, //!< Key 'F'. + G = 71, //!< Key 'G'. + H = 72, //!< Key 'H'. + I = 73, //!< Key 'I'. + J = 74, //!< Key 'J'. + K = 75, //!< Key 'K'. + L = 76, //!< Key 'L'. + M = 77, //!< Key 'M'. + N = 78, //!< Key 'N'. + O = 79, //!< Key 'O'. + P = 80, //!< Key 'P'. + Q = 81, //!< Key 'Q'. + R = 82, //!< Key 'R'. + S = 83, //!< Key 'S'. + T = 84, //!< Key 'T'. + U = 85, //!< Key 'U'. + V = 86, //!< Key 'V'. + W = 87, //!< Key 'W'. + X = 88, //!< Key 'X'. + Y = 89, //!< Key 'Y'. + Z = 90, //!< Key 'Z'. + LEFT_BRACKET = 91, //!< Left bracket ([). + BACKSLASH = 92, //!< Backslash (\). + RIGHT_BRACKET = 93, //!< Right bracket (]). + GRAVE_ACCENT = 96, //!< Grave accent (`). + WORLD1 = 161, //!< Non-US key #1. + WORLD2 = 162, //!< Non-US key #2. + ESCAPE = 256, //!< Escape key. + ENTER = 257, //!< Enter key. + TAB = 258, //!< Tab key. + BACKSPACE = 259, //!< Backspace key. + INSERT = 260, //!< Insert key. + DELETE = 261, //!< Delete key. + RIGHT = 262, //!< Right arrow key. + LEFT = 263, //!< Left arrow key. + DOWN = 264, //!< Down arrow key. + UP = 265, //!< Up arrow key. + PAGE_UP = 266, //!< Page Up key. + PAGE_DOWN = 267, //!< Page Down key. + HOME = 268, //!< Home key. + END = 269, //!< End key. + CAPS_LOCK = 280, //!< Caps Lock key. + SCROLL_LOCK = 281, //!< Scroll Lock key. + NUM_LOCK = 282, //!< Num Lock key. + PRINT_SCREEN = 283, //!< Print Screen key. + PAUSE = 284, //!< Pause key. + /** + * \name Function keys (F1-F25). + * \{ + */ + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + /// \} + /** + * \name Keypad digits and operators. + * \{ + */ + KP0 = 320, + KP1 = 321, + KP2 = 322, + KP3 = 323, + KP4 = 324, + KP5 = 325, + KP6 = 326, + KP7 = 327, + KP8 = 328, + KP9 = 329, + KP_DECIMAL = 330, + KP_DIVIDE = 331, + KP_MULTIPLY = 332, + KP_SUBTRACT = 333, + KP_ADD = 334, + KP_ENTER = 335, + KP_EQUAL = 336, + /// \} + /** + * \name Modifier keys. + * \{ + */ + LEFT_SHIFT = 340, + LEFT_CONTROL = 341, + LEFT_ALT = 342, + LEFT_SUPER = 343, + RIGHT_SHIFT = 344, + RIGHT_CONTROL = 345, + RIGHT_ALT = 346, + RIGHT_SUPER = 347, + /// \} + MENU = 348, //!< Menu key. +}; +} // namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 6303e2b..8fef0c4 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,14 +1,12 @@ -#include "../facade/SDLContext.h" - #include "../system/AnimatorSystem.h" #include "../system/CollisionSystem.h" +#include "../system/InputSystem.h" #include "../system/ParticleSystem.h" #include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" #include "LoopManager.h" -#include "LoopTimer.h" using namespace crepe; using namespace std; @@ -20,13 +18,10 @@ LoopManager::LoopManager() { this->load_system<PhysicsSystem>(); this->load_system<RenderSystem>(); this->load_system<ScriptSystem>(); + this->load_system<InputSystem>(); } -ComponentManager & LoopManager::get_component_manager() { return this->component_manager; } - -void LoopManager::process_input() { - SDLContext::get_instance().handle_events(this->game_running); -} +void LoopManager::process_input() { this->get_system<InputSystem>().update(); } void LoopManager::start() { this->setup(); @@ -37,7 +32,7 @@ void LoopManager::set_running(bool running) { this->game_running = running; } void LoopManager::fixed_update() {} void LoopManager::loop() { - LoopTimer & timer = LoopTimer::get_instance(); + LoopTimer & timer = this->loop_timer; timer.start(); while (game_running) { @@ -57,15 +52,17 @@ void LoopManager::loop() { } void LoopManager::setup() { + LoopTimer & timer = this->loop_timer; + this->game_running = true; - LoopTimer::get_instance().start(); - LoopTimer::get_instance().set_fps(60); + timer.start(); + timer.set_fps(200); } void LoopManager::render() { - if (this->game_running) { - this->get_system<RenderSystem>().update(); - } + if (!this->game_running) return; + + this->get_system<RenderSystem>().update(); } -void LoopManager::update() { LoopTimer & timer = LoopTimer::get_instance(); } +void LoopManager::update() {} diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 288dca2..d8910a0 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -2,16 +2,33 @@ #include <memory> -#include "../ComponentManager.h" +#include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h" +#include "../manager/SceneManager.h" #include "../system/System.h" +#include "LoopTimer.h" + namespace crepe { +/** + * \brief Main game loop manager + * + * This class is responsible for managing the game loop, including initialization and updating. + */ class LoopManager { public: void start(); LoopManager(); + /** + * \brief Add a new concrete scene to the scene manager + * + * \tparam T Type of concrete scene + */ + template <typename T> + void add_scene(); + private: /** * \brief Setup function for one-time initialization. @@ -53,12 +70,14 @@ private: * This function updates physics and game logic based on LoopTimer's fixed_delta_time. */ void fixed_update(); + /** * \brief Set game running variable * * \param running running (false = game shutdown, true = game running) */ void set_running(bool running); + /** * \brief Function for executing render-related systems. * @@ -68,18 +87,42 @@ private: bool game_running = false; -protected: - ComponentManager & get_component_manager(); - template <class T> - T & get_system(); - private: - ComponentManager component_manager{}; - std::unordered_map<std::type_index, std::unique_ptr<System>> systems; + //! Global context + Mediator mediator; + + //! Component manager instance + ComponentManager component_manager{mediator}; + //! Scene manager instance + SceneManager scene_manager{mediator}; + + //! SDL context \todo no more singletons! + SDLContext & sdl_context = SDLContext::get_instance(); + //! Loop timer \todo no more singletons! + LoopTimer & loop_timer = LoopTimer::get_instance(); private: + /** + * \brief Collection of System instances + * + * This map holds System instances indexed by the system's class typeid. It is filled in the + * constructor of \c LoopManager using LoopManager::load_system. + */ + std::unordered_map<std::type_index, std::unique_ptr<System>> systems; + /** + * \brief Initialize a system + * \tparam T System type (must be derivative of \c System) + */ template <class T> void load_system(); + /** + * \brief Retrieve a reference to ECS system + * \tparam T System type + * \returns Reference to system instance + * \throws std::runtime_error if the System is not initialized + */ + template <class T> + T & get_system(); }; } // namespace crepe diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp index 0b14fdb..266758a 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/api/LoopManager.hpp @@ -11,6 +11,11 @@ namespace crepe { template <class T> +void LoopManager::add_scene() { + this->scene_manager.add_scene<T>(); +} + +template <class T> T & LoopManager::get_system() { using namespace std; static_assert(is_base_of<System, T>::value, @@ -33,8 +38,11 @@ void LoopManager::load_system() { static_assert(is_base_of<System, T>::value, "load_system must recieve a derivative class of System"); - System * system = new T(this->component_manager); - this->systems[typeid(T)] = unique_ptr<System>(system); + const type_info & type = typeid(T); + if (this->systems.contains(type)) + throw runtime_error(format("LoopManager: {} is already initialized", type.name())); + System * system = new T(this->mediator); + this->systems[type] = unique_ptr<System>(system); } } // namespace crepe diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp index a9800b7..15a0e3a 100644 --- a/src/crepe/api/LoopTimer.cpp +++ b/src/crepe/api/LoopTimer.cpp @@ -47,7 +47,7 @@ double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.c void LoopTimer::set_fps(int fps) { this->fps = fps; // target time per frame in seconds - this->frame_target_time = std::chrono::seconds(1) / fps; + this->frame_target_time = std::chrono::duration<double>(1.0) / fps; } int LoopTimer::get_fps() const { return this->fps; } diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index f277d7b..9393439 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -130,9 +130,9 @@ private: //! Delta time for the current frame in seconds std::chrono::duration<double> delta_time{0.0}; //! Target time per frame in seconds - std::chrono::duration<double> frame_target_time = std::chrono::seconds(1) / fps; + std::chrono::duration<double> frame_target_time = std::chrono::duration<double>(1.0) / fps; //! Fixed delta time for fixed updates in seconds - std::chrono::duration<double> fixed_delta_time = std::chrono::seconds(1) / 50; + std::chrono::duration<double> fixed_delta_time = std::chrono::duration<double>(1.0) / 50.0; //! Total elapsed game time in seconds std::chrono::duration<double> elapsed_time{0.0}; //! Total elapsed time for fixed updates in seconds diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index 33112e1..b83fd61 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -4,7 +4,7 @@ #include "Component.h" #include "Particle.h" -#include "Vector2.h" +#include "types.h" namespace crepe { @@ -30,7 +30,7 @@ public: //! boundary height (midpoint is emitter location) double height = 0.0; //! boundary offset from particle emitter location - Vector2 offset; + vec2 offset; //! reset on exit or stop velocity and set max postion bool reset_on_exit = false; }; @@ -43,7 +43,7 @@ public: */ struct Data { //! position of the emitter - Vector2 position; + vec2 position; //! maximum number of particles const unsigned int max_particles = 0; //! rate of particle emission per update (Lowest value = 0.001 any lower is ignored) @@ -61,7 +61,7 @@ public: //! end Lifespan of particle double end_lifespan = 0.0; //! force over time (physics) - Vector2 force_over_time; + vec2 force_over_time; //! particle boundary Boundary boundary; //! collection of particles diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp index 6b87695..576ca45 100644 --- a/src/crepe/api/Rigidbody.cpp +++ b/src/crepe/api/Rigidbody.cpp @@ -6,7 +6,7 @@ crepe::Rigidbody::Rigidbody(game_object_id_t id, const Data & data) : Component(id), data(data) {} -void crepe::Rigidbody::add_force_linear(const Vector2 & force) { +void crepe::Rigidbody::add_force_linear(const vec2 & force) { this->data.linear_velocity += force; } diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 3e5c7a3..3b0588f 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -2,7 +2,7 @@ #include "../Component.h" -#include "Vector2.h" +#include "types.h" namespace crepe { @@ -56,11 +56,11 @@ public: //! Changes if physics apply BodyType body_type = BodyType::DYNAMIC; //! linear velocity of object - Vector2 linear_velocity; + vec2 linear_velocity; //! maximum linear velocity of object - Vector2 max_linear_velocity; + vec2 max_linear_velocity; //! linear damping of object - Vector2 linear_damping; + vec2 linear_damping; //! angular velocity of object double angular_velocity = 0.0; //! max angular velocity of object @@ -90,7 +90,7 @@ public: * * \param force Vector2 that is added to the linear force. */ - void add_force_linear(const Vector2 & force); + void add_force_linear(const vec2 & force); /** * \brief add a angular force to the Rigidbody. * diff --git a/src/crepe/api/SaveManager.cpp b/src/crepe/api/SaveManager.cpp deleted file mode 100644 index c5f43ea..0000000 --- a/src/crepe/api/SaveManager.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "../facade/DB.h" -#include "../util/Log.h" - -#include "Config.h" -#include "SaveManager.h" -#include "ValueBroker.h" - -using namespace std; -using namespace crepe; - -template <> -string SaveManager::serialize(const string & value) const noexcept { - return value; -} -template <typename T> -string SaveManager::serialize(const T & value) const noexcept { - return to_string(value); -} -template string SaveManager::serialize(const uint8_t &) const noexcept; -template string SaveManager::serialize(const int8_t &) const noexcept; -template string SaveManager::serialize(const uint16_t &) const noexcept; -template string SaveManager::serialize(const int16_t &) const noexcept; -template string SaveManager::serialize(const uint32_t &) const noexcept; -template string SaveManager::serialize(const int32_t &) const noexcept; -template string SaveManager::serialize(const uint64_t &) const noexcept; -template string SaveManager::serialize(const int64_t &) const noexcept; -template string SaveManager::serialize(const float &) const noexcept; -template string SaveManager::serialize(const double &) const noexcept; - -template <> -uint64_t SaveManager::deserialize(const string & value) const noexcept { - try { - return stoul(value); - } catch (std::invalid_argument &) { - return 0; - } -} -template <> -int64_t SaveManager::deserialize(const string & value) const noexcept { - try { - return stol(value); - } catch (std::invalid_argument &) { - return 0; - } -} -template <> -float SaveManager::deserialize(const string & value) const noexcept { - try { - return stof(value); - } catch (std::invalid_argument &) { - return 0; - } - return stof(value); -} -template <> -double SaveManager::deserialize(const string & value) const noexcept { - try { - return stod(value); - } catch (std::invalid_argument &) { - return 0; - } -} -template <> -string SaveManager::deserialize(const string & value) const noexcept { - return value; -} - -template <> -uint8_t SaveManager::deserialize(const string & value) const noexcept { - return deserialize<uint64_t>(value); -} -template <> -int8_t SaveManager::deserialize(const string & value) const noexcept { - return deserialize<int64_t>(value); -} -template <> -uint16_t SaveManager::deserialize(const string & value) const noexcept { - return deserialize<uint64_t>(value); -} -template <> -int16_t SaveManager::deserialize(const string & value) const noexcept { - return deserialize<int64_t>(value); -} -template <> -uint32_t SaveManager::deserialize(const string & value) const noexcept { - return deserialize<uint64_t>(value); -} -template <> -int32_t SaveManager::deserialize(const string & value) const noexcept { - return deserialize<int64_t>(value); -} - -SaveManager::SaveManager() { dbg_trace(); } - -SaveManager & SaveManager::get_instance() { - dbg_trace(); - static SaveManager instance; - return instance; -} - -DB & SaveManager::get_db() { - Config & cfg = Config::get_instance(); - // TODO: make this path relative to XDG_DATA_HOME on Linux and whatever the - // default equivalent is on Windows using some third party library - static DB db(cfg.savemgr.location); - return db; -} - -bool SaveManager::has(const string & key) { - DB & db = this->get_db(); - return db.has(key); -} - -template <> -void SaveManager::set(const string & key, const string & value) { - DB & db = this->get_db(); - db.set(key, value); -} -template <typename T> -void SaveManager::set(const string & key, const T & value) { - DB & db = this->get_db(); - db.set(key, std::to_string(value)); -} -template void SaveManager::set(const string &, const uint8_t &); -template void SaveManager::set(const string &, const int8_t &); -template void SaveManager::set(const string &, const uint16_t &); -template void SaveManager::set(const string &, const int16_t &); -template void SaveManager::set(const string &, const uint32_t &); -template void SaveManager::set(const string &, const int32_t &); -template void SaveManager::set(const string &, const uint64_t &); -template void SaveManager::set(const string &, const int64_t &); -template void SaveManager::set(const string &, const float &); -template void SaveManager::set(const string &, const double &); - -template <typename T> -ValueBroker<T> SaveManager::get(const string & key, const T & default_value) { - if (!this->has(key)) this->set<T>(key, default_value); - return this->get<T>(key); -} -template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &); -template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &); -template ValueBroker<uint16_t> SaveManager::get(const string &, const uint16_t &); -template ValueBroker<int16_t> SaveManager::get(const string &, const int16_t &); -template ValueBroker<uint32_t> SaveManager::get(const string &, const uint32_t &); -template ValueBroker<int32_t> SaveManager::get(const string &, const int32_t &); -template ValueBroker<uint64_t> SaveManager::get(const string &, const uint64_t &); -template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &); -template ValueBroker<float> SaveManager::get(const string &, const float &); -template ValueBroker<double> SaveManager::get(const string &, const double &); -template ValueBroker<string> SaveManager::get(const string &, const string &); - -template <typename T> -ValueBroker<T> SaveManager::get(const string & key) { - T value; - return { - [this, key](const T & target) { this->set<T>(key, target); }, - [this, key, value]() mutable -> const T & { - value = this->deserialize<T>(this->get_db().get(key)); - return value; - }, - }; -} -template ValueBroker<uint8_t> SaveManager::get(const string &); -template ValueBroker<int8_t> SaveManager::get(const string &); -template ValueBroker<uint16_t> SaveManager::get(const string &); -template ValueBroker<int16_t> SaveManager::get(const string &); -template ValueBroker<uint32_t> SaveManager::get(const string &); -template ValueBroker<int32_t> SaveManager::get(const string &); -template ValueBroker<uint64_t> SaveManager::get(const string &); -template ValueBroker<int64_t> SaveManager::get(const string &); -template ValueBroker<float> SaveManager::get(const string &); -template ValueBroker<double> SaveManager::get(const string &); -template ValueBroker<string> SaveManager::get(const string &); diff --git a/src/crepe/api/SaveManager.h b/src/crepe/api/SaveManager.h deleted file mode 100644 index 3d8c852..0000000 --- a/src/crepe/api/SaveManager.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include <memory> - -#include "../ValueBroker.h" - -namespace crepe { - -class DB; - -/** - * \brief Save data manager - * - * This class provides access to a simple key-value store that stores - * - integers (8-64 bit, signed or unsigned) - * - real numbers (float or double) - * - string (std::string) - * - * The underlying database is a key-value store. - */ -class SaveManager { -public: - /** - * \brief Get a read/write reference to a value and initialize it if it does not yet exist - * - * \param key The value key - * \param default_value Value to initialize \c key with if it does not already exist in the - * database - * - * \return Read/write reference to the value - */ - template <typename T> - ValueBroker<T> get(const std::string & key, const T & default_value); - - /** - * \brief Get a read/write reference to a value - * - * \param key The value key - * - * \return Read/write reference to the value - * - * \note Attempting to read this value before it is initialized (i.e. set) will result in an - * exception - */ - template <typename T> - ValueBroker<T> get(const std::string & key); - - /** - * \brief Set a value directly - * - * \param key The value key - * \param value The value to store - */ - template <typename T> - void set(const std::string & key, const T & value); - - /** - * \brief Check if the save file has a value for this \c key - * - * \param key The value key - * - * \returns True if the key exists, or false if it does not - */ - bool has(const std::string & key); - -private: - SaveManager(); - virtual ~SaveManager() = default; - -private: - /** - * \brief Serialize an arbitrary value to STL string - * - * \tparam T Type of arbitrary value - * - * \returns String representation of value - */ - template <typename T> - std::string serialize(const T &) const noexcept; - - /** - * \brief Deserialize an STL string back to type \c T - * - * \tparam T Type of value - * \param value Serialized value - * - * \returns Deserialized value - */ - template <typename T> - T deserialize(const std::string & value) const noexcept; - -public: - // singleton - static SaveManager & get_instance(); - SaveManager(const SaveManager &) = delete; - SaveManager(SaveManager &&) = delete; - SaveManager & operator=(const SaveManager &) = delete; - SaveManager & operator=(SaveManager &&) = delete; - -private: - /** - * \brief Create an instance of DB and return its reference - * - * \returns DB instance - * - * This function exists because DB is a facade class, which can't directly be used in the API - * without workarounds - * - * TODO: better solution - */ - static DB & get_db(); -}; - -} // namespace crepe diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp deleted file mode 100644 index 88aa82d..0000000 --- a/src/crepe/api/Scene.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "Scene.h" - -using namespace crepe; - -Scene::Scene(ComponentManager & mgr, const std::string & name) - : component_manager(mgr), - name(name) {} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index 334f306..9f1e8ce 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -2,22 +2,57 @@ #include <string> +#include "../manager/Mediator.h" +#include "../util/OptionalRef.h" + namespace crepe { +class SceneManager; class ComponentManager; +/** + * \brief Represents a Scene + * + * This class represents a Scene. The Scene class is only used as an interface for the game + * programmer. + */ class Scene { +protected: + // NOTE: This must be the only constructor on Scene, see "Late references" below + Scene() = default; + //! SceneManager instances Scene + friend class SceneManager; + public: - Scene(ComponentManager & mgr, const std::string & name); virtual ~Scene() = default; +public: + //! Load the scene virtual void load_scene() = 0; + /** + * \brief Get the scene's name + * \return The scene's name + */ + virtual std::string get_name() const = 0; -public: - const std::string name; + // TODO: Late references should ALWAYS be private! This is currently kept as-is so unit tests + // keep passing, but this reference should not be directly accessible by the user!!! protected: - ComponentManager & component_manager; + /** + * \name Late references + * + * These references are set by SceneManager immediately after calling the constructor of Scene. + * + * \note Scene must have a constructor without arguments so the game programmer doesn't need to + * manually add `using Scene::Scene` to their concrete scene class, if they want to add a + * constructor with arguments (e.g. for passing references to their own concrete Scene classes). + * + * \{ + */ + //! Mediator reference + OptionalRef<Mediator> mediator; + //! \} }; } // namespace crepe diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/api/SceneManager.cpp deleted file mode 100644 index 7fb5cb0..0000000 --- a/src/crepe/api/SceneManager.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include <algorithm> -#include <memory> - -#include "../ComponentManager.h" - -#include "SceneManager.h" - -using namespace crepe; -using namespace std; - -SceneManager::SceneManager(ComponentManager & mgr) : component_manager(mgr) {} - -void SceneManager::set_next_scene(const string & name) { next_scene = name; } - -void SceneManager::load_next_scene() { - // next scene not set - if (this->next_scene.empty()) return; - - auto it = find_if(this->scenes.begin(), this->scenes.end(), - [&next_scene = this->next_scene](unique_ptr<Scene> & scene) { - return scene->name == next_scene; - }); - - // next scene not found - if (it == this->scenes.end()) return; - unique_ptr<Scene> & scene = *it; - - // Delete all components of the current scene - ComponentManager & mgr = this->component_manager; - mgr.delete_all_components(); - - // Load the new scene - scene->load_scene(); -} diff --git a/src/crepe/api/SceneManager.h b/src/crepe/api/SceneManager.h deleted file mode 100644 index e854794..0000000 --- a/src/crepe/api/SceneManager.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include <memory> -#include <queue> -#include <vector> - -#include "Scene.h" - -namespace crepe { - -class ComponentManager; - -class SceneManager { -public: - SceneManager(ComponentManager & mgr); - -public: - /** - * \brief Add a new concrete scene to the scene manager - * - * \tparam T Type of concrete scene - * \param name Name of new scene - */ - template <typename T> - void add_scene(const std::string & name); - /** - * \brief Set the next scene - * - * This scene will be loaded at the end of the frame - * - * \param name Name of the next scene - */ - void set_next_scene(const std::string & name); - //! Load a new scene (if there is one) - void load_next_scene(); - -private: - std::vector<std::unique_ptr<Scene>> scenes; - std::string next_scene; - ComponentManager & component_manager; -}; - -} // namespace crepe - -#include "SceneManager.hpp" diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/api/SceneManager.hpp deleted file mode 100644 index 714f690..0000000 --- a/src/crepe/api/SceneManager.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "SceneManager.h" - -namespace crepe { - -template <typename T> -void SceneManager::add_scene(const std::string & name) { - using namespace std; - static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene"); - - Scene * scene = new T(this->component_manager, name); - this->scenes.emplace_back(unique_ptr<Scene>(scene)); - - // The first scene added, is the one that will be loaded at the beginning - if (next_scene.empty()) { - next_scene = name; - } -} - -} // namespace crepe diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp new file mode 100644 index 0000000..4091fd4 --- /dev/null +++ b/src/crepe/api/Script.cpp @@ -0,0 +1,27 @@ +#include <string> + +#include "../manager/SceneManager.h" + +#include "Script.h" + +using namespace crepe; +using namespace std; + +Script::~Script() { + Mediator & mediator = this->mediator; + EventManager & mgr = mediator.event_manager; + for (auto id : this->listeners) { + mgr.unsubscribe(id); + } +} + +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback) { + this->subscribe_internal(callback, this->game_object_id); +} + +void Script::set_next_scene(const string & name) { + Mediator & mediator = this->mediator; + SceneManager & mgr = mediator.scene_manager; + mgr.set_next_scene(name); +} diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 12ec7f1..1b339b0 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,6 +2,11 @@ #include <vector> +#include "../manager/EventManager.h" +#include "../manager/Mediator.h" +#include "../types.h" +#include "../util/OptionalRef.h" + namespace crepe { class ScriptSystem; @@ -11,72 +16,180 @@ class ComponentManager; /** * \brief Script interface * - * This class is used as a base class for user-defined scripts that can be - * added to game objects using the \c BehaviorScript component. + * This class is used as a base class for user-defined scripts that can be added to game + * objects using the \c BehaviorScript component. + * + * \info Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as + * member or lambda methods in derivative user script classes and registered in \c init(). + * + * \warning Concrete scripts are allowed do create a custom constructor, but the utility + * functions should not be called inside the constructor as they rely on late references that + * are only available after the constructor returns. + * + * \see feature_script */ class Script { - //! ScriptSystem calls \c update() - friend class crepe::ScriptSystem; - protected: /** - * \brief Script initialization function + * \name Interface functions + * \{ + */ + /** + * \brief Script initialization function (empty by default) * * This function is called during the ScriptSystem::update() routine *before* - * Script::update() if it (a) has not yet been called and (b) the \c - * BehaviorScript component holding this script instance is active. + * Script::update() if it (a) has not yet been called and (b) the \c BehaviorScript component + * holding this script instance is active. */ virtual void init() {} /** - * \brief Script update function + * \brief Script update function (empty by default) * - * This function is called during the ScriptSystem::update() routine if the - * \c BehaviorScript component holding this script instance is active. + * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript + * component holding this script instance is active. */ virtual void update() {} - // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be implemented as - // member methods in derivative user script classes and registered in init(), otherwise this - // class will balloon in size with each added event. + //! \} + + //! ScriptSystem calls \c init() and \c update() + friend class crepe::ScriptSystem; protected: /** - * \brief Get single component of type \c T on this game object (utility) + * \name Utility functions + * \{ + */ + + /** + * \brief Get single component of type \c T on this game object * * \tparam T Type of component * * \returns Reference to component * - * \throws nullptr if this game object does not have a component matching - * type \c T + * \throws std::runtime_error if this game object does not have a component with type \c T */ template <typename T> T & get_component() const; + // TODO: make get_component calls for component types that can have more than 1 instance + // cause compile-time errors /** - * \brief Get all components of type \c T on this game object (utility) + * \brief Get all components of type \c T on this game object * * \tparam T Type of component * * \returns List of component references */ template <typename T> - std::vector<std::reference_wrapper<T>> get_components() const; + RefVector<T> get_components() const; + + /** + * \brief Log a message using Log::logf + * + * \tparam Args Log::logf parameters + * \param args Log::logf parameters + */ + template <typename... Args> + void logf(Args &&... args); + + /** + * \brief Subscribe to an event with an explicit channel + * \see EventManager::subscribe + */ + template <typename EventType> + void subscribe(const EventHandler<EventType> & callback, event_channel_t channel); + /** + * \brief Subscribe to an event on EventManager::CHANNEL_ALL + * \see EventManager::subscribe + */ + template <typename EventType> + void subscribe(const EventHandler<EventType> & callback); + + /** + * \brief Set the next scene using SceneManager + * \see SceneManager::set_next_scene + */ + void set_next_scene(const std::string & name); + + //! \} + +private: + /** + * \brief Internal subscribe function + * + * This function exists so certain template specializations of Script::subscribe can be + * explicitly deleted, and does the following: + * - Wrap the user-provided callback in a check that tests if the parent BehaviorScript + * component is still active + * - Store the subscriber handle returned by the event manager so this listener is + * automatically unsubscribed at the end of this Script instance's life + * + * \tparam EventType concrete Event class + * \param callback User-provided callback function + * \param channel Event channel (may have been overridden by template specializations) + */ + template <typename EventType> + void subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel); protected: - // NOTE: Script must have a constructor without arguments so the game - // programmer doesn't need to manually add `using Script::Script` to their - // concrete script class. + // NOTE: This must be the only constructor on Script, see "Late references" below Script() = default; //! Only \c BehaviorScript instantiates Script friend class BehaviorScript; +public: + // std::unique_ptr destroys script + virtual ~Script(); + +private: + Script(const Script &) = delete; + Script(Script &&) = delete; + Script & operator=(const Script &) = delete; + Script & operator=(Script &&) = delete; + private: - // These references are set by BehaviorScript immediately after calling the - // constructor of Script. - BehaviorScript * parent_ref = nullptr; - ComponentManager * component_manager_ref = nullptr; + /** + * \name Late references + * + * These references are set by BehaviorScript immediately after calling the constructor of + * Script. + * + * \note Script must have a constructor without arguments so the game programmer doesn't need + * to manually add `using Script::Script` to their concrete script class if they want to + * implement a non-default constructor (e.g. for passing references to their own concrete + * Script classes). + * + * \{ + */ + //! Game object ID of game object parent BehaviorScript is attached to + game_object_id_t game_object_id; + //! Reference to parent component + OptionalRef<bool> active; + //! Mediator reference + OptionalRef<Mediator> mediator; + //! \} + +private: + //! Flag to indicate if \c init() has been called already + bool initialized = false; + //! List of subscribed events + std::vector<subscription_t> listeners; }; +/** + * \brief Subscribe to CollisionEvent for the current GameObject + * + * This is a template specialization for Script::subscribe which automatically sets the event + * channel so the callback handler is only called for CollisionEvent events that apply to the + * current GameObject the parent BehaviorScript is attached to. + */ +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback); +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback, event_channel_t) + = delete; + } // namespace crepe #include "Script.hpp" diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index c4e07e8..45f1ff1 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../ComponentManager.h" +#include "../manager/ComponentManager.h" #include "BehaviorScript.h" #include "Script.h" @@ -10,7 +10,7 @@ namespace crepe { template <typename T> T & Script::get_component() const { using namespace std; - vector<reference_wrapper<T>> all_components = this->get_components<T>(); + RefVector<T> all_components = this->get_components<T>(); if (all_components.size() < 1) throw runtime_error( format("Script: no component found with type = {}", typeid(T).name())); @@ -19,11 +19,41 @@ T & Script::get_component() const { } template <typename T> -std::vector<std::reference_wrapper<T>> Script::get_components() const { - auto & parent = *this->parent_ref; - auto & mgr = *this->component_manager_ref; +RefVector<T> Script::get_components() const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; - return mgr.get_components_by_id<T>(parent.game_object_id); + return mgr.get_components_by_id<T>(this->game_object_id); +} + +template <typename... Args> +void Script::logf(Args &&... args) { + Log::logf(std::forward<Args>(args)...); +} + +template <typename EventType> +void Script::subscribe_internal(const EventHandler<EventType> & callback, + event_channel_t channel) { + Mediator & mediator = this->mediator; + EventManager & mgr = mediator.event_manager; + subscription_t listener = mgr.subscribe<EventType>( + [this, callback](const EventType & data) -> bool { + bool & active = this->active; + if (!active) return false; + return callback(data); + }, + channel); + this->listeners.push_back(listener); +} + +template <typename EventType> +void Script::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) { + this->subscribe_internal(callback, channel); +} + +template <typename EventType> +void Script::subscribe(const EventHandler<EventType> & callback) { + this->subscribe_internal(callback, EventManager::CHANNEL_ALL); } } // namespace crepe diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index bd2d5cf..0a2ad4c 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,7 +1,7 @@ -#include <memory> +#include <cmath> +#include <utility> #include "../util/Log.h" -#include "facade/SDLContext.h" #include "Component.h" #include "Sprite.h" @@ -10,16 +10,21 @@ using namespace std; using namespace crepe; -Sprite::Sprite(game_object_id_t id, const shared_ptr<Texture> image, const Color & color, - const FlipSettings & flip) +Sprite::Sprite(game_object_id_t id, Texture & image, const Color & color, + const FlipSettings & flip, int sort_layer, int order_layer, int height) : Component(id), color(color), flip(flip), - sprite_image(image) { + sprite_image(std::move(image)), + sorting_in_layer(sort_layer), + order_in_layer(order_layer), + height(height) { + dbg_trace(); - this->sprite_rect.w = sprite_image->get_width(); - this->sprite_rect.h = sprite_image->get_height(); + this->mask.w = sprite_image.get_size().x; + this->mask.h = sprite_image.get_size().y; + this->aspect_ratio = static_cast<double>(this->mask.w) / this->mask.h; } Sprite::~Sprite() { dbg_trace(); } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index 0192793..a0e90a0 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -1,25 +1,14 @@ #pragma once -#include <memory> +#include <cstdint> + +#include "../Component.h" #include "Color.h" -#include "Component.h" #include "Texture.h" namespace crepe { -struct Rect { - int w = 0; - int h = 0; - int x = 0; - int y = 0; -}; - -struct FlipSettings { - bool flip_x = false; - bool flip_y = false; -}; - class SDLContext; class Animator; class AnimatorSystem; @@ -33,6 +22,12 @@ class AnimatorSystem; class Sprite : public Component { public: + struct FlipSettings { + bool flip_x = false; + bool flip_y = false; + }; + +public: // TODO: Loek comment in github #27 will be looked another time // about shared_ptr Texture /** @@ -41,9 +36,12 @@ public: * \param image Shared pointer to the texture for this sprite. * \param color Color tint applied to the sprite. * \param flip Flip settings for horizontal and vertical orientation. + * \param order_layer decides the sorting in layer of the sprite. + * \param sort_layer decides the order in layer of the sprite. + * \param height the height of the image in game units */ - Sprite(game_object_id_t id, const std::shared_ptr<Texture> image, const Color & color, - const FlipSettings & flip); + Sprite(game_object_id_t id, Texture & image, const Color & color, + const FlipSettings & flip, int sort_layer, int order_layer, int height); /** * \brief Destroys the Sprite instance. @@ -51,38 +49,49 @@ public: ~Sprite(); //! Texture used for the sprite - const std::shared_ptr<Texture> sprite_image; + const Texture sprite_image; + //! Color tint of the sprite Color color; + //! Flip settings for the sprite FlipSettings flip; + //! Layer sorting level of the sprite - uint8_t sorting_in_layer = 0; + const int sorting_in_layer; //! Order within the sorting layer - uint8_t order_in_layer = 0; + const int order_in_layer; + + //! height in world units + const int height; -public: /** - * \brief Gets the maximum number of instances allowed for this sprite. - * \return Maximum instance count as an integer. + * \aspect_ratio ratio of the img so that scaling will not become weird * - * For now is this number randomly picked. I think it will eventually be 1. + * cannot be const because if Animator component is addded then ratio becomes scuffed and + * does it need to be calculated again in the Animator */ - virtual int get_instances_max() const { return 10; } + double aspect_ratio; private: - //! Reads the sprite_rect of sprite + //! Reads the mask of sprite friend class SDLContext; - //! Reads the all the variables plus the sprite_rect + //! Reads the all the variables plus the mask friend class Animator; - //! Reads the all the variables plus the sprite_rect + //! Reads the all the variables plus the mask friend class AnimatorSystem; + struct Rect { + int w = 0; + int h = 0; + int x = 0; + int y = 0; + }; //! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator - // object is present in GameObject - Rect sprite_rect; + // object is present in GameObject. this is in sprite pixels + Rect mask; }; } // namespace crepe diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index de0d0ea..2b56271 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -1,22 +1,16 @@ -#include <SDL2/SDL_render.h> - #include "../facade/SDLContext.h" #include "../util/Log.h" #include "Asset.h" #include "Texture.h" +#include "types.h" using namespace crepe; using namespace std; -Texture::Texture(unique_ptr<Asset> res) { - dbg_trace(); - this->load(std::move(res)); -} - -Texture::Texture(const char * src) { +Texture::Texture(const Asset & src) { dbg_trace(); - this->load(make_unique<Asset>(src)); + this->load(src); } Texture::~Texture() { @@ -24,16 +18,21 @@ Texture::~Texture() { this->texture.reset(); } -void Texture::load(unique_ptr<Asset> res) { - SDLContext & ctx = SDLContext::get_instance(); - this->texture = std::move(ctx.texture_from_path(res->get_canonical())); +Texture::Texture(Texture && other) noexcept : texture(std::move(other.texture)) {} + +Texture & Texture::operator=(Texture && other) noexcept { + if (this != &other) { + texture = std::move(other.texture); + } + return *this; } -int Texture::get_width() const { - if (this->texture == nullptr) return 0; - return SDLContext::get_instance().get_width(*this); +void Texture::load(const Asset & res) { + SDLContext & ctx = SDLContext::get_instance(); + this->texture = ctx.texture_from_path(res.get_path()); } -int Texture::get_height() const { - if (this->texture == nullptr) return 0; - return SDLContext::get_instance().get_width(*this); + +ivec2 Texture::get_size() const { + if (this->texture == nullptr) return {}; + return SDLContext::get_instance().get_size(*this); } diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h index 6965223..1817910 100644 --- a/src/crepe/api/Texture.h +++ b/src/crepe/api/Texture.h @@ -8,6 +8,7 @@ #include <memory> #include "Asset.h" +#include "types.h" namespace crepe { @@ -25,16 +26,10 @@ class Texture { public: /** - * \brief Constructs a Texture from a file path. - * \param src Path to the image file to be loaded as a texture. - */ - Texture(const char * src); - - /** * \brief Constructs a Texture from an Asset resource. - * \param res Unique pointer to an Asset resource containing texture data. + * \param src Asset with texture data to load. */ - Texture(std::unique_ptr<Asset> res); + Texture(const Asset & src); /** * \brief Destroys the Texture instance, freeing associated resources. @@ -42,24 +37,23 @@ public: ~Texture(); // FIXME: this constructor shouldn't be necessary because this class doesn't manage memory - /** - * \brief Gets the width of the texture. - * \return Width of the texture in pixels. - */ - int get_width() const; + Texture(Texture && other) noexcept; + Texture & operator=(Texture && other) noexcept; + Texture(const Texture &) = delete; + Texture & operator=(const Texture &) = delete; /** - * \brief Gets the height of the texture. - * \return Height of the texture in pixels. + * \brief Gets the width and height of the texture. + * \return Width and height of the texture in pixels. */ - int get_height() const; + ivec2 get_size() const; private: /** * \brief Loads the texture from an Asset resource. * \param res Unique pointer to an Asset resource to load the texture from. */ - void load(std::unique_ptr<Asset> res); + void load(const Asset & res); private: //! The texture of the class from the library diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index cd944bd..a85b792 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -4,7 +4,7 @@ using namespace crepe; -Transform::Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale) +Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale) : Component(id), position(point), rotation(rotation), diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index 18aa293..6243a93 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -1,8 +1,7 @@ #pragma once -#include "api/Vector2.h" - #include "Component.h" +#include "types.h" namespace crepe { @@ -15,7 +14,7 @@ namespace crepe { class Transform : public Component { public: //! Translation (shift) - Vector2 position = {0, 0}; + vec2 position = {0, 0}; //! Rotation, in degrees double rotation = 0; //! Multiplication factor @@ -28,7 +27,7 @@ protected: * \param rotation The rotation of the GameObject * \param scale The scale of the GameObject */ - Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale); + Transform(game_object_id_t id, const vec2 & point, double rotation, double scale); /** * There is always exactly one transform component per entity * \return 1 diff --git a/src/crepe/api/UIObject.cpp b/src/crepe/api/UIObject.cpp new file mode 100644 index 0000000..d239b89 --- /dev/null +++ b/src/crepe/api/UIObject.cpp @@ -0,0 +1,8 @@ +#include "UIObject.h" + +using namespace crepe; + +UIObject::UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset) + : Component(id), + dimensions(dimensions), + offset(offset) {} diff --git a/src/crepe/api/UIObject.h b/src/crepe/api/UIObject.h new file mode 100644 index 0000000..f7f4fba --- /dev/null +++ b/src/crepe/api/UIObject.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../Component.h" + +namespace crepe { + +/** + * \brief Represents a UI object in the game, derived from the Component class. + */ +class UIObject : public Component { +public: + /** + * \brief Constructs a UiObject with the specified game object ID. + * \param id The unique ID of the game object associated with this UI object. + * \param dimensions width and height of the UIObject + * \param offset Offset relative to the GameObject Transform + */ + UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset); + //! Width and height of the UIObject + vec2 dimensions; + //! Position offset relative to this GameObjects Transform + vec2 offset; +}; + +} // namespace crepe diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp deleted file mode 100644 index 30b968e..0000000 --- a/src/crepe/api/Vector2.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "Vector2.h" - -using namespace crepe; - -Vector2 Vector2::operator-(const Vector2 & other) const { return {x - other.x, y - other.y}; } - -Vector2 Vector2::operator+(const Vector2 & other) const { return {x + other.x, y + other.y}; } - -Vector2 Vector2::operator*(double scalar) const { return {x * scalar, y * scalar}; } - -Vector2 & Vector2::operator*=(const Vector2 & other) { - x *= other.x; - y *= other.y; - return *this; -} - -Vector2 & Vector2::operator+=(const Vector2 & other) { - x += other.x; - y += other.y; - return *this; -} - -Vector2 & Vector2::operator+=(double other) { - x += other; - y += other; - return *this; -} - -Vector2 Vector2::operator-() const { return {-x, -y}; } - -bool Vector2::operator==(const Vector2 & other) const { return x == other.x && y == other.y; } - -bool Vector2::operator!=(const Vector2 & other) const { return !(*this == other); } diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index 2fb6136..c278c87 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -3,38 +3,71 @@ namespace crepe { //! 2D vector +template <class T> struct Vector2 { //! X component of the vector - double x = 0; + T x = 0; //! Y component of the vector - double y = 0; + T y = 0; //! Subtracts another vector from this vector and returns the result. - Vector2 operator-(const Vector2 & other) const; + Vector2 operator-(const Vector2<T> & other) const; + + //! Subtracts a scalar value from both components of this vector and returns the result. + Vector2 operator-(T scalar) const; //! Adds another vector to this vector and returns the result. - Vector2 operator+(const Vector2 & other) const; + Vector2 operator+(const Vector2<T> & other) const; + + //! Adds a scalar value to both components of this vector and returns the result. + Vector2 operator+(T scalar) const; + + //! Multiplies this vector by another vector element-wise and returns the result. + Vector2 operator*(const Vector2<T> & other) const; //! Multiplies this vector by a scalar and returns the result. - Vector2 operator*(double scalar) const; + Vector2 operator*(T scalar) const; - //! Multiplies this vector by another vector element-wise and updates this vector. - Vector2 & operator*=(const Vector2 & other); + //! Divides this vector by another vector element-wise and returns the result. + Vector2 operator/(const Vector2<T> & other) const; + + //! Divides this vector by a scalar and returns the result. + Vector2 operator/(T scalar) const; //! Adds another vector to this vector and updates this vector. - Vector2 & operator+=(const Vector2 & other); + Vector2 & operator+=(const Vector2<T> & other); //! Adds a scalar value to both components of this vector and updates this vector. - Vector2 & operator+=(double other); + Vector2 & operator+=(T other); + + //! Subtracts another vector from this vector and updates this vector. + Vector2 & operator-=(const Vector2<T> & other); + + //! Subtracts a scalar value from both components of this vector and updates this vector. + Vector2 & operator-=(T other); + + //! Multiplies this vector by another vector element-wise and updates this vector. + Vector2 & operator*=(const Vector2<T> & other); + + //! Multiplies this vector by a scalar and updates this vector. + Vector2 & operator*=(T other); + + //! Divides this vector by another vector element-wise and updates this vector. + Vector2 & operator/=(const Vector2<T> & other); + + //! Divides this vector by a scalar and updates this vector. + Vector2 & operator/=(T other); //! Returns the negation of this vector. Vector2 operator-() const; //! Checks if this vector is equal to another vector. - bool operator==(const Vector2 & other) const; + bool operator==(const Vector2<T> & other) const; //! Checks if this vector is not equal to another vector. - bool operator!=(const Vector2 & other) const; + bool operator!=(const Vector2<T> & other) const; }; } // namespace crepe + +#include "Vector2.hpp" diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp new file mode 100644 index 0000000..cad15f8 --- /dev/null +++ b/src/crepe/api/Vector2.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "Vector2.h" + +namespace crepe { + +template <class T> +Vector2<T> Vector2<T>::operator-(const Vector2<T> & other) const { + return {x - other.x, y - other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator-(T scalar) const { + return {x - scalar, y - scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator+(const Vector2<T> & other) const { + return {x + other.x, y + other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator+(T scalar) const { + return {x + scalar, y + scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator*(const Vector2<T> & other) const { + return {x * other.x, y * other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator*(T scalar) const { + return {x * scalar, y * scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator/(const Vector2<T> & other) const { + return {x / other.x, y / other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator/(T scalar) const { + return {x / scalar, y / scalar}; +} + +template <class T> +Vector2<T> & Vector2<T>::operator+=(const Vector2<T> & other) { + x += other.x; + y += other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator+=(T other) { + x += other; + y += other; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator-=(const Vector2<T> & other) { + x -= other.x; + y -= other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator-=(T other) { + x -= other; + y -= other; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator*=(const Vector2<T> & other) { + x *= other.x; + y *= other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator*=(T other) { + x *= other; + y *= other; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator/=(const Vector2<T> & other) { + x /= other.x; + y /= other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator/=(T other) { + x /= other; + y /= other; + return *this; +} + +template <class T> +Vector2<T> Vector2<T>::operator-() const { + return {-x, -y}; +} + +template <class T> +bool Vector2<T>::operator==(const Vector2<T> & other) const { + return x == other.x && y == other.y; +} + +template <class T> +bool Vector2<T>::operator!=(const Vector2<T> & other) const { + return !(*this == other); +} + +} // namespace crepe |