aboutsummaryrefslogtreecommitdiff
path: root/src/crepe
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe')
-rw-r--r--src/crepe/Component.cpp12
-rw-r--r--src/crepe/Component.h28
-rw-r--r--src/crepe/api/Animator.cpp2
-rw-r--r--src/crepe/api/BehaviorScript.cpp4
-rw-r--r--src/crepe/api/BehaviorScript.hpp3
-rw-r--r--src/crepe/api/CMakeLists.txt5
-rw-r--r--src/crepe/api/Camera.cpp2
-rw-r--r--src/crepe/api/Engine.cpp63
-rw-r--r--src/crepe/api/Engine.h78
-rw-r--r--src/crepe/api/Engine.hpp12
-rw-r--r--src/crepe/api/GameObject.cpp21
-rw-r--r--src/crepe/api/GameObject.h25
-rw-r--r--src/crepe/api/GameObject.hpp2
-rw-r--r--src/crepe/api/LoopManager.cpp86
-rw-r--r--src/crepe/api/LoopManager.h122
-rw-r--r--src/crepe/api/ParticleEmitter.cpp14
-rw-r--r--src/crepe/api/ParticleEmitter.h6
-rw-r--r--src/crepe/api/Script.cpp20
-rw-r--r--src/crepe/api/Script.h96
-rw-r--r--src/crepe/api/Script.hpp34
-rw-r--r--src/crepe/api/Sprite.cpp2
-rw-r--r--src/crepe/api/Transform.cpp11
-rw-r--r--src/crepe/api/Transform.h6
-rw-r--r--src/crepe/facade/DB.cpp2
-rw-r--r--src/crepe/facade/SDLContext.cpp2
-rw-r--r--src/crepe/facade/Sound.cpp2
-rw-r--r--src/crepe/facade/SoundContext.cpp2
-rw-r--r--src/crepe/facade/Texture.cpp2
-rw-r--r--src/crepe/manager/CMakeLists.txt5
-rw-r--r--src/crepe/manager/ComponentManager.cpp29
-rw-r--r--src/crepe/manager/ComponentManager.h48
-rw-r--r--src/crepe/manager/LoopTimerManager.cpp2
-rw-r--r--src/crepe/manager/LoopTimerManager.h4
-rw-r--r--src/crepe/manager/Mediator.h4
-rw-r--r--src/crepe/manager/ReplayManager.cpp70
-rw-r--r--src/crepe/manager/ReplayManager.h94
-rw-r--r--src/crepe/manager/ResourceManager.cpp2
-rw-r--r--src/crepe/manager/SystemManager.cpp66
-rw-r--r--src/crepe/manager/SystemManager.h85
-rw-r--r--src/crepe/manager/SystemManager.hpp (renamed from src/crepe/api/LoopManager.hpp)17
-rw-r--r--src/crepe/system/AISystem.cpp2
-rw-r--r--src/crepe/system/AISystem.h2
-rw-r--r--src/crepe/system/AnimatorSystem.cpp4
-rw-r--r--src/crepe/system/AnimatorSystem.h2
-rw-r--r--src/crepe/system/AudioSystem.cpp2
-rw-r--r--src/crepe/system/AudioSystem.h2
-rw-r--r--src/crepe/system/CMakeLists.txt4
-rw-r--r--src/crepe/system/CollisionSystem.cpp2
-rw-r--r--src/crepe/system/CollisionSystem.h2
-rw-r--r--src/crepe/system/EventSystem.cpp9
-rw-r--r--src/crepe/system/EventSystem.h21
-rw-r--r--src/crepe/system/InputSystem.cpp2
-rw-r--r--src/crepe/system/InputSystem.h2
-rw-r--r--src/crepe/system/ParticleSystem.cpp2
-rw-r--r--src/crepe/system/ParticleSystem.h2
-rw-r--r--src/crepe/system/PhysicsSystem.cpp3
-rw-r--r--src/crepe/system/PhysicsSystem.h2
-rw-r--r--src/crepe/system/RenderSystem.cpp2
-rw-r--r--src/crepe/system/RenderSystem.h2
-rw-r--r--src/crepe/system/ReplaySystem.cpp54
-rw-r--r--src/crepe/system/ReplaySystem.h44
-rw-r--r--src/crepe/system/ScriptSystem.cpp3
-rw-r--r--src/crepe/system/ScriptSystem.h2
-rw-r--r--src/crepe/system/System.h10
-rw-r--r--src/crepe/util/Log.cpp1
-rw-r--r--src/crepe/util/Log.h21
-rw-r--r--src/crepe/util/dbg.h18
67 files changed, 947 insertions, 365 deletions
diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp
index acfd35c..ae76e65 100644
--- a/src/crepe/Component.cpp
+++ b/src/crepe/Component.cpp
@@ -1,5 +1,17 @@
#include "Component.h"
using namespace crepe;
+using namespace std;
Component::Component(game_object_id_t id) : game_object_id(id) {}
+
+Component & Component::operator=(const Component & other) {
+ this->active = other.active;
+ return *this;
+}
+
+unique_ptr<Component> Component::save() const {
+ return unique_ptr<Component>(new Component(*this));
+}
+
+void Component::restore(const Component & snapshot) { *this = snapshot; }
diff --git a/src/crepe/Component.h b/src/crepe/Component.h
index eff5a58..52e06d5 100644
--- a/src/crepe/Component.h
+++ b/src/crepe/Component.h
@@ -1,5 +1,7 @@
#pragma once
+#include <memory>
+
#include "types.h"
namespace crepe {
@@ -32,11 +34,33 @@ protected:
//! Only ComponentManager can create components
friend class ComponentManager;
- Component(const Component &) = delete;
+ // components are never moved
Component(Component &&) = delete;
- virtual Component & operator=(const Component &) = delete;
virtual Component & operator=(Component &&) = delete;
+protected:
+ /**
+ * \name ReplayManager (Memento) functions
+ * \{
+ */
+ /**
+ * \brief Save a snapshot of this component's state
+ * \note This function should only be implemented on components that should be saved/restored
+ * by ReplayManager.
+ * \returns Unique pointer to a deep copy of this component
+ */
+ virtual std::unique_ptr<Component> save() const;
+ //! Copy constructor (used by \c save())
+ Component(const Component &) = default;
+ /**
+ * \brief Restore this component from a snapshot
+ * \param snapshot Data to fill this component with (as returned by \c save())
+ */
+ virtual void restore(const Component & snapshot);
+ //! Copy assignment operator (used by \c restore())
+ virtual Component & operator=(const Component &);
+ //! \}
+
public:
virtual ~Component() = default;
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp
index 4ce4bf0..203cef3 100644
--- a/src/crepe/api/Animator.cpp
+++ b/src/crepe/api/Animator.cpp
@@ -1,5 +1,5 @@
-#include "util/Log.h"
+#include "util/dbg.h"
#include "Animator.h"
#include "Component.h"
diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp
index d22afdf..af7572c 100644
--- a/src/crepe/api/BehaviorScript.cpp
+++ b/src/crepe/api/BehaviorScript.cpp
@@ -10,6 +10,6 @@ BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator)
template <>
BehaviorScript & GameObject::add_component<BehaviorScript>() {
- ComponentManager & mgr = this->component_manager;
- return mgr.add_component<BehaviorScript>(this->id, mgr.mediator);
+ ComponentManager & mgr = this->mediator.component_manager;
+ return mgr.add_component<BehaviorScript>(this->id, this->mediator);
}
diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp
index b9bb1e2..353d5e2 100644
--- a/src/crepe/api/BehaviorScript.hpp
+++ b/src/crepe/api/BehaviorScript.hpp
@@ -2,8 +2,6 @@
#include <type_traits>
-#include "../util/Log.h"
-
#include "BehaviorScript.h"
#include "Script.h"
@@ -11,7 +9,6 @@ namespace crepe {
template <class T, typename... Args>
BehaviorScript & BehaviorScript::set_script(Args &&... args) {
- dbg_trace();
static_assert(std::is_base_of<Script, T>::value);
this->script = std::unique_ptr<Script>(new T(std::forward<Args>(args)...));
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 8f84f06..18d6942 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -13,7 +13,7 @@ target_sources(crepe PUBLIC
Animator.cpp
BoxCollider.cpp
CircleCollider.cpp
- LoopManager.cpp
+ Engine.cpp
Asset.cpp
EventHandler.cpp
Script.cpp
@@ -46,7 +46,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
EventHandler.h
EventHandler.hpp
Event.h
- LoopManager.h
+ Engine.h
+ Engine.hpp
Asset.h
Button.h
UIObject.h
diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp
index 179dc18..19a3296 100644
--- a/src/crepe/api/Camera.cpp
+++ b/src/crepe/api/Camera.cpp
@@ -1,4 +1,4 @@
-#include "util/Log.h"
+#include "util/dbg.h"
#include "Camera.h"
#include "Component.h"
diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp
new file mode 100644
index 0000000..bbb4494
--- /dev/null
+++ b/src/crepe/api/Engine.cpp
@@ -0,0 +1,63 @@
+#include "../util/Log.h"
+
+#include "Engine.h"
+
+using namespace crepe;
+using namespace std;
+
+int Engine::main() noexcept {
+ try {
+ this->setup();
+ } catch (const exception & e) {
+ Log::logf(Log::Level::ERROR, "Uncaught exception in setup: {}\n", e.what());
+ return EXIT_FAILURE;
+ }
+
+ try {
+ this->loop();
+ } catch (const exception & e) {
+ Log::logf(Log::Level::ERROR, "Uncaught exception in main loop: {}\n", e.what());
+ this->event_manager.trigger_event<ShutDownEvent>();
+ }
+
+ return EXIT_SUCCESS;
+}
+
+void Engine::setup() {
+ this->loop_timer.start();
+ this->scene_manager.load_next_scene();
+
+ this->event_manager.subscribe<ShutDownEvent>([this](const ShutDownEvent & event) {
+ this->game_running = false;
+
+ // propagate to possible user ShutDownEvent listeners
+ return false;
+ });
+}
+
+void Engine::loop() {
+ LoopTimerManager & timer = this->loop_timer;
+ SystemManager & systems = this->system_manager;
+
+ while (game_running) {
+ timer.update();
+
+ while (timer.get_lag() >= timer.get_fixed_delta_time()) {
+ try {
+ systems.fixed_update();
+ } catch (const exception & e) {
+ Log::logf(Log::Level::WARNING,
+ "Uncaught exception in fixed update function: {}\n", e.what());
+ }
+ timer.advance_fixed_elapsed_time();
+ }
+
+ try {
+ systems.frame_update();
+ } catch (const exception & e) {
+ Log::logf(Log::Level::WARNING, "Uncaught exception in frame update function: {}\n",
+ e.what());
+ }
+ timer.enforce_frame_rate();
+ }
+}
diff --git a/src/crepe/api/Engine.h b/src/crepe/api/Engine.h
new file mode 100644
index 0000000..3145723
--- /dev/null
+++ b/src/crepe/api/Engine.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "../facade/SDLContext.h"
+#include "../manager/ComponentManager.h"
+#include "../manager/EventManager.h"
+#include "../manager/LoopTimerManager.h"
+#include "../manager/ReplayManager.h"
+#include "../manager/ResourceManager.h"
+#include "../manager/SaveManager.h"
+#include "../manager/SceneManager.h"
+#include "../manager/SystemManager.h"
+
+namespace crepe {
+
+/**
+ * \brief Main game entrypoint
+ *
+ * This class is responsible for managing the game loop, including initialization and updating.
+ */
+class Engine {
+public:
+ /**
+ * \brief Engine entrypoint
+ *
+ * This function is called by the game programmer after registering all scenes
+ *
+ * \returns process exit code
+ */
+ int main() noexcept;
+
+ //! \copydoc SceneManager::add_scene
+ template <typename T>
+ void add_scene();
+
+private:
+ /**
+ * \brief Setup function for one-time initialization.
+ *
+ * This function initializes necessary components for the game.
+ */
+ void setup();
+ /**
+ * \brief Main game loop function.
+ *
+ * This function runs the main loop, handling game updates and rendering.
+ */
+ void loop();
+
+ //! Game loop condition
+ bool game_running = true;
+
+private:
+ //! Global context
+ Mediator mediator;
+
+ //! Component manager instance
+ ComponentManager component_manager{mediator};
+ //! Scene manager instance
+ SceneManager scene_manager{mediator};
+ //! LoopTimerManager instance
+ LoopTimerManager loop_timer{mediator};
+ //! EventManager instance
+ EventManager event_manager{mediator};
+ //! Resource manager instance
+ ResourceManager resource_manager{mediator};
+ //! Save manager instance
+ SaveManager save_manager{mediator};
+ //! SDLContext instance
+ SDLContext sdl_context{mediator};
+ //! ReplayManager instance
+ ReplayManager replay_manager{mediator};
+ //! SystemManager
+ SystemManager system_manager{mediator};
+};
+
+} // namespace crepe
+
+#include "Engine.hpp"
diff --git a/src/crepe/api/Engine.hpp b/src/crepe/api/Engine.hpp
new file mode 100644
index 0000000..f2fdc0a
--- /dev/null
+++ b/src/crepe/api/Engine.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Engine.h"
+
+namespace crepe {
+
+template <class T>
+void Engine::add_scene() {
+ this->scene_manager.add_scene<T>();
+}
+
+} // namespace crepe
diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp
index 9ef4682..9b94cad 100644
--- a/src/crepe/api/GameObject.cpp
+++ b/src/crepe/api/GameObject.cpp
@@ -7,20 +7,17 @@
using namespace crepe;
using namespace std;
-GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id,
- const std::string & name, const std::string & tag,
- const vec2 & position, double rotation, double scale)
+GameObject::GameObject(Mediator & mediator, game_object_id_t id, const std::string & name,
+ const std::string & tag, const vec2 & position, double rotation,
+ double scale)
: id(id),
- component_manager(component_manager) {
-
- // Add Transform and Metadata components
- ComponentManager & mgr = this->component_manager;
- mgr.add_component<Transform>(this->id, position, rotation, scale);
- mgr.add_component<Metadata>(this->id, name, tag);
-}
+ mediator(mediator),
+ transform(mediator.component_manager->add_component<Transform>(this->id, position,
+ rotation, scale)),
+ metadata(mediator.component_manager->add_component<Metadata>(this->id, name, tag)) {}
void GameObject::set_parent(const GameObject & parent) {
- ComponentManager & mgr = this->component_manager;
+ ComponentManager & mgr = this->mediator.component_manager;
// Set parent on own Metadata component
RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id);
@@ -32,7 +29,7 @@ void GameObject::set_parent(const GameObject & parent) {
}
void GameObject::set_persistent(bool persistent) {
- ComponentManager & mgr = this->component_manager;
+ ComponentManager & mgr = this->mediator.component_manager;
mgr.set_persistent(this->id, persistent);
}
diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h
index ff80f49..572ce3a 100644
--- a/src/crepe/api/GameObject.h
+++ b/src/crepe/api/GameObject.h
@@ -6,7 +6,9 @@
namespace crepe {
-class ComponentManager;
+class Mediator;
+class Transform;
+class Metadata;
/**
* \brief Represents a GameObject
@@ -20,7 +22,7 @@ private:
* This constructor creates a new GameObject. It creates a new Transform and Metadata
* component and adds them to the ComponentManager.
*
- * \param component_manager Reference to component_manager
+ * \param mediator Reference to mediator
* \param id The id of the GameObject
* \param name The name of the GameObject
* \param tag The tag of the GameObject
@@ -28,13 +30,20 @@ private:
* \param rotation The rotation of the GameObject
* \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 vec2 & position,
- double rotation, double scale);
+ GameObject(Mediator & mediator, game_object_id_t id, const std::string & name,
+ const std::string & tag, const vec2 & position, double rotation, double scale);
//! ComponentManager instances GameObject
friend class ComponentManager;
public:
+ //! The id of the GameObject
+ const game_object_id_t id;
+ //! This entity's transform
+ Transform & transform;
+ //! This entity's metadata
+ Metadata & metadata;
+
+public:
/**
* \brief Set the parent of this GameObject
*
@@ -68,12 +77,8 @@ public:
*/
void set_persistent(bool persistent = true);
-public:
- //! The id of the GameObject
- const game_object_id_t id;
-
protected:
- ComponentManager & component_manager;
+ Mediator & mediator;
};
} // namespace crepe
diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp
index a6b45b0..69f7d73 100644
--- a/src/crepe/api/GameObject.hpp
+++ b/src/crepe/api/GameObject.hpp
@@ -8,7 +8,7 @@ namespace crepe {
template <typename T, typename... Args>
T & GameObject::add_component(Args &&... args) {
- ComponentManager & mgr = this->component_manager;
+ ComponentManager & mgr = this->mediator.component_manager;
return mgr.add_component<T>(this->id, std::forward<Args>(args)...);
}
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
deleted file mode 100644
index b5e5ff7..0000000
--- a/src/crepe/api/LoopManager.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "../facade/SDLContext.h"
-#include "../manager/EventManager.h"
-#include "../manager/LoopTimerManager.h"
-#include "../system/AISystem.h"
-#include "../system/AnimatorSystem.h"
-#include "../system/AudioSystem.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 "../util/Log.h"
-
-#include "LoopManager.h"
-
-using namespace crepe;
-using namespace std;
-
-LoopManager::LoopManager() {
- this->load_system<AnimatorSystem>();
- this->load_system<CollisionSystem>();
- this->load_system<ParticleSystem>();
- this->load_system<PhysicsSystem>();
- this->load_system<RenderSystem>();
- this->load_system<ScriptSystem>();
- this->load_system<InputSystem>();
- this->event_manager.subscribe<ShutDownEvent>(
- [this](const ShutDownEvent & event) { return this->on_shutdown(event); });
- this->load_system<AudioSystem>();
- this->load_system<AISystem>();
-}
-void LoopManager::start() {
- this->setup();
- this->loop();
-}
-
-void LoopManager::setup() {
- this->game_running = true;
- this->loop_timer.start();
- this->scene_manager.load_next_scene();
-}
-
-void LoopManager::loop() {
- try {
- while (game_running) {
- this->loop_timer.update();
-
- while (this->loop_timer.get_lag() >= this->loop_timer.get_fixed_delta_time()) {
- this->fixed_update();
- this->loop_timer.advance_fixed_elapsed_time();
- }
-
- this->frame_update();
- this->loop_timer.enforce_frame_rate();
- }
- } catch (const exception & e) {
- Log::logf(Log::Level::ERROR, "Exception caught in main loop: {}", e.what());
- this->event_manager.trigger_event<ShutDownEvent>(ShutDownEvent{});
- }
-}
-
-// will be called at a fixed interval
-void LoopManager::fixed_update() {
- this->get_system<InputSystem>().update();
- this->event_manager.dispatch_events();
- this->get_system<ScriptSystem>().update();
- this->get_system<AISystem>().update();
- this->get_system<PhysicsSystem>().update();
- this->get_system<CollisionSystem>().update();
- this->get_system<AudioSystem>().update();
-}
-
-// will be called every frame
-void LoopManager::frame_update() {
- this->scene_manager.load_next_scene();
- this->get_system<AnimatorSystem>().update();
- //render
- this->get_system<RenderSystem>().update();
-}
-
-bool LoopManager::on_shutdown(const ShutDownEvent & e) {
- this->game_running = false;
- // propagate to possible user ShutDownEvent listeners
- return false;
-}
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
deleted file mode 100644
index 40e6b38..0000000
--- a/src/crepe/api/LoopManager.h
+++ /dev/null
@@ -1,122 +0,0 @@
-#pragma once
-
-#include <memory>
-
-#include "../facade/SDLContext.h"
-#include "../manager/ComponentManager.h"
-#include "../manager/EventManager.h"
-#include "../manager/LoopTimerManager.h"
-#include "../manager/Mediator.h"
-#include "../manager/ResourceManager.h"
-#include "../manager/SaveManager.h"
-#include "../manager/SceneManager.h"
-#include "../system/System.h"
-
-namespace crepe {
-/**
- * \brief Main game loop manager
- *
- * This class is responsible for managing the game loop, including initialization and updating.
- */
-class LoopManager {
-public:
- LoopManager();
- /**
- * \brief Start the gameloop
- *
- * This is the start of the engine where the setup is called and then the loop keeps running until the game stops running.
- * The Game programmer needs to call this function to run the game. This should be done after creating and adding all scenes.
- */
- void start();
-
- /**
- * \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.
- *
- * This function initializes necessary components for the game.
- */
- void setup();
- /**
- * \brief Main game loop function.
- *
- * This function runs the main loop, handling game updates and rendering.
- */
- void loop();
-
- /**
- * \brief Per-frame update.
- *
- * Updates the game state based on the elapsed time since the last frame.
- */
- virtual void frame_update();
-
- /**
- * \brief Fixed update executed at a fixed rate.
- *
- * This function updates physics and game logic based on LoopTimer's fixed_delta_time.
- */
- virtual void fixed_update();
-
- //! Indicates whether the game is running.
- bool game_running = false;
-
-private:
- //! Global context
- Mediator mediator;
-
- //! Component manager instance
- ComponentManager component_manager{mediator};
- //! Scene manager instance
- SceneManager scene_manager{mediator};
- //! LoopTimerManager instance
- LoopTimerManager loop_timer{mediator};
- //! EventManager instance
- EventManager event_manager{mediator};
- //! Resource manager instance
- ResourceManager resource_manager{mediator};
- //! Save manager instance
- SaveManager save_manager{mediator};
- //! SDLContext instance
- SDLContext sdl_context{mediator};
-
-private:
- /**
- * \brief Callback function for ShutDownEvent
- *
- * This function sets the game_running variable to false, stopping the gameloop and therefor quitting the game.
- */
- bool on_shutdown(const ShutDownEvent & e);
- /**
- * \brief Collection of System instances
- *
- * This map holds System instances indexed by the system's class typeid. It is filled in the
- * constructor of 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
-
-#include "LoopManager.hpp"
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 90b77a0..a9c5cf6 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -1,6 +1,7 @@
#include "ParticleEmitter.h"
using namespace crepe;
+using namespace std;
ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & data)
: Component(game_object_id),
@@ -9,3 +10,16 @@ ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & d
this->data.particles.emplace_back();
}
}
+
+unique_ptr<Component> ParticleEmitter::save() const {
+ return unique_ptr<Component>{new ParticleEmitter(*this)};
+}
+
+void ParticleEmitter::restore(const Component & snapshot) {
+ *this = static_cast<const ParticleEmitter &>(snapshot);
+}
+
+ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter & other) {
+ data.particles = other.data.particles;
+ return *this;
+}
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index b83fd61..5f563de 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -80,6 +80,12 @@ public:
public:
//! Configuration data for particle emission settings.
Data data;
+
+protected:
+ virtual std::unique_ptr<Component> save() const;
+ ParticleEmitter(const ParticleEmitter &) = default;
+ virtual void restore(const Component & snapshot);
+ virtual ParticleEmitter & operator=(const ParticleEmitter &);
};
} // namespace crepe
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index 753a9e3..34e7908 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -25,3 +25,23 @@ void Script::set_next_scene(const string & name) {
}
SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; }
+
+void Script::replay::record_start() {
+ ReplayManager & mgr = this->mediator->replay_manager;
+ return mgr.record_start();
+}
+
+recording_t Script::replay::record_end() {
+ ReplayManager & mgr = this->mediator->replay_manager;
+ return mgr.record_end();
+}
+
+void Script::replay::play(recording_t recording) {
+ ReplayManager & mgr = this->mediator->replay_manager;
+ return mgr.play(recording);
+}
+
+void Script::replay::release(recording_t recording) {
+ ReplayManager & mgr = this->mediator->replay_manager;
+ return mgr.release(recording);
+}
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index 668e5d1..6536fa4 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -4,8 +4,10 @@
#include "../manager/EventManager.h"
#include "../manager/Mediator.h"
+#include "../manager/ReplayManager.h"
#include "../system/CollisionSystem.h"
#include "../types.h"
+#include "../util/Log.h"
#include "../util/OptionalRef.h"
namespace crepe {
@@ -57,84 +59,104 @@ protected:
protected:
/**
- * \name Utility functions
+ * \name Component query functions
+ * \see ComponentManager
* \{
*/
-
/**
* \brief Get single component of type \c T on this game object
- *
* \tparam T Type of component
- *
* \returns Reference to component
- *
* \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
- *
* \tparam T Type of component
- *
* \returns List of component references
*/
template <typename T>
RefVector<T> get_components() const;
-
- /**
- * \copydoc ComponentManager::get_components_by_id
- * \see ComponentManager::get_components_by_id
- */
+ //! \copydoc ComponentManager::get_components_by_id
template <typename T>
RefVector<T> get_components_by_id(game_object_id_t id) const;
- /**
- * \copydoc ComponentManager::get_components_by_name
- * \see ComponentManager::get_components_by_name
- */
+ //! \copydoc ComponentManager::get_components_by_name
template <typename T>
RefVector<T> get_components_by_name(const std::string & name) const;
- /**
- * \copydoc ComponentManager::get_components_by_tag
- * \see ComponentManager::get_components_by_tag
- */
+ //! \copydoc ComponentManager::get_components_by_tag
template <typename T>
RefVector<T> get_components_by_tag(const std::string & tag) const;
+ //! \}
/**
- * \brief Log a message using Log::logf
- *
- * \tparam Args Log::logf parameters
- * \param args Log::logf parameters
+ * \name Logging functions
+ * \see Log
+ * \{
*/
- template <typename... Args>
- void logf(Args &&... args);
+ //! \copydoc Log::logf
+ template <class... Args>
+ void logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args);
+ //! \copydoc Log::logf
+ template <class... Args>
+ void logf(std::format_string<Args...> fmt, Args &&... args);
+ // \}
/**
- * \brief Subscribe to an event with an explicit channel
- * \see EventManager::subscribe
+ * \name Event manager functions
+ * \see EventManager
+ * \{
*/
+ //! \copydoc 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
- */
+ //! \copydoc EventManager::subscribe
template <typename EventType>
void subscribe(const EventHandler<EventType> & callback);
+ //! \copydoc EventManager::trigger_event
+ template <typename EventType>
+ void trigger_event(const EventType & event = {},
+ event_channel_t channel = EventManager::CHANNEL_ALL);
+ //! \copydoc EventManager::queue_event
+ template <typename EventType>
+ void queue_event(const EventType & event = {},
+ event_channel_t channel = EventManager::CHANNEL_ALL);
+ //! \}
/**
- * \brief Set the next scene using SceneManager
- * \see SceneManager::set_next_scene
+ * \name Scene-related functions
+ * \see SceneManager
+ * \{
*/
+ //! \copydoc SceneManager::set_next_scene
void set_next_scene(const std::string & name);
+ //! \}
+ /**
+ * \name Save data management functions
+ * \see SaveManager
+ * \{
+ */
//! Retrieve SaveManager reference
SaveManager & get_save_manager() const;
+ //! \}
+ //! Replay management functions
+ struct replay { // NOLINT
+ //! \copydoc ReplayManager::record_start
+ void record_start();
+ //! \copydoc ReplayManager::record_end
+ recording_t record_end();
+ //! \copydoc ReplayManager::play
+ void play(recording_t);
+ //! \copydoc ReplayManager::release
+ void release(recording_t);
+
+ private:
+ OptionalRef<Mediator> & mediator;
+ replay(OptionalRef<Mediator> & mediator) : mediator(mediator) {}
+ friend class Script;
+ } replay{mediator};
//! \}
private:
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index 225a51c..4462a41 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "../manager/ComponentManager.h"
+#include "../manager/ReplayManager.h"
#include "BehaviorScript.h"
#include "Script.h"
@@ -23,9 +24,14 @@ RefVector<T> Script::get_components() const {
return this->get_components_by_id<T>(this->game_object_id);
}
-template <typename... Args>
-void Script::logf(Args &&... args) {
- Log::logf(std::forward<Args>(args)...);
+template <class... Args>
+void Script::logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args) {
+ Log::logf(level, fmt, std::forward<Args>(args)...);
+}
+
+template <class... Args>
+void Script::logf(std::format_string<Args...> fmt, Args &&... args) {
+ Log::logf(fmt, std::forward<Args>(args)...);
}
template <typename EventType>
@@ -34,8 +40,18 @@ void Script::subscribe_internal(const EventHandler<EventType> & callback,
EventManager & mgr = this->mediator->event_manager;
subscription_t listener = mgr.subscribe<EventType>(
[this, callback](const EventType & data) -> bool {
+ // check if (parent) BehaviorScript component is active
bool & active = this->active;
if (!active) return false;
+
+ // check if replay manager is playing (if initialized)
+ try {
+ ReplayManager & replay = this->mediator->replay_manager;
+ if (replay.get_state() == ReplayManager::PLAYING) return false;
+ } catch (const std::runtime_error &) {
+ }
+
+ // call user-provided callback
return callback(data);
},
channel);
@@ -52,6 +68,18 @@ void Script::subscribe(const EventHandler<EventType> & callback) {
this->subscribe_internal(callback, EventManager::CHANNEL_ALL);
}
+template <typename EventType>
+void Script::trigger_event(const EventType & event, event_channel_t channel) {
+ EventManager & mgr = this->mediator->event_manager;
+ mgr.trigger_event(event, channel);
+}
+
+template <typename EventType>
+void Script::queue_event(const EventType & event, event_channel_t channel) {
+ EventManager & mgr = this->mediator->event_manager;
+ mgr.queue_event(event, channel);
+}
+
template <typename T>
RefVector<T> Script::get_components_by_id(game_object_id_t id) const {
Mediator & mediator = this->mediator;
diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp
index ba684ba..0107c7b 100644
--- a/src/crepe/api/Sprite.cpp
+++ b/src/crepe/api/Sprite.cpp
@@ -1,6 +1,6 @@
#include <cmath>
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "api/Asset.h"
#include "Component.h"
diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp
index a85b792..fcfce14 100644
--- a/src/crepe/api/Transform.cpp
+++ b/src/crepe/api/Transform.cpp
@@ -1,8 +1,9 @@
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "Transform.h"
using namespace crepe;
+using namespace std;
Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale)
: Component(id),
@@ -11,3 +12,11 @@ Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, d
scale(scale) {
dbg_trace();
}
+
+unique_ptr<Component> Transform::save() const {
+ return unique_ptr<Component>{new Transform(*this)};
+}
+
+void Transform::restore(const Component & snapshot) {
+ *this = static_cast<const Transform &>(snapshot);
+}
diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h
index 7ee6d65..a6f3486 100644
--- a/src/crepe/api/Transform.h
+++ b/src/crepe/api/Transform.h
@@ -35,6 +35,12 @@ protected:
virtual int get_instances_max() const { return 1; }
//! ComponentManager instantiates all components
friend class ComponentManager;
+
+protected:
+ virtual std::unique_ptr<Component> save() const;
+ Transform(const Transform &) = default;
+ virtual void restore(const Component & snapshot);
+ virtual Transform & operator=(const Transform &) = default;
};
} // namespace crepe
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index 95cf606..d45cd82 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -1,6 +1,6 @@
#include <cstring>
-#include "util/Log.h"
+#include "util/dbg.h"
#include "DB.h"
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 20bb030..fccc15f 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -19,7 +19,7 @@
#include "../api/Color.h"
#include "../api/Config.h"
#include "../api/Sprite.h"
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "manager/Mediator.h"
#include "SDLContext.h"
diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp
index 97e455e..b1e6463 100644
--- a/src/crepe/facade/Sound.cpp
+++ b/src/crepe/facade/Sound.cpp
@@ -1,5 +1,5 @@
#include "../api/Asset.h"
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "Sound.h"
diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp
index b1f8cb3..5091e07 100644
--- a/src/crepe/facade/SoundContext.cpp
+++ b/src/crepe/facade/SoundContext.cpp
@@ -1,4 +1,4 @@
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "SoundContext.h"
diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp
index b63403d..23a9c8e 100644
--- a/src/crepe/facade/Texture.cpp
+++ b/src/crepe/facade/Texture.cpp
@@ -1,4 +1,4 @@
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "facade/SDLContext.h"
#include "manager/Mediator.h"
diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt
index f73e165..48e444f 100644
--- a/src/crepe/manager/CMakeLists.txt
+++ b/src/crepe/manager/CMakeLists.txt
@@ -6,6 +6,8 @@ target_sources(crepe PUBLIC
SceneManager.cpp
LoopTimerManager.cpp
ResourceManager.cpp
+ ReplayManager.cpp
+ SystemManager.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -21,5 +23,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
LoopTimerManager.h
ResourceManager.h
ResourceManager.hpp
+ ReplayManager.h
+ SystemManager.h
+ SystemManager.hpp
)
diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp
index df30d27..745ddae 100644
--- a/src/crepe/manager/ComponentManager.cpp
+++ b/src/crepe/manager/ComponentManager.cpp
@@ -1,7 +1,7 @@
#include "../api/GameObject.h"
#include "../api/Metadata.h"
#include "../types.h"
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "ComponentManager.h"
@@ -53,7 +53,7 @@ GameObject ComponentManager::new_object(const string & name, const string & tag,
this->next_id++;
}
- GameObject object{*this, this->next_id, name, tag, position, rotation, scale};
+ GameObject object{this->mediator, this->next_id, name, tag, position, rotation, scale};
this->next_id++;
return object;
@@ -72,3 +72,28 @@ set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) c
return this->get_objects_by_predicate<Metadata>(
[tag](const Metadata & data) { return data.tag == tag; });
}
+
+ComponentManager::Snapshot ComponentManager::save() {
+ Snapshot snapshot{};
+ for (const auto & [type, by_id_index] : this->components) {
+ for (game_object_id_t id = 0; id < by_id_index.size(); id++) {
+ const auto & components = by_id_index[id];
+ for (size_t index = 0; index < components.size(); index++) {
+ const Component & component = *components[index];
+ snapshot.components.push_back(SnapshotComponent{
+ .type = type,
+ .id = id,
+ .index = index,
+ .component = component.save(),
+ });
+ }
+ }
+ }
+ return snapshot;
+}
+
+void ComponentManager::restore(const Snapshot & snapshot) {
+ for (const SnapshotComponent & info : snapshot.components) {
+ this->components[info.type][info.id][info.index]->restore(*info.component);
+ }
+}
diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h
index 19a8e81..c3a5b4a 100644
--- a/src/crepe/manager/ComponentManager.h
+++ b/src/crepe/manager/ComponentManager.h
@@ -21,13 +21,6 @@ class GameObject;
* This class manages all components. It provides methods to add, delete and get components.
*/
class ComponentManager : public Manager {
- // TODO: This relation should be removed! I (loek) believe that the scene manager should
- // create/destroy components because the GameObject's are stored in concrete Scene classes,
- // which will in turn call GameObject's destructor, which will in turn call
- // ComponentManager::delete_components_by_id or something. This is a pretty major change, so
- // here is a comment and temporary fix instead :tada:
- friend class SceneManager;
-
public:
ComponentManager(Mediator & mediator);
~ComponentManager(); // dbg_trace
@@ -49,12 +42,7 @@ public:
const vec2 & position = {0, 0}, double rotation = 0,
double scale = 1);
-protected:
- /**
- * GameObject is used as an interface to add/remove components, and the game programmer is
- * supposed to use it instead of interfacing with the component manager directly.
- */
- friend class GameObject;
+public:
/**
* \brief Add a component to the ComponentManager
*
@@ -154,6 +142,40 @@ public:
template <typename T>
RefVector<T> get_components_by_tag(const std::string & tag) const;
+ //! Snapshot of single component (including path in \c components)
+ struct SnapshotComponent {
+ //! \c components path
+ std::type_index type;
+ //! \c components path
+ game_object_id_t id;
+ //! \c components path
+ size_t index;
+ //! Actual component snapshot
+ std::unique_ptr<Component> component;
+ };
+ //! Snapshot of the entire component manager state
+ struct Snapshot {
+ //! All components
+ std::vector<SnapshotComponent> components;
+ // TODO: some kind of hash code that ensures components exist in all the same places as
+ // this snapshot
+ };
+ /**
+ * \name ReplayManager (Memento) functions
+ * \{
+ */
+ /**
+ * \brief Save a snapshot of the component manager state
+ * \returns Deep copy of the component manager's internal state
+ */
+ Snapshot save();
+ /**
+ * \brief Restore component manager from a snapshot
+ * \param snapshot Snapshot to restore from (as returned by \c save())
+ */
+ void restore(const Snapshot & snapshot);
+ //! \}
+
private:
/**
* \brief Get object IDs by predicate function
diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp
index a6e4788..e78f92f 100644
--- a/src/crepe/manager/LoopTimerManager.cpp
+++ b/src/crepe/manager/LoopTimerManager.cpp
@@ -1,7 +1,7 @@
#include <chrono>
#include <thread>
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "LoopTimerManager.h"
diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h
index 76b02d3..2f1e6b6 100644
--- a/src/crepe/manager/LoopTimerManager.h
+++ b/src/crepe/manager/LoopTimerManager.h
@@ -6,6 +6,8 @@
namespace crepe {
+class Engine;
+
typedef std::chrono::duration<float> duration_t;
typedef std::chrono::duration<unsigned long long, std::micro> elapsed_time_t;
@@ -107,7 +109,7 @@ public:
private:
//! Friend relation to use start,enforce_frame_rate,get_lag,update,advance_fixed_update.
- friend class LoopManager;
+ friend class Engine;
/**
* \brief Start the loop timer.
*
diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h
index a336410..842f1de 100644
--- a/src/crepe/manager/Mediator.h
+++ b/src/crepe/manager/Mediator.h
@@ -11,6 +11,8 @@ class LoopTimerManager;
class SaveManager;
class ResourceManager;
class SDLContext;
+class ReplayManager;
+class SystemManager;
/**
* Struct to pass references to classes that would otherwise need to be singletons down to
@@ -32,6 +34,8 @@ struct Mediator {
OptionalRef<LoopTimerManager> loop_timer;
OptionalRef<SaveManager> save_manager;
OptionalRef<ResourceManager> resource_manager;
+ OptionalRef<ReplayManager> replay_manager;
+ OptionalRef<SystemManager> system_manager;
};
} // namespace crepe
diff --git a/src/crepe/manager/ReplayManager.cpp b/src/crepe/manager/ReplayManager.cpp
new file mode 100644
index 0000000..090a94e
--- /dev/null
+++ b/src/crepe/manager/ReplayManager.cpp
@@ -0,0 +1,70 @@
+#include <format>
+
+#include "Manager.h"
+#include "ReplayManager.h"
+
+using namespace crepe;
+using namespace std;
+
+ReplayManager::ReplayManager(Mediator & mediator) : Manager(mediator) {
+ mediator.replay_manager = *this;
+}
+
+void ReplayManager::record_start() {
+ if (this->state == RECORDING) this->release(this->id);
+ this->id++;
+ this->memory[this->id] = make_unique<Recording>();
+ this->recording = *this->memory.at(this->id);
+ this->state = RECORDING;
+}
+
+recording_t ReplayManager::record_end() {
+ this->state = IDLE;
+ return this->id;
+}
+
+void ReplayManager::play(recording_t handle) {
+ if (!this->memory.contains(handle))
+ throw out_of_range(format("ReplayManager: no recording for handle {}", handle));
+ this->recording = *this->memory.at(handle);
+ this->recording->frame = 0;
+ this->state = PLAYING;
+}
+
+void ReplayManager::release(recording_t handle) {
+ if (!this->memory.contains(handle)) return;
+ this->memory.erase(handle);
+}
+
+void ReplayManager::frame_record() {
+ if (this->state != RECORDING)
+ throw runtime_error("ReplayManager: frame_step called while not playing");
+
+ ComponentManager & components = this->mediator.component_manager;
+ Recording & recording = this->recording;
+
+ recording.frames.push_back(components.save());
+ recording.frame++;
+}
+
+bool ReplayManager::frame_step() {
+ if (this->state != PLAYING)
+ throw runtime_error("ReplayManager: frame_step called while not playing");
+
+ ComponentManager & components = this->mediator.component_manager;
+ Recording & recording = this->recording;
+
+ ComponentManager::Snapshot & frame = recording.frames.at(recording.frame);
+
+ components.restore(frame);
+ recording.frame++;
+
+ if (recording.frame < recording.frames.size()) return false;
+ // end of recording
+ recording.frame = 0;
+ this->state = IDLE;
+ this->recording.clear();
+ return true;
+}
+
+ReplayManager::State ReplayManager::get_state() const { return this->state; }
diff --git a/src/crepe/manager/ReplayManager.h b/src/crepe/manager/ReplayManager.h
new file mode 100644
index 0000000..ab15b27
--- /dev/null
+++ b/src/crepe/manager/ReplayManager.h
@@ -0,0 +1,94 @@
+#pragma once
+
+#include <unordered_map>
+
+#include "ComponentManager.h"
+#include "Manager.h"
+#include "util/OptionalRef.h"
+
+namespace crepe {
+
+typedef size_t recording_t;
+
+/**
+ * \brief Replay manager
+ *
+ * The replay manager is responsible for creating, storing and restoring ComponentManager
+ * snapshots. Sequential snapshots can be recorded and replayed in combination with
+ * ReplaySystem.
+ */
+class ReplayManager : public Manager {
+ // TODO: Delete recordings at end of scene
+
+public:
+ ReplayManager(Mediator & mediator);
+
+public:
+ //! Start a new recording
+ void record_start();
+ /**
+ * \brief End the latest recording started by \c record_start()
+ * \returns Handle to recording
+ */
+ recording_t record_end();
+ /**
+ * \brief Play a recording
+ * \param handle Handle to recording (as returned by \c record_end())
+ */
+ void play(recording_t handle);
+ /**
+ * \brief Delete a recording from memory
+ * \param handle Handle to recording (as returned by \c record_end())
+ */
+ void release(recording_t handle);
+
+public:
+ //! Internal state
+ enum State {
+ IDLE, //!< Not doing anything
+ RECORDING, //!< Currently recording
+ PLAYING, //!< Currently playing back a recording
+ };
+ //! Get current internal state
+ State get_state() const;
+
+public:
+ /**
+ * \brief Record a single frame to the current recording
+ *
+ * This function is called by ReplaySystem after the game programmer has called \c
+ * record_start()
+ */
+ void frame_record();
+ /**
+ * \brief Play the next frame of the current recording
+ *
+ * \returns `true` if the recording is finished playing
+ * \returns `false` if there are more frames
+ *
+ * This function also automatically resets the internal state from PLAYING to IDLE at the end
+ * of a recording.
+ */
+ bool frame_step();
+
+private:
+ /**
+ * \brief Recording data
+ */
+ struct Recording {
+ //! Current frame being shown
+ size_t frame = 0;
+ //! All frames in recording
+ std::vector<ComponentManager::Snapshot> frames;
+ };
+ //! Internal state
+ State state = IDLE;
+ //! Current recording handle
+ recording_t id = -1;
+ //! Current recording data
+ OptionalRef<Recording> recording;
+ //! Recording storage
+ std::unordered_map<recording_t, std::unique_ptr<Recording>> memory;
+};
+
+} // namespace crepe
diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp
index a141a46..5713183 100644
--- a/src/crepe/manager/ResourceManager.cpp
+++ b/src/crepe/manager/ResourceManager.cpp
@@ -1,4 +1,4 @@
-#include "util/Log.h"
+#include "util/dbg.h"
#include "ResourceManager.h"
diff --git a/src/crepe/manager/SystemManager.cpp b/src/crepe/manager/SystemManager.cpp
new file mode 100644
index 0000000..5ada30f
--- /dev/null
+++ b/src/crepe/manager/SystemManager.cpp
@@ -0,0 +1,66 @@
+#include "../system/AISystem.h"
+#include "../system/AnimatorSystem.h"
+#include "../system/AudioSystem.h"
+#include "../system/CollisionSystem.h"
+#include "../system/EventSystem.h"
+#include "../system/InputSystem.h"
+#include "../system/ParticleSystem.h"
+#include "../system/PhysicsSystem.h"
+#include "../system/RenderSystem.h"
+#include "../system/ReplaySystem.h"
+#include "../system/ScriptSystem.h"
+
+#include "SystemManager.h"
+
+using namespace crepe;
+using namespace std;
+
+SystemManager::SystemManager(Mediator & mediator) : Manager(mediator) {
+ this->load_system<InputSystem>();
+ this->load_system<EventSystem>();
+ this->load_system<ScriptSystem>();
+ this->load_system<AISystem>();
+ this->load_system<PhysicsSystem>();
+ this->load_system<CollisionSystem>();
+ this->load_system<AudioSystem>();
+ this->load_system<AnimatorSystem>();
+ this->load_system<ParticleSystem>();
+ this->load_system<RenderSystem>();
+ this->load_system<ReplaySystem>();
+
+ this->mediator.system_manager = *this;
+}
+
+void SystemManager::fixed_update() {
+ for (auto & [type, system] : this->systems) {
+ if (!system->active) continue;
+ system->fixed_update();
+ }
+}
+
+void SystemManager::frame_update() {
+ for (auto & [type, system] : this->systems) {
+ if (!system->active) continue;
+ system->frame_update();
+ }
+}
+
+SystemManager::Snapshot SystemManager::save() {
+ Snapshot snapshot;
+ for (auto & [type, system] : this->systems) {
+ snapshot[type] = system->active;
+ }
+ return snapshot;
+}
+
+void SystemManager::restore(const Snapshot & snapshot) {
+ for (auto & [type, active] : snapshot) {
+ this->systems[type]->active = active;
+ }
+}
+
+void SystemManager::disable_all() {
+ for (auto & [type, system] : this->systems) {
+ system->active = false;
+ }
+}
diff --git a/src/crepe/manager/SystemManager.h b/src/crepe/manager/SystemManager.h
new file mode 100644
index 0000000..50acf77
--- /dev/null
+++ b/src/crepe/manager/SystemManager.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+
+#include "../system/System.h"
+
+#include "Manager.h"
+
+namespace crepe {
+
+/**
+ * \brief Collection of all systems
+ *
+ * This manager aggregates all systems and provides utility functions to retrieve references to
+ * and update systems.
+ */
+class SystemManager : public Manager {
+public:
+ SystemManager(Mediator &);
+
+ /**
+ * \brief Per-frame update.
+ *
+ * Updates the game state based on the elapsed time since the last frame.
+ */
+ void frame_update();
+
+ /**
+ * \brief Fixed update executed at a fixed rate.
+ *
+ * This function updates physics and game logic based on LoopTimer's fixed_delta_time.
+ */
+ void fixed_update();
+
+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 SystemManager using SystemManager::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();
+
+public:
+ /**
+ * \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();
+
+public:
+ /**
+ * \brief SystemManager snapshot
+ *
+ * The SystemManager snapshot only stores which systems are active
+ */
+ typedef std::unordered_map<std::type_index, bool> Snapshot;
+ /**
+ * \brief Save a snapshot of the systems' state
+ * \returns Copy of each system's active property
+ */
+ Snapshot save();
+ /**
+ * \brief Restore system active state from a snapshot
+ * \param snapshot Snapshot to restore from (as returned by \c save())
+ */
+ void restore(const Snapshot & snapshot);
+ //! Disable all systems
+ void disable_all();
+};
+
+} // namespace crepe
+
+#include "SystemManager.hpp"
diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/manager/SystemManager.hpp
index 266758a..3d26e4c 100644
--- a/src/crepe/api/LoopManager.hpp
+++ b/src/crepe/manager/SystemManager.hpp
@@ -4,26 +4,19 @@
#include <format>
#include <memory>
-#include "../system/System.h"
-
-#include "LoopManager.h"
+#include "SystemManager.h"
namespace crepe {
template <class T>
-void LoopManager::add_scene() {
- this->scene_manager.add_scene<T>();
-}
-
-template <class T>
-T & LoopManager::get_system() {
+T & SystemManager::get_system() {
using namespace std;
static_assert(is_base_of<System, T>::value,
"get_system must recieve a derivative class of System");
const type_info & type = typeid(T);
if (!this->systems.contains(type))
- throw runtime_error(format("LoopManager: {} is not initialized", type.name()));
+ throw runtime_error(format("SystemManager: {} is not initialized", type.name()));
System * system = this->systems.at(type).get();
T * concrete_system = dynamic_cast<T *>(system);
@@ -33,14 +26,14 @@ T & LoopManager::get_system() {
}
template <class T>
-void LoopManager::load_system() {
+void SystemManager::load_system() {
using namespace std;
static_assert(is_base_of<System, T>::value,
"load_system must recieve a derivative class of System");
const type_info & type = typeid(T);
if (this->systems.contains(type))
- throw runtime_error(format("LoopManager: {} is already initialized", type.name()));
+ throw runtime_error(format("SystemManager: {} is already initialized", type.name()));
System * system = new T(this->mediator);
this->systems[type] = unique_ptr<System>(system);
}
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 680dbb8..0f35010 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -10,7 +10,7 @@
using namespace crepe;
using namespace std::chrono;
-void AISystem::update() {
+void AISystem::fixed_update() {
const Mediator & mediator = this->mediator;
ComponentManager & mgr = mediator.component_manager;
LoopTimerManager & loop_timer = mediator.loop_timer;
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index d5f8a8e..04807cf 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -20,7 +20,7 @@ public:
using System::System;
//! Update the AI system
- void update() override;
+ void fixed_update() override;
private:
/**
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index 107b25d..e5ab2fa 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,5 +1,3 @@
-
-
#include "../api/Animator.h"
#include "../manager/ComponentManager.h"
#include "../manager/LoopTimerManager.h"
@@ -10,7 +8,7 @@
using namespace crepe;
using namespace std::chrono;
-void AnimatorSystem::update() {
+void AnimatorSystem::frame_update() {
ComponentManager & mgr = this->mediator.component_manager;
LoopTimerManager & timer = this->mediator.loop_timer;
RefVector<Animator> animations = mgr.get_components_by_type<Animator>();
diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h
index 7d3f565..092e131 100644
--- a/src/crepe/system/AnimatorSystem.h
+++ b/src/crepe/system/AnimatorSystem.h
@@ -22,7 +22,7 @@ public:
* Animator components, moving the animations forward and managing their behavior (e.g.,
* looping).
*/
- void update() override;
+ void frame_update() override;
};
} // namespace crepe
diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp
index b1aa0f8..d4e8b9f 100644
--- a/src/crepe/system/AudioSystem.cpp
+++ b/src/crepe/system/AudioSystem.cpp
@@ -7,7 +7,7 @@
using namespace crepe;
using namespace std;
-void AudioSystem::update() {
+void AudioSystem::fixed_update() {
ComponentManager & component_manager = this->mediator.component_manager;
ResourceManager & resource_manager = this->mediator.resource_manager;
RefVector<AudioSource> components
diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h
index 2ddc443..56fc98c 100644
--- a/src/crepe/system/AudioSystem.h
+++ b/src/crepe/system/AudioSystem.h
@@ -11,7 +11,7 @@ namespace crepe {
class AudioSystem : public System {
public:
using System::System;
- void update() override;
+ void fixed_update() override;
private:
/**
diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt
index 0e2db76..52369d0 100644
--- a/src/crepe/system/CMakeLists.txt
+++ b/src/crepe/system/CMakeLists.txt
@@ -8,6 +8,8 @@ target_sources(crepe PUBLIC
AudioSystem.cpp
AnimatorSystem.cpp
InputSystem.cpp
+ EventSystem.cpp
+ ReplaySystem.cpp
AISystem.cpp
)
@@ -20,5 +22,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
AudioSystem.h
AnimatorSystem.h
InputSystem.h
+ EventSystem.h
+ ReplaySystem.h
AISystem.h
)
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index 496224e..9178e12 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -23,7 +23,7 @@
using namespace crepe;
-void CollisionSystem::update() {
+void CollisionSystem::fixed_update() {
std::vector<CollisionInternal> all_colliders;
game_object_id_t id = 0;
ComponentManager & mgr = this->mediator.component_manager;
diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h
index 5b136c6..48a8f86 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -85,7 +85,7 @@ public:
public:
//! Updates the collision system by checking for collisions between colliders and handling them.
- void update() override;
+ void fixed_update() override;
private:
/**
diff --git a/src/crepe/system/EventSystem.cpp b/src/crepe/system/EventSystem.cpp
new file mode 100644
index 0000000..7e168ab
--- /dev/null
+++ b/src/crepe/system/EventSystem.cpp
@@ -0,0 +1,9 @@
+#include "EventSystem.h"
+#include "../manager/EventManager.h"
+
+using namespace crepe;
+
+void EventSystem::fixed_update() {
+ EventManager & ev = this->mediator.event_manager;
+ ev.dispatch_events();
+}
diff --git a/src/crepe/system/EventSystem.h b/src/crepe/system/EventSystem.h
new file mode 100644
index 0000000..0ae48d2
--- /dev/null
+++ b/src/crepe/system/EventSystem.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "System.h"
+
+namespace crepe {
+
+/**
+ * \brief EventManager dispatch helper system
+ */
+class EventSystem : public System {
+public:
+ using System::System;
+
+ /**
+ * \brief Dispatch queued events
+ * \see EventManager::dispatch_events
+ */
+ void fixed_update() override;
+};
+
+} // namespace crepe
diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp
index a710ae2..7796b47 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -8,7 +8,7 @@
using namespace crepe;
-void InputSystem::update() {
+void InputSystem::fixed_update() {
ComponentManager & mgr = this->mediator.component_manager;
EventManager & event_mgr = this->mediator.event_manager;
SDLContext & context = this->mediator.sdl_context;
diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h
index 87e86f8..62b0fcd 100644
--- a/src/crepe/system/InputSystem.h
+++ b/src/crepe/system/InputSystem.h
@@ -27,7 +27,7 @@ public:
* \brief Updates the system, processing all input events.
* This method processes all events and triggers corresponding actions.
*/
- void update() override;
+ void fixed_update() override;
private:
//! Stores the last position of the mouse when the button was pressed.
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index b14c52f..5ccd128 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -10,7 +10,7 @@
using namespace crepe;
-void ParticleSystem::update() {
+void ParticleSystem::frame_update() {
// Get all emitters
ComponentManager & mgr = this->mediator.component_manager;
RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>();
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 068f01c..6c631ea 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -20,7 +20,7 @@ public:
* \brief Updates all particle emitters by emitting particles, updating particle states, and
* checking bounds.
*/
- void update() override;
+ void frame_update() override;
private:
/**
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index 3b3b8ab..62f8132 100644
--- a/src/crepe/system/PhysicsSystem.cpp
+++ b/src/crepe/system/PhysicsSystem.cpp
@@ -12,8 +12,7 @@
using namespace crepe;
-void PhysicsSystem::update() {
-
+void PhysicsSystem::fixed_update() {
const Mediator & mediator = this->mediator;
ComponentManager & mgr = mediator.component_manager;
LoopTimerManager & loop_timer = mediator.loop_timer;
diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h
index 26152a5..5ed624f 100644
--- a/src/crepe/system/PhysicsSystem.h
+++ b/src/crepe/system/PhysicsSystem.h
@@ -18,7 +18,7 @@ public:
*
* It calculates new velocties and changes the postion in the transform.
*/
- void update() override;
+ void fixed_update() override;
};
} // namespace crepe
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index afd9548..607bbab 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -64,7 +64,7 @@ RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const {
return sorted_objs;
}
-void RenderSystem::update() {
+void RenderSystem::frame_update() {
this->clear_screen();
this->render();
this->present_screen();
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index fc7b46e..1a61f99 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -24,7 +24,7 @@ public:
* \brief Updates the RenderSystem for the current frame.
* This method is called to perform all rendering operations for the current game frame.
*/
- void update() override;
+ void frame_update() override;
private:
//! Clears the screen in preparation for rendering.
diff --git a/src/crepe/system/ReplaySystem.cpp b/src/crepe/system/ReplaySystem.cpp
new file mode 100644
index 0000000..efc3be4
--- /dev/null
+++ b/src/crepe/system/ReplaySystem.cpp
@@ -0,0 +1,54 @@
+#include "../manager/ReplayManager.h"
+#include "../manager/SystemManager.h"
+
+#include "EventSystem.h"
+#include "RenderSystem.h"
+#include "ReplaySystem.h"
+
+using namespace crepe;
+using namespace std;
+
+void ReplaySystem::fixed_update() {
+ ReplayManager & replay = this->mediator.replay_manager;
+ ReplayManager::State state = replay.get_state();
+ ReplayManager::State last_state = this->last_state;
+ this->last_state = state;
+
+ switch (state) {
+ case ReplayManager::IDLE:
+ break;
+ case ReplayManager::RECORDING: {
+ replay.frame_record();
+ break;
+ }
+ case ReplayManager::PLAYING: {
+ if (last_state != ReplayManager::PLAYING) this->playback_begin();
+ bool last = replay.frame_step();
+ if (last) this->playback_end();
+ break;
+ }
+ }
+}
+
+void ReplaySystem::playback_begin() {
+ SystemManager & systems = this->mediator.system_manager;
+ ComponentManager & components = this->mediator.component_manager;
+
+ this->playback = {
+ .components = components.save(),
+ .systems = systems.save(),
+ };
+
+ systems.disable_all();
+ systems.get_system<RenderSystem>().active = true;
+ systems.get_system<ReplaySystem>().active = true;
+ systems.get_system<EventSystem>().active = true;
+}
+
+void ReplaySystem::playback_end() {
+ SystemManager & systems = this->mediator.system_manager;
+ ComponentManager & components = this->mediator.component_manager;
+
+ components.restore(this->playback.components);
+ systems.restore(this->playback.systems);
+}
diff --git a/src/crepe/system/ReplaySystem.h b/src/crepe/system/ReplaySystem.h
new file mode 100644
index 0000000..bbc8d76
--- /dev/null
+++ b/src/crepe/system/ReplaySystem.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "../manager/ReplayManager.h"
+#include "../manager/SystemManager.h"
+
+#include "System.h"
+
+namespace crepe {
+
+/**
+ * \brief ReplayManager helper system
+ *
+ * This system records and replays recordings using ReplayManager.
+ */
+class ReplaySystem : public System {
+public:
+ using System::System;
+
+ void fixed_update() override;
+
+private:
+ //! Last ReplayManager state
+ ReplayManager::State last_state = ReplayManager::IDLE;
+
+ /**
+ * \brief Playback snapshot
+ *
+ * When starting playback, the component state is saved and most systems are disabled. This
+ * struct stores the engine state before ReplayManager::play is called.
+ */
+ struct Snapshot {
+ ComponentManager::Snapshot components;
+ SystemManager::Snapshot systems;
+ };
+ //! Before playback snapshot
+ Snapshot playback;
+
+ //! Snapshot state and disable systems during playback
+ void playback_begin();
+ //! Restore state from before \c playback_begin()
+ void playback_end();
+};
+
+} // namespace crepe
diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp
index d6b2ca1..0d10011 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -1,13 +1,14 @@
#include "../api/BehaviorScript.h"
#include "../api/Script.h"
#include "../manager/ComponentManager.h"
+#include "../util/dbg.h"
#include "ScriptSystem.h"
using namespace std;
using namespace crepe;
-void ScriptSystem::update() {
+void ScriptSystem::fixed_update() {
dbg_trace();
ComponentManager & mgr = this->mediator.component_manager;
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index 3db1b1e..612c2ae 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -22,7 +22,7 @@ public:
* method. It also calls Script::init() if this has not been done before on
* the \c BehaviorScript instance.
*/
- void update() override;
+ void fixed_update() override;
};
} // namespace crepe
diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h
index 063dfbf..e2ce7eb 100644
--- a/src/crepe/system/System.h
+++ b/src/crepe/system/System.h
@@ -14,10 +14,12 @@ class ComponentManager;
*/
class System {
public:
- /**
- * \brief Process all components this system is responsible for.
- */
- virtual void update() = 0;
+ //! Code that runs in the fixed loop
+ virtual void fixed_update() {};
+ //! Code that runs in the frame loop
+ virtual void frame_update() {};
+ //! Indicates that the update functions of this system should be run
+ bool active = true;
public:
System(const Mediator & m);
diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp
index 84d80a8..ce25a1d 100644
--- a/src/crepe/util/Log.cpp
+++ b/src/crepe/util/Log.cpp
@@ -4,6 +4,7 @@
#include "../api/Config.h"
#include "Log.h"
+#include "LogColor.h"
using namespace crepe;
using namespace std;
diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h
index fc0bb3a..b43fe30 100644
--- a/src/crepe/util/Log.h
+++ b/src/crepe/util/Log.h
@@ -2,27 +2,6 @@
#include <format>
-// allow user to disable debug macros
-#ifndef CREPE_DISABLE_MACROS
-
-#include "LogColor.h"
-
-// utility macros
-#define _crepe_logf_here(level, fmt, ...) \
- crepe::Log::logf(level, "{}" fmt, \
- crepe::LogColor().fg_white(false).str(std::format( \
- "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \
- __VA_ARGS__)
-
-// very illegal global function-style macros
-// NOLINTBEGIN
-#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__)
-#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str)
-#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "")
-// NOLINTEND
-
-#endif
-
namespace crepe {
/**
diff --git a/src/crepe/util/dbg.h b/src/crepe/util/dbg.h
new file mode 100644
index 0000000..c7283ee
--- /dev/null
+++ b/src/crepe/util/dbg.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Log.h"
+#include "LogColor.h"
+
+// utility macros
+#define _crepe_logf_here(level, fmt, ...) \
+ crepe::Log::logf(level, "{}" fmt, \
+ crepe::LogColor().fg_white(false).str(std::format( \
+ "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \
+ __VA_ARGS__)
+
+// very illegal global function-style macros
+// NOLINTBEGIN
+#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__)
+#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str)
+#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "")
+// NOLINTEND