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/Particle.cpp5
-rw-r--r--src/crepe/Particle.h4
-rw-r--r--src/crepe/api/AI.cpp33
-rw-r--r--src/crepe/api/AI.h12
-rw-r--r--src/crepe/api/Animator.cpp10
-rw-r--r--src/crepe/api/Animator.h13
-rw-r--r--src/crepe/api/Asset.cpp2
-rw-r--r--src/crepe/api/AudioSource.h2
-rw-r--r--src/crepe/api/BehaviorScript.cpp4
-rw-r--r--src/crepe/api/BehaviorScript.hpp3
-rw-r--r--src/crepe/api/BoxCollider.cpp5
-rw-r--r--src/crepe/api/BoxCollider.h5
-rw-r--r--src/crepe/api/Button.cpp7
-rw-r--r--src/crepe/api/Button.h18
-rw-r--r--src/crepe/api/CMakeLists.txt5
-rw-r--r--src/crepe/api/Camera.cpp7
-rw-r--r--src/crepe/api/Camera.h6
-rw-r--r--src/crepe/api/CircleCollider.cpp5
-rw-r--r--src/crepe/api/CircleCollider.h5
-rw-r--r--src/crepe/api/Color.cpp18
-rw-r--r--src/crepe/api/Color.h2
-rw-r--r--src/crepe/api/Config.h2
-rw-r--r--src/crepe/api/Engine.cpp68
-rw-r--r--src/crepe/api/Engine.h81
-rw-r--r--src/crepe/api/Engine.hpp12
-rw-r--r--src/crepe/api/Event.h43
-rw-r--r--src/crepe/api/GameObject.cpp23
-rw-r--r--src/crepe/api/GameObject.h27
-rw-r--r--src/crepe/api/GameObject.hpp2
-rw-r--r--src/crepe/api/LoopManager.cpp87
-rw-r--r--src/crepe/api/LoopManager.h123
-rw-r--r--src/crepe/api/ParticleEmitter.cpp19
-rw-r--r--src/crepe/api/ParticleEmitter.h6
-rw-r--r--src/crepe/api/Scene.cpp6
-rw-r--r--src/crepe/api/Scene.h9
-rw-r--r--src/crepe/api/Script.cpp36
-rw-r--r--src/crepe/api/Script.h162
-rw-r--r--src/crepe/api/Script.hpp45
-rw-r--r--src/crepe/api/Sprite.cpp15
-rw-r--r--src/crepe/api/Sprite.h10
-rw-r--r--src/crepe/api/Text.cpp21
-rw-r--r--src/crepe/api/Text.h32
-rw-r--r--src/crepe/api/Transform.cpp11
-rw-r--r--src/crepe/api/Transform.h6
-rw-r--r--src/crepe/api/UIObject.h2
-rw-r--r--src/crepe/facade/DB.cpp2
-rw-r--r--src/crepe/facade/FontFacade.cpp5
-rw-r--r--src/crepe/facade/SDLContext.cpp73
-rw-r--r--src/crepe/facade/SDLContext.h7
-rw-r--r--src/crepe/facade/Sound.cpp2
-rw-r--r--src/crepe/facade/SoundContext.cpp2
-rw-r--r--src/crepe/facade/Texture.cpp12
-rw-r--r--src/crepe/manager/CMakeLists.txt5
-rw-r--r--src/crepe/manager/ComponentManager.cpp45
-rw-r--r--src/crepe/manager/ComponentManager.h55
-rw-r--r--src/crepe/manager/ComponentManager.hpp20
-rw-r--r--src/crepe/manager/EventManager.h6
-rw-r--r--src/crepe/manager/EventManager.hpp21
-rw-r--r--src/crepe/manager/LoopTimerManager.cpp8
-rw-r--r--src/crepe/manager/LoopTimerManager.h16
-rw-r--r--src/crepe/manager/Mediator.h4
-rw-r--r--src/crepe/manager/ReplayManager.cpp70
-rw-r--r--src/crepe/manager/ReplayManager.h96
-rw-r--r--src/crepe/manager/ResourceManager.cpp2
-rw-r--r--src/crepe/manager/ResourceManager.hpp13
-rw-r--r--src/crepe/manager/SceneManager.cpp10
-rw-r--r--src/crepe/manager/SystemManager.cpp66
-rw-r--r--src/crepe/manager/SystemManager.h93
-rw-r--r--src/crepe/manager/SystemManager.hpp (renamed from src/crepe/api/LoopManager.hpp)28
-rw-r--r--src/crepe/system/AISystem.cpp23
-rw-r--r--src/crepe/system/AISystem.h2
-rw-r--r--src/crepe/system/AnimatorSystem.cpp32
-rw-r--r--src/crepe/system/AnimatorSystem.h2
-rw-r--r--src/crepe/system/AudioSystem.cpp4
-rw-r--r--src/crepe/system/AudioSystem.h2
-rw-r--r--src/crepe/system/CMakeLists.txt4
-rw-r--r--src/crepe/system/CollisionSystem.cpp83
-rw-r--r--src/crepe/system/CollisionSystem.h42
-rw-r--r--src/crepe/system/EventSystem.cpp9
-rw-r--r--src/crepe/system/EventSystem.h21
-rw-r--r--src/crepe/system/InputSystem.cpp96
-rw-r--r--src/crepe/system/InputSystem.h35
-rw-r--r--src/crepe/system/ParticleSystem.cpp19
-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.cpp8
-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.cpp20
-rw-r--r--src/crepe/system/ScriptSystem.h23
-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.h21
98 files changed, 1587 insertions, 718 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/Particle.cpp b/src/crepe/Particle.cpp
index b340826..27fa97f 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -2,8 +2,9 @@
using namespace crepe;
-void Particle::reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity,
- float angle) {
+void Particle::reset(
+ unsigned int lifespan, const vec2 & position, const vec2 & velocity, float angle
+) {
// Initialize the particle state
this->time_in_life = 0;
this->lifespan = lifespan;
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index ee0cd66..c013de5 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -41,8 +41,8 @@ public:
* \param velocity The initial velocity of the particle.
* \param angle The angle of the particle's trajectory or orientation.
*/
- void reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity,
- float angle);
+ void
+ reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, float angle);
/**
* \brief Updates the particle's state.
*
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
index 2195249..2fedaf4 100644
--- a/src/crepe/api/AI.cpp
+++ b/src/crepe/api/AI.cpp
@@ -8,8 +8,9 @@ namespace crepe {
AI::AI(game_object_id_t id, float max_force) : Component(id), max_force(max_force) {}
-void AI::make_circle_path(float radius, const vec2 & center, float start_angle,
- bool clockwise) {
+void AI::make_circle_path(
+ float radius, const vec2 & center, float start_angle, bool clockwise
+) {
if (radius <= 0) {
throw std::runtime_error("Radius must be greater than 0");
}
@@ -25,19 +26,25 @@ void AI::make_circle_path(float radius, const vec2 & center, float start_angle,
if (clockwise) {
for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) {
- path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)),
- static_cast<float>(center.y + radius * sin(i))});
+ path.push_back(vec2 {
+ static_cast<float>(center.x + radius * cos(i)),
+ static_cast<float>(center.y + radius * sin(i))
+ });
}
} else {
for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) {
- path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)),
- static_cast<float>(center.y + radius * sin(i))});
+ path.push_back(vec2 {
+ static_cast<float>(center.x + radius * cos(i)),
+ static_cast<float>(center.y + radius * sin(i))
+ });
}
}
}
-void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, float start_angle,
- bool clockwise, float rotation) {
+void AI::make_oval_path(
+ float radius_x, float radius_y, const vec2 & center, float start_angle, bool clockwise,
+ float rotation
+) {
if (radius_x <= 0 && radius_y <= 0) {
throw std::runtime_error("Radius must be greater than 0");
}
@@ -73,14 +80,16 @@ void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, flo
if (clockwise) {
for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) {
- vec2 point = {static_cast<float>(center.x + radius_x * cos(i)),
- static_cast<float>(center.y + radius_y * sin(i))};
+ vec2 point
+ = {static_cast<float>(center.x + radius_x * cos(i)),
+ static_cast<float>(center.y + radius_y * sin(i))};
path.push_back(rotate_point(point, center));
}
} else {
for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) {
- vec2 point = {static_cast<float>(center.x + radius_x * cos(i)),
- static_cast<float>(center.y + radius_y * sin(i))};
+ vec2 point
+ = {static_cast<float>(center.x + radius_x * cos(i)),
+ static_cast<float>(center.y + radius_y * sin(i))};
path.push_back(rotate_point(point, center));
}
}
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index c780a91..bee11b3 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -70,8 +70,10 @@ public:
* \param start_angle The start angle of the circle (in radians)
* \param clockwise The direction of the circle
*/
- void make_circle_path(float radius, const vec2 & center = {0, 0}, float start_angle = 0,
- bool clockwise = true);
+ void make_circle_path(
+ float radius, const vec2 & center = {0, 0}, float start_angle = 0,
+ bool clockwise = true
+ );
/**
* \brief Make an oval path (for the path following behavior)
*
@@ -84,8 +86,10 @@ public:
* \param clockwise The direction of the oval
* \param rotation The rotation of the oval (in radians)
*/
- void make_oval_path(float radius_x, float radius_y, const vec2 & center = {0, 0},
- float start_angle = 0, bool clockwise = true, float rotation = 0);
+ void make_oval_path(
+ float radius_x, float radius_y, const vec2 & center = {0, 0}, float start_angle = 0,
+ bool clockwise = true, float rotation = 0
+ );
public:
//! The maximum force that can be applied to the entity (higher values will make the entity adjust faster)
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp
index 4ce4bf0..c558d86 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"
@@ -7,8 +7,10 @@
using namespace crepe;
-Animator::Animator(game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
- const uvec2 & grid_size, const Animator::Data & data)
+Animator::Animator(
+ game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
+ const uvec2 & grid_size, const Animator::Data & data
+)
: Component(id),
spritesheet(spritesheet),
grid_size(grid_size),
@@ -52,6 +54,6 @@ void Animator::set_anim(int col) {
void Animator::next_anim() {
Animator::Data & ctx = this->data;
- ctx.row = ctx.row++ % this->grid_size.x;
+ ctx.row = ++ctx.row % this->grid_size.x;
this->spritesheet.mask.x = ctx.row * this->spritesheet.mask.w;
}
diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h
index 5918800..95539d3 100644
--- a/src/crepe/api/Animator.h
+++ b/src/crepe/api/Animator.h
@@ -1,5 +1,6 @@
#pragma once
+#include "../manager/LoopTimerManager.h"
#include "../types.h"
#include "Component.h"
@@ -83,8 +84,10 @@ public:
* This constructor sets up the Animator with the given parameters, and initializes the
* animation system.
*/
- Animator(game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
- const uvec2 & grid_size, const Animator::Data & data);
+ Animator(
+ game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
+ const uvec2 & grid_size, const Animator::Data & data
+ );
~Animator(); // dbg_trace
public:
@@ -97,6 +100,12 @@ private:
//! The maximum number of rows and columns inside the spritesheet
const uvec2 grid_size;
+ // the time elapsed from a frame duration
+ duration_t elapsed_time = {};
+
+ // frame counter
+ unsigned int frame = 0;
+
//! Uses the spritesheet
friend AnimatorSystem;
};
diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp
index e148367..bab82e7 100644
--- a/src/crepe/api/Asset.cpp
+++ b/src/crepe/api/Asset.cpp
@@ -50,5 +50,5 @@ string Asset::whereami() const noexcept {
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());
+ return std::hash<string> {}(asset.get_path());
};
diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h
index b20e490..eaa56e8 100644
--- a/src/crepe/api/AudioSource.h
+++ b/src/crepe/api/AudioSource.h
@@ -68,7 +68,7 @@ private:
typeof(loop) last_loop = loop;
//! \}
//! This source's voice handle
- SoundHandle voice{};
+ SoundHandle voice {};
};
} // namespace crepe
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/BoxCollider.cpp b/src/crepe/api/BoxCollider.cpp
index a893d41..f6b358d 100644
--- a/src/crepe/api/BoxCollider.cpp
+++ b/src/crepe/api/BoxCollider.cpp
@@ -4,7 +4,8 @@
using namespace crepe;
-BoxCollider::BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions,
- const vec2 & offset)
+BoxCollider::BoxCollider(
+ game_object_id_t game_object_id, const vec2 & dimensions, const vec2 & offset
+)
: Collider(game_object_id, offset),
dimensions(dimensions) {}
diff --git a/src/crepe/api/BoxCollider.h b/src/crepe/api/BoxCollider.h
index d643e7f..229b90f 100644
--- a/src/crepe/api/BoxCollider.h
+++ b/src/crepe/api/BoxCollider.h
@@ -13,8 +13,9 @@ namespace crepe {
*/
class BoxCollider : public Collider {
public:
- BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions,
- const vec2 & offset = {0, 0});
+ BoxCollider(
+ game_object_id_t game_object_id, const vec2 & dimensions, const vec2 & offset = {0, 0}
+ );
//! Width and height of the box collider
vec2 dimensions;
diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp
index 40153c9..8eadd89 100644
--- a/src/crepe/api/Button.cpp
+++ b/src/crepe/api/Button.cpp
@@ -2,7 +2,10 @@
namespace crepe {
-Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset)
- : UIObject(id, dimensions, offset) {}
+Button::Button(
+ game_object_id_t id, const vec2 & dimensions, const Data & data, const vec2 & offset
+)
+ : UIObject(id, dimensions, offset),
+ data(data) {}
} // namespace crepe
diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h
index d42527e..e986c04 100644
--- a/src/crepe/api/Button.h
+++ b/src/crepe/api/Button.h
@@ -1,8 +1,7 @@
#pragma once
-#include <functional>
+#include "../types.h"
-#include "Event.h"
#include "UIObject.h"
namespace crepe {
@@ -21,14 +20,24 @@ namespace crepe {
*/
class Button : public UIObject {
public:
+ struct Data {
+ //! variable indicating if transform is relative to camera(false) or world(true)
+ bool world_space = false;
+ };
+
+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 data additional data the button has
*/
- Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset);
+ Button(
+ game_object_id_t id, const vec2 & dimensions, const Data & data,
+ const vec2 & offset = {0, 0}
+ );
/**
* \brief Get the maximum number of instances for this component
*
@@ -38,6 +47,9 @@ public:
*/
virtual int get_instances_max() const { return 1; }
+public:
+ Data data;
+
private:
//! friend relation hover variable
friend class InputSystem;
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index e8d6f92..2bee3fb 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
@@ -47,7 +47,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..b1466b5 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"
@@ -6,8 +6,9 @@
using namespace crepe;
-Camera::Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size,
- const Data & data)
+Camera::Camera(
+ game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size, const Data & data
+)
: Component(id),
screen(screen),
viewport_size(viewport_size),
diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h
index 54d9a73..3191b04 100644
--- a/src/crepe/api/Camera.h
+++ b/src/crepe/api/Camera.h
@@ -44,8 +44,10 @@ public:
* \param viewport_size is the view of the world in game units
* \param data the camera component data
*/
- Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size,
- const Camera::Data & data);
+ Camera(
+ game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size,
+ const Camera::Data & data
+ );
~Camera(); // dbg_trace only
public:
diff --git a/src/crepe/api/CircleCollider.cpp b/src/crepe/api/CircleCollider.cpp
index 90ab5e7..e72800c 100644
--- a/src/crepe/api/CircleCollider.cpp
+++ b/src/crepe/api/CircleCollider.cpp
@@ -2,7 +2,8 @@
using namespace crepe;
-CircleCollider::CircleCollider(game_object_id_t game_object_id, float radius,
- const vec2 & offset)
+CircleCollider::CircleCollider(
+ game_object_id_t game_object_id, float radius, const vec2 & offset
+)
: Collider(game_object_id, offset),
radius(radius) {}
diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h
index 22da836..e6ad4fa 100644
--- a/src/crepe/api/CircleCollider.h
+++ b/src/crepe/api/CircleCollider.h
@@ -13,8 +13,9 @@ namespace crepe {
*/
class CircleCollider : public Collider {
public:
- CircleCollider(game_object_id_t game_object_id, float radius,
- const vec2 & offset = {0, 0});
+ CircleCollider(
+ game_object_id_t game_object_id, float radius, const vec2 & offset = {0, 0}
+ );
//! Radius of the circle collider.
float radius;
diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp
index 29bd77a..d0e3b35 100644
--- a/src/crepe/api/Color.cpp
+++ b/src/crepe/api/Color.cpp
@@ -2,11 +2,13 @@
using namespace crepe;
-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};
+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};
+const Color Color::GREY {0x80, 0x80, 0x80};
+const Color Color::GOLD {249, 205, 91};
diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h
index 84edb5c..dbfd0ed 100644
--- a/src/crepe/api/Color.h
+++ b/src/crepe/api/Color.h
@@ -18,6 +18,8 @@ struct Color {
static const Color MAGENTA;
static const Color YELLOW;
static const Color BLACK;
+ static const Color GREY;
+ static const Color GOLD;
};
} // namespace crepe
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
index 6b9e3ca..9c226a3 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -85,7 +85,7 @@ struct Config final {
* This config option is the font size at which all fonts will be loaded initially.
*
*/
- unsigned int size = 16;
+ unsigned int size = 500;
} font;
//! Configuration for click tolerance.
struct {
diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp
new file mode 100644
index 0000000..0bbe51f
--- /dev/null
+++ b/src/crepe/api/Engine.cpp
@@ -0,0 +1,68 @@
+#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 (this->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();
+ this->scene_manager.load_next_scene();
+ } 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..452a856
--- /dev/null
+++ b/src/crepe/api/Engine.h
@@ -0,0 +1,81 @@
+#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;
+
+ //! SystemManager
+ SystemManager system_manager {mediator};
+
+ //! SDLContext instance
+ SDLContext sdl_context {mediator};
+
+ //! Resource manager instance
+ ResourceManager resource_manager {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};
+ //! Save manager instance
+ SaveManager save_manager {mediator};
+ //! ReplayManager instance
+ ReplayManager replay_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/Event.h b/src/crepe/api/Event.h
index 8e38280..7d4df21 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -10,15 +10,14 @@
namespace crepe {
/**
- * \brief Base class for all event types in the system.
+ * \brief Base struct for all event types in the system.
*/
-class Event {};
+struct Event {};
/**
* \brief Event triggered when a key is pressed.
*/
-class KeyPressEvent : public Event {
-public:
+struct KeyPressEvent : public Event {
//! false if first time press, true if key is repeated
bool repeat = false;
@@ -29,8 +28,7 @@ public:
/**
* \brief Event triggered when a key is released.
*/
-class KeyReleaseEvent : public Event {
-public:
+struct KeyReleaseEvent : public Event {
//! The key that was released.
Keycode key = Keycode::NONE;
};
@@ -38,8 +36,7 @@ public:
/**
* \brief Event triggered when a mouse button is pressed.
*/
-class MousePressEvent : public Event {
-public:
+struct MousePressEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
@@ -50,8 +47,7 @@ public:
/**
* \brief Event triggered when a mouse button is clicked (press and release).
*/
-class MouseClickEvent : public Event {
-public:
+struct MouseClickEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
@@ -62,8 +58,7 @@ public:
/**
* \brief Event triggered when a mouse button is released.
*/
-class MouseReleaseEvent : public Event {
-public:
+struct MouseReleaseEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
@@ -74,8 +69,7 @@ public:
/**
* \brief Event triggered when the mouse is moved.
*/
-class MouseMoveEvent : public Event {
-public:
+struct MouseMoveEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
//! The change in mouse position relative to the last position (in pixels).
@@ -85,8 +79,7 @@ public:
/**
* \brief Event triggered when the mouse is moved.
*/
-class MouseScrollEvent : public Event {
-public:
+struct MouseScrollEvent : public Event {
//! mouse position in world coordinates (game units) when the scroll happened.
vec2 mouse_pos = {0, 0};
//! scroll direction (-1 = down, 1 = up)
@@ -98,20 +91,19 @@ public:
/**
* \brief Event triggered to indicate the application is shutting down.
*/
-class ShutDownEvent : public Event {};
+struct ShutDownEvent : public Event {};
/**
* \brief Event triggered to indicate the window is overlapped by another window.
*
* When two windows overlap the bottom window gets distorted and that window has to be redrawn.
*/
-class WindowExposeEvent : public Event {};
+struct WindowExposeEvent : public Event {};
/**
* \brief Event triggered to indicate the window is resized.
*/
-class WindowResizeEvent : public Event {
-public:
+struct WindowResizeEvent : public Event {
//! new window dimensions
ivec2 dimensions = {0, 0};
};
@@ -119,8 +111,7 @@ public:
/**
* \brief Event triggered to indicate the window is moved.
*/
-class WindowMoveEvent : public Event {
-public:
+struct WindowMoveEvent : public Event {
//! The change in position relative to the last position (in pixels).
ivec2 delta_move = {0, 0};
};
@@ -128,12 +119,12 @@ public:
/**
* \brief Event triggered to indicate the window is minimized.
*/
-class WindowMinimizeEvent : public Event {};
+struct WindowMinimizeEvent : public Event {};
/**
* \brief Event triggered to indicate the window is maximized
*/
-class WindowMaximizeEvent : public Event {};
+struct WindowMaximizeEvent : public Event {};
/**
* \brief Event triggered to indicate the window gained focus
@@ -141,7 +132,7 @@ class WindowMaximizeEvent : public Event {};
* This event is triggered when the window receives focus, meaning it becomes the active window
* for user interaction.
*/
-class WindowFocusGainEvent : public Event {};
+struct WindowFocusGainEvent : public Event {};
/**
* \brief Event triggered to indicate the window lost focus
@@ -149,6 +140,6 @@ class WindowFocusGainEvent : public Event {};
* This event is triggered when the window loses focus, meaning it is no longer the active window
* for user interaction.
*/
-class WindowFocusLostEvent : public Event {};
+struct WindowFocusLostEvent : public Event {};
} // namespace crepe
diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp
index 9ef4682..100e210 100644
--- a/src/crepe/api/GameObject.cpp
+++ b/src/crepe/api/GameObject.cpp
@@ -7,20 +7,19 @@
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 +31,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..043913a 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,22 @@ 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 +79,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 7a78019..0000000
--- a/src/crepe/api/LoopManager.cpp
+++ /dev/null
@@ -1,87 +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<ParticleSystem>().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 124cd3a..0000000
--- a/src/crepe/api/LoopManager.h
+++ /dev/null
@@ -1,123 +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;
- /**
- * \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;
-
- //! SDLContext instance
- SDLContext sdl_context{mediator};
- //! Resource manager instance
- ResourceManager resource_manager{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};
- //! Save manager instance
- SaveManager save_manager{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 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 4f54bbd..341c1e2 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -2,9 +2,11 @@
#include "api/Sprite.h"
using namespace crepe;
+using namespace std;
-ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite,
- const Data & data)
+ParticleEmitter::ParticleEmitter(
+ game_object_id_t game_object_id, const Sprite & sprite, const Data & data
+)
: Component(game_object_id),
sprite(sprite),
data(data) {
@@ -12,3 +14,16 @@ ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite &
this->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) {
+ this->particles = other.particles;
+ return *this;
+}
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 8ac2e72..1edd2b5 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -84,6 +84,12 @@ 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 &);
+
private:
//! Only ParticleSystem can move and read particles
friend ParticleSystem;
diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp
index ad729d2..84da7e8 100644
--- a/src/crepe/api/Scene.cpp
+++ b/src/crepe/api/Scene.cpp
@@ -4,8 +4,10 @@ using namespace crepe;
SaveManager & Scene::get_save_manager() const { return mediator->save_manager; }
-GameObject Scene::new_object(const std::string & name, const std::string & tag,
- const vec2 & position, double rotation, double scale) {
+GameObject Scene::new_object(
+ const std::string & name, const std::string & tag, const vec2 & position, double rotation,
+ double scale
+) {
// Forward the call to ComponentManager's new_object method
return mediator->component_manager->new_object(name, tag, position, rotation, scale);
}
diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h
index dcca9d4..b50a0fc 100644
--- a/src/crepe/api/Scene.h
+++ b/src/crepe/api/Scene.h
@@ -60,7 +60,7 @@ private:
OptionalRef<Mediator> mediator;
//! \}
-protected:
+public:
/**
* \brief Retrieve the reference to the SaveManager instance
*
@@ -69,9 +69,10 @@ protected:
SaveManager & get_save_manager() const;
//! \copydoc ComponentManager::new_object
- GameObject new_object(const std::string & name, const std::string & tag = "",
- const vec2 & position = {0, 0}, double rotation = 0,
- double scale = 1);
+ GameObject new_object(
+ const std::string & name, const std::string & tag = "", const vec2 & position = {0, 0},
+ double rotation = 0, double scale = 1
+ );
//! \copydoc ResourceManager::set_persistent
void set_persistent(const Asset & asset, bool persistent);
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index 7531388..06b535f 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -2,6 +2,7 @@
#include "../facade/SDLContext.h"
#include "../manager/SceneManager.h"
+
#include "Script.h"
using namespace crepe;
@@ -19,6 +20,21 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
this->subscribe_internal(callback, this->game_object_id);
}
+template <>
+void Script::subscribe(const EventHandler<ButtonExitEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
+
+template <>
+void Script::subscribe(const EventHandler<ButtonPressEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
+
+template <>
+void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
+
void Script::set_next_scene(const string & name) {
SceneManager & mgr = this->mediator->scene_manager;
mgr.set_next_scene(name);
@@ -28,6 +44,26 @@ SaveManager & Script::get_save_manager() const { return this->mediator->save_man
LoopTimerManager & Script::get_loop_timer() const { return this->mediator->loop_timer; }
+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);
+}
+
const keyboard_state_t & Script::get_keyboard_state() const {
SDLContext & sdl_context = this->mediator->sdl_context;
return sdl_context.get_keyboard_state();
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index a87af4e..b000d9d 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -6,8 +6,11 @@
#include "../manager/EventManager.h"
#include "../manager/LoopTimerManager.h"
#include "../manager/Mediator.h"
+#include "../manager/ReplayManager.h"
#include "../system/CollisionSystem.h"
+#include "../system/InputSystem.h"
#include "../types.h"
+#include "../util/Log.h"
#include "../util/OptionalRef.h"
namespace crepe {
@@ -46,14 +49,23 @@ protected:
*/
virtual void init() {}
/**
- * \brief Script update function (empty by default)
+ * \brief Script fixed update function (empty by default)
*
* \param delta_time Time since last fixed update
*
- * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript
- * component holding this script instance is active.
+ * \note This function is called during the ScriptSystem::update() routine if the \c
+ * BehaviorScript component holding this script instance is active.
*/
- virtual void update(duration_t delta_time) {}
+ virtual void fixed_update(duration_t delta_time) {}
+ /**
+ * \brief Script frame update function (empty by default)
+ *
+ * \param delta_time Time since last frame update
+ *
+ * \note This function is called during the ScriptSystem::update() routine if the \c
+ * BehaviorScript component holding this script instance is active.
+ */
+ virtual void frame_update(duration_t delta_time) {}
//! \}
//! ScriptSystem calls \c init() and \c update()
@@ -61,93 +73,121 @@ 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;
+ //! \}
+ /**
+ * \name Timing functions
+ * \see LoopTimerManager
+ * \{
+ */
//! Retrieve LoopTimerManager reference
LoopTimerManager & get_loop_timer() 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};
/**
* \brief Utility function to retrieve the keyboard state
* \see SDLContext::get_keyboard_state
*
* \return current keyboard state map with Keycode as key and bool as value(true = pressed, false = not pressed)
- *
*/
const keyboard_state_t & get_keyboard_state() const;
/**
@@ -155,7 +195,6 @@ protected:
* \see SDLContext::get_keyboard_state
*
* \return Keycode state (true if pressed, false if not pressed).
- *
*/
bool get_key_state(Keycode key) const noexcept;
@@ -234,7 +273,42 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback);
template <>
void Script::subscribe(const EventHandler<CollisionEvent> & callback, event_channel_t)
= delete;
-
+/**
+ * \brief Subscribe to ButtonPressEvent 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 ButtonPressEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<ButtonPressEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<ButtonPressEvent> & callback, event_channel_t)
+ = delete;
+/**
+ * \brief Subscribe to ButtonExitEvent 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 ButtonExitEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<ButtonExitEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<ButtonExitEvent> & callback, event_channel_t)
+ = delete;
+/**
+ * \brief Subscribe to ButtonEnterEvent 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 ButtonEnterEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<ButtonEnterEvent> & 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 225a51c..c7fa6ff 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"
@@ -13,7 +14,8 @@ T & Script::get_component() const {
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()));
+ format("Script: no component found with type = {}", typeid(T).name())
+ );
return all_components.back().get();
}
@@ -23,22 +25,39 @@ 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>
-void Script::subscribe_internal(const EventHandler<EventType> & callback,
- event_channel_t channel) {
+void Script::subscribe_internal(
+ const EventHandler<EventType> & callback, event_channel_t channel
+) {
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);
+ channel
+ );
this->listeners.push_back(listener);
}
@@ -52,6 +71,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..3c77e2e 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"
@@ -19,3 +19,16 @@ Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data &
}
Sprite::~Sprite() { dbg_trace(); }
+
+unique_ptr<Component> Sprite::save() const { return unique_ptr<Component>(new Sprite(*this)); }
+
+void Sprite::restore(const Component & snapshot) {
+ *this = static_cast<const Sprite &>(snapshot);
+}
+
+Sprite & Sprite::operator=(const Sprite & snapshot) {
+ this->active = snapshot.active;
+ this->data = snapshot.data;
+ this->mask = snapshot.mask;
+ return *this;
+}
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index a3fc319..3565bed 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -42,10 +42,10 @@ public:
FlipSettings flip;
//! Layer sorting level of the sprite
- const int sorting_in_layer = 0;
+ int sorting_in_layer = 0;
//! Order within the sorting layer
- const int order_in_layer = 0;
+ int order_in_layer = 0;
/**
* \brief width and height of the sprite in game units
@@ -119,6 +119,12 @@ private:
//! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator
// object is present in GameObject. this is in sprite pixels
Rect mask;
+
+protected:
+ virtual std::unique_ptr<Component> save() const;
+ Sprite(const Sprite &) = default;
+ virtual void restore(const Component & snapshot);
+ virtual Sprite & operator=(const Sprite &);
};
} // namespace crepe
diff --git a/src/crepe/api/Text.cpp b/src/crepe/api/Text.cpp
index 4a94180..e5cc39d 100644
--- a/src/crepe/api/Text.cpp
+++ b/src/crepe/api/Text.cpp
@@ -1,10 +1,27 @@
+#include "../types.h"
+
#include "Text.h"
using namespace crepe;
+using namespace std;
-Text::Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
- const std::string & font_family, const Data & data, const std::string & text)
+Text::Text(
+ game_object_id_t id, const vec2 & dimensions, const std::string & font_family,
+ const Data & data, const vec2 & offset, const std::string & text
+)
: UIObject(id, dimensions, offset),
text(text),
data(data),
font_family(font_family) {}
+
+unique_ptr<Component> Text::save() const { return unique_ptr<Component>(new Text(*this)); }
+
+void Text::restore(const Component & snapshot) { *this = static_cast<const Text &>(snapshot); }
+
+Text & Text::operator=(const Text & snapshot) {
+ this->active = snapshot.active;
+ this->data = snapshot.data;
+ this->text = snapshot.text;
+ this->font_family = snapshot.font_family;
+ return *this;
+}
diff --git a/src/crepe/api/Text.h b/src/crepe/api/Text.h
index da40141..859490e 100644
--- a/src/crepe/api/Text.h
+++ b/src/crepe/api/Text.h
@@ -3,6 +3,8 @@
#include <optional>
#include <string>
+#include "../types.h"
+
#include "Asset.h"
#include "Color.h"
#include "UIObject.h"
@@ -17,22 +19,8 @@ class Text : public UIObject {
public:
//! Text data that does not have to be set in the constructor
struct Data {
- /**
- * \brief fontsize for text rendering
- *
- * \note this is not the actual font size that is loaded in.
- *
- * Since SDL_TTF requires the font size when loading in the font it is not possible to switch the font size.
- * The default font size that is loaded is set in the Config.
- * Instead this value is used to upscale the font texture which can cause blurring or distorted text when upscaling or downscaling too much.
- */
- unsigned int font_size = 16;
-
- //! Layer sorting level of the text
- const int sorting_in_layer = 0;
-
- //! Order within the sorting text
- const int order_in_layer = 0;
+ //! variable indicating if transform is relative to camera(false) or world(true)
+ bool world_space = false;
//! Label text color.
Color text_color = Color::BLACK;
@@ -48,8 +36,10 @@ public:
* \param data Data struct containing extra text parameters.
* \param font Optional font asset that can be passed or left empty.
*/
- Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
- const std::string & font_family, const Data & data, const std::string & text = "");
+ Text(
+ game_object_id_t id, const vec2 & dimensions, const std::string & font_family,
+ const Data & data, const vec2 & offset = {0, 0}, const std::string & text = ""
+ );
//! Label text.
std::string text = "";
@@ -59,6 +49,12 @@ public:
std::optional<Asset> font;
//! Data instance
Data data;
+
+protected:
+ virtual std::unique_ptr<Component> save() const;
+ Text(const Text &) = default;
+ virtual void restore(const Component & snapshot);
+ virtual Text & operator=(const Text &);
};
} // namespace crepe
diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp
index a85b792..b70174c 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/api/UIObject.h b/src/crepe/api/UIObject.h
index f7f4fba..0d9b1f7 100644
--- a/src/crepe/api/UIObject.h
+++ b/src/crepe/api/UIObject.h
@@ -15,7 +15,7 @@ public:
* \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);
+ UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset = {0, 0});
//! Width and height of the UIObject
vec2 dimensions;
//! Position offset relative to this GameObjects Transform
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index ae2d4bc..7a3e473 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/FontFacade.cpp b/src/crepe/facade/FontFacade.cpp
index 87f95ab..e284f5a 100644
--- a/src/crepe/facade/FontFacade.cpp
+++ b/src/crepe/facade/FontFacade.cpp
@@ -20,8 +20,9 @@ Asset FontFacade::get_font_asset(const string & font_family) {
= FcNameParse(reinterpret_cast<const FcChar8 *>(font_family.c_str()));
if (raw_pattern == NULL) throw runtime_error("Failed to create font pattern.");
- unique_ptr<FcPattern, function<void(FcPattern *)>> pattern{
- raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }};
+ unique_ptr<FcPattern, function<void(FcPattern *)>> pattern {
+ raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }
+ };
FcConfig * config = FcConfigGetCurrent();
if (config == NULL) throw runtime_error("Failed to get current Fontconfig configuration.");
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 2dae1e7..6c93fb2 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,5 +1,6 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_blendmode.h>
+#include <SDL2/SDL_hints.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_pixels.h>
@@ -19,7 +20,7 @@
#include "../api/Color.h"
#include "../api/Config.h"
#include "../api/Sprite.h"
-#include "../util/Log.h"
+#include "../util/dbg.h"
#include "api/Text.h"
#include "api/Transform.h"
#include "facade/Font.h"
@@ -40,9 +41,10 @@ SDLContext::SDLContext(Mediator & mediator) {
}
auto & cfg = Config::get_instance().window_settings;
- SDL_Window * tmp_window
- = SDL_CreateWindow(cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED,
- SDL_WINDOWPOS_CENTERED, cfg.default_size.x, cfg.default_size.y, 0);
+ SDL_Window * tmp_window = SDL_CreateWindow(
+ cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ cfg.default_size.x, cfg.default_size.y, 0
+ );
if (!tmp_window) {
throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError()));
}
@@ -51,8 +53,8 @@ SDLContext::SDLContext(Mediator & mediator) {
SDL_Renderer * tmp_renderer
= SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
if (!tmp_renderer) {
- throw runtime_error(
- format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
+ throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())
+ );
}
this->game_renderer
@@ -105,7 +107,7 @@ const keyboard_state_t & SDLContext::get_keyboard_state() {
MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) {
static const std::array<MouseButton, 5> MOUSE_BUTTON_LOOKUP_TABLE = [] {
- std::array<MouseButton, 5> table{};
+ std::array<MouseButton, 5> table {};
table.fill(MouseButton::NONE);
table[SDL_BUTTON_LEFT] = MouseButton::LEFT_MOUSE;
@@ -161,7 +163,7 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
- size / 2 + cam_aux_data.bar_size;
}
- return SDL_FRect{
+ return SDL_FRect {
.x = screen_pos.x,
.y = screen_pos.y,
.w = size.x,
@@ -170,6 +172,7 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
}
void SDLContext::draw(const RenderContext & ctx) {
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
const Sprite::Data & data = ctx.sprite.data;
SDL_RendererFlip render_flip
= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * data.flip.flip_x)
@@ -185,7 +188,7 @@ void SDLContext::draw(const RenderContext & ctx) {
srcrect_ptr = &srcrect;
}
- SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData{
+ SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData {
.sprite = ctx.sprite,
.texture = ctx.texture,
.pos = ctx.pos,
@@ -195,11 +198,14 @@ void SDLContext::draw(const RenderContext & ctx) {
double angle = ctx.angle + data.angle_offset;
this->set_color_texture(ctx.texture, ctx.sprite.data.color);
- SDL_RenderCopyExF(this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect,
- angle, NULL, render_flip);
+ SDL_RenderCopyExF(
+ this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect, angle, NULL,
+ render_flip
+ );
}
void SDLContext::draw_text(const RenderText & data) {
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
const Text & text = data.text;
const Font & font = data.font;
@@ -207,7 +213,7 @@ void SDLContext::draw_text(const RenderText & data) {
std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> font_surface;
std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> font_texture;
- SDL_Color color{
+ SDL_Color color {
.r = text.data.text_color.r,
.g = text.data.text_color.g,
.b = text.data.text_color.b,
@@ -229,20 +235,28 @@ void SDLContext::draw_text(const RenderText & data) {
= {tmp_font_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }};
vec2 size = text.dimensions * cam_aux_data.render_scale * data.transform.scale;
- vec2 screen_pos
- = (absoluut_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2)
- * cam_aux_data.render_scale
- - size / 2 + cam_aux_data.bar_size;
+ vec2 screen_pos = absoluut_pos;
+ if (text.data.world_space) {
+ screen_pos = (screen_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2)
+ * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ } else {
+ screen_pos
+ = (screen_pos + (cam_aux_data.zoomed_viewport) / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ }
- SDL_FRect dstrect{
+ SDL_FRect dstrect {
.x = screen_pos.x,
.y = screen_pos.y,
.w = size.x,
.h = size.y,
};
- SDL_RenderCopyExF(this->game_renderer.get(), font_texture.get(), NULL, &dstrect,
- data.transform.rotation, NULL, SDL_FLIP_NONE);
+ SDL_RenderCopyExF(
+ this->game_renderer.get(), font_texture.get(), NULL, &dstrect, data.transform.rotation,
+ NULL, SDL_FLIP_NONE
+ );
}
void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) {
@@ -288,8 +302,10 @@ void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) {
render_scale.x = render_scale.y = scale;
}
- SDL_SetRenderDrawColor(this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g,
- cam_data.bg_color.b, cam_data.bg_color.a);
+ SDL_SetRenderDrawColor(
+ this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g,
+ cam_data.bg_color.b, cam_data.bg_color.a
+ );
SDL_Rect bg = {
.x = 0,
@@ -424,11 +440,12 @@ std::vector<EventData> SDLContext::get_events() {
return event_list;
}
-void SDLContext::handle_window_event(const SDL_WindowEvent & window_event,
- std::vector<EventData> & event_list) {
+void SDLContext::handle_window_event(
+ const SDL_WindowEvent & window_event, std::vector<EventData> & event_list
+) {
switch (window_event.event) {
case SDL_WINDOWEVENT_EXPOSED:
- event_list.push_back(EventData{EventType::WINDOW_EXPOSE});
+ event_list.push_back(EventData {EventType::WINDOW_EXPOSE});
break;
case SDL_WINDOWEVENT_RESIZED:
event_list.push_back(EventData{
@@ -452,16 +469,16 @@ void SDLContext::handle_window_event(const SDL_WindowEvent & window_event,
break;
case SDL_WINDOWEVENT_MINIMIZED:
- event_list.push_back(EventData{EventType::WINDOW_MINIMIZE});
+ event_list.push_back(EventData {EventType::WINDOW_MINIMIZE});
break;
case SDL_WINDOWEVENT_MAXIMIZED:
- event_list.push_back(EventData{EventType::WINDOW_MAXIMIZE});
+ event_list.push_back(EventData {EventType::WINDOW_MAXIMIZE});
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
- event_list.push_back(EventData{EventType::WINDOW_FOCUS_GAIN});
+ event_list.push_back(EventData {EventType::WINDOW_FOCUS_GAIN});
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
- event_list.push_back(EventData{EventType::WINDOW_FOCUS_LOST});
+ event_list.push_back(EventData {EventType::WINDOW_FOCUS_LOST});
break;
}
}
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index e570073..bc118f9 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -116,8 +116,9 @@ public:
* This method checks if any window events are triggered and adds them to the event_list.
*
*/
- void handle_window_event(const SDL_WindowEvent & window_event,
- std::vector<EventData> & event_list);
+ void handle_window_event(
+ const SDL_WindowEvent & window_event, std::vector<EventData> & event_list
+ );
/**
* \brief Converts an SDL scan code to the custom Keycode type.
*
@@ -254,7 +255,7 @@ private:
private:
//! instance of the font_facade
- FontFacade font_facade{};
+ FontFacade font_facade {};
public:
/**
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..06caa54 100644
--- a/src/crepe/facade/Texture.cpp
+++ b/src/crepe/facade/Texture.cpp
@@ -1,10 +1,11 @@
-#include "../util/Log.h"
-#include "facade/SDLContext.h"
-#include "manager/Mediator.h"
+#include "../Resource.h"
+#include "../facade/SDLContext.h"
+#include "../manager/Mediator.h"
+#include "../types.h"
+#include "../util/dbg.h"
-#include "Resource.h"
+#include "SDLContext.h"
#include "Texture.h"
-#include "types.h"
using namespace crepe;
using namespace std;
@@ -23,6 +24,7 @@ Texture::~Texture() {
}
const ivec2 & Texture::get_size() const noexcept { return this->size; }
+
const float & Texture::get_ratio() const noexcept { return this->aspect_ratio; }
SDL_Texture * Texture::get_img() const noexcept { return this->texture.get(); }
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..245419d 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"
@@ -46,14 +46,16 @@ void ComponentManager::delete_all_components() {
this->next_id = 0;
}
-GameObject ComponentManager::new_object(const string & name, const string & tag,
- const vec2 & position, double rotation, double scale) {
+GameObject ComponentManager::new_object(
+ const string & name, const string & tag, const vec2 & position, double rotation,
+ double scale
+) {
// Find the first available id (taking persistent objects into account)
while (this->persistent[this->next_id]) {
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;
@@ -64,11 +66,38 @@ void ComponentManager::set_persistent(game_object_id_t id, bool persistent) {
}
set<game_object_id_t> ComponentManager::get_objects_by_name(const string & name) const {
- return this->get_objects_by_predicate<Metadata>(
- [name](const Metadata & data) { return data.name == name; });
+ return this->get_objects_by_predicate<Metadata>([name](const Metadata & data) {
+ return data.name == name;
+ });
}
set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) const {
- return this->get_objects_by_predicate<Metadata>(
- [tag](const Metadata & data) { return data.tag == tag; });
+ 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..2eb1f7e 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
@@ -45,16 +38,12 @@ public:
*
* \note This method automatically assigns a new entity ID
*/
- GameObject new_object(const std::string & name, const std::string & tag = "",
- const vec2 & position = {0, 0}, double rotation = 0,
- double scale = 1);
+ GameObject new_object(
+ const std::string & name, const std::string & tag = "", 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 +143,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/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp
index 9e70865..6d32edb 100644
--- a/src/crepe/manager/ComponentManager.hpp
+++ b/src/crepe/manager/ComponentManager.hpp
@@ -11,8 +11,10 @@ template <class T, typename... Args>
T & ComponentManager::add_component(game_object_id_t id, Args &&... args) {
using namespace std;
- static_assert(is_base_of<Component, T>::value,
- "add_component must recieve a derivative class of Component");
+ static_assert(
+ is_base_of<Component, T>::value,
+ "add_component must recieve a derivative class of Component"
+ );
// Determine the type of T (this is used as the key of the unordered_map<>)
type_index type = typeid(T);
@@ -40,8 +42,8 @@ T & ComponentManager::add_component(game_object_id_t id, Args &&... args) {
// Check if the vector size is not greater than get_instances_max
int max_instances = instance->get_instances_max();
if (max_instances != -1 && components[type][id].size() >= max_instances) {
- throw std::runtime_error(
- "Exceeded maximum number of instances for this component type");
+ throw std::runtime_error("Exceeded maximum number of instances for this component type"
+ );
}
// store its unique_ptr in the vector<>
@@ -95,8 +97,10 @@ template <typename T>
RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const {
using namespace std;
- static_assert(is_base_of<Component, T>::value,
- "get_components_by_id must recieve a derivative class of Component");
+ static_assert(
+ is_base_of<Component, T>::value,
+ "get_components_by_id must recieve a derivative class of Component"
+ );
type_index type = typeid(T);
if (!this->components.contains(type)) return {};
@@ -170,8 +174,8 @@ ComponentManager::get_objects_by_predicate(const std::function<bool(const T &)>
}
template <typename T>
-RefVector<T>
-ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids) const {
+RefVector<T> ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids
+) const {
using namespace std;
RefVector<T> out = {};
diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h
index 639e37f..5766a0c 100644
--- a/src/crepe/manager/EventManager.h
+++ b/src/crepe/manager/EventManager.h
@@ -49,8 +49,8 @@ public:
* \return A unique subscription ID associated with the registered callback.
*/
template <typename EventType>
- subscription_t subscribe(const EventHandler<EventType> & callback,
- event_channel_t channel = CHANNEL_ALL);
+ subscription_t
+ subscribe(const EventHandler<EventType> & callback, event_channel_t channel = CHANNEL_ALL);
/**
* \brief Unsubscribe a previously registered callback.
@@ -106,7 +106,7 @@ private:
* \brief Represents an entry in the event queue.
*/
struct QueueEntry {
- std::unique_ptr<Event> event; ///< The event instance.
+ std::unique_ptr<Event, std::function<void(Event *)>> event; ///< The event instance.
event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event.
std::type_index type; ///< The type of the event.
};
diff --git a/src/crepe/manager/EventManager.hpp b/src/crepe/manager/EventManager.hpp
index a5f4556..1f44943 100644
--- a/src/crepe/manager/EventManager.hpp
+++ b/src/crepe/manager/EventManager.hpp
@@ -5,24 +5,31 @@
namespace crepe {
template <typename EventType>
-subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
- event_channel_t channel) {
+subscription_t
+EventManager::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) {
subscription_counter++;
std::type_index event_type = typeid(EventType);
std::unique_ptr<EventHandlerWrapper<EventType>> handler
= std::make_unique<EventHandlerWrapper<EventType>>(callback);
std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
- handlers.emplace_back(CallbackEntry{
- .callback = std::move(handler), .channel = channel, .id = subscription_counter});
+ handlers.emplace_back(CallbackEntry {
+ .callback = std::move(handler), .channel = channel, .id = subscription_counter
+ });
return subscription_counter;
}
template <typename EventType>
void EventManager::queue_event(const EventType & event, event_channel_t channel) {
- static_assert(std::is_base_of<Event, EventType>::value,
- "EventType must derive from Event");
+ static_assert(
+ std::is_base_of<Event, EventType>::value, "EventType must derive from Event"
+ );
this->events_queue.push_back(QueueEntry{
- .event = std::make_unique<EventType>(event),
+ // unique_ptr w/ custom destructor implementation is used because the base Event interface
+ // can't be polymorphic (= have default virtual destructor)
+ .event = {
+ new EventType(event),
+ [](Event * ev) { delete static_cast<EventType *>(ev); },
+ },
.channel = channel,
.type = typeid(EventType),
});
diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp
index a6e4788..b4cd07f 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"
@@ -17,9 +17,9 @@ LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) {
void LoopTimerManager::start() {
this->last_frame_time = std::chrono::steady_clock::now();
- this->elapsed_time = elapsed_time_t{0};
- this->elapsed_fixed_time = elapsed_time_t{0};
- this->delta_time = duration_t{0};
+ this->elapsed_time = elapsed_time_t {0};
+ this->elapsed_fixed_time = elapsed_time_t {0};
+ this->delta_time = duration_t {0};
}
void LoopTimerManager::update() {
diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h
index 76b02d3..279d6b2 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.
*
@@ -155,17 +157,17 @@ private:
//! Time scale for speeding up or slowing down the game (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up).
float time_scale = 1;
//! Maximum delta time in seconds to avoid large jumps.
- duration_t maximum_delta_time{0.25};
+ duration_t maximum_delta_time {0.25};
//! Delta time for the current frame in seconds.
- duration_t delta_time{0.0};
+ duration_t delta_time {0.0};
//! Target time per frame in seconds
- duration_t frame_target_time{1.0 / target_fps};
+ duration_t frame_target_time {1.0 / target_fps};
//! Fixed delta time for fixed updates in seconds.
- duration_t fixed_delta_time{1.0 / 50.0};
+ duration_t fixed_delta_time {1.0 / 50.0};
//! Total elapsed game time in microseconds.
- elapsed_time_t elapsed_time{0};
+ elapsed_time_t elapsed_time {0};
//! Total elapsed time for fixed updates in microseconds.
- elapsed_time_t elapsed_fixed_time{0};
+ elapsed_time_t elapsed_fixed_time {0};
typedef std::chrono::steady_clock::time_point time_point_t;
//! Time of the last frame.
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..f06a58b
--- /dev/null
+++ b/src/crepe/manager/ReplayManager.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <unordered_map>
+
+#include "../util/OptionalRef.h"
+
+#include "ComponentManager.h"
+#include "Manager.h"
+
+namespace crepe {
+
+//! Handle to recording held by ReplayManager
+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/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp
index cf5c949..4ca6be0 100644
--- a/src/crepe/manager/ResourceManager.hpp
+++ b/src/crepe/manager/ResourceManager.hpp
@@ -9,17 +9,20 @@ namespace crepe {
template <typename T>
T & ResourceManager::get(const Asset & asset) {
using namespace std;
- static_assert(is_base_of<Resource, T>::value,
- "cache must recieve a derivative class of Resource");
+ static_assert(
+ is_base_of<Resource, T>::value, "cache must recieve a derivative class of Resource"
+ );
CacheEntry & entry = this->get_entry(asset);
if (entry.resource == nullptr) entry.resource = make_unique<T>(asset, this->mediator);
T * concrete_resource = dynamic_cast<T *>(entry.resource.get());
if (concrete_resource == nullptr)
- throw runtime_error(format("ResourceManager: mismatch between requested type and "
- "actual type of resource ({})",
- asset.get_path()));
+ throw runtime_error(format(
+ "ResourceManager: mismatch between requested type and "
+ "actual type of resource ({})",
+ asset.get_path()
+ ));
return *concrete_resource;
}
diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp
index d4ca90b..e6f92db 100644
--- a/src/crepe/manager/SceneManager.cpp
+++ b/src/crepe/manager/SceneManager.cpp
@@ -17,10 +17,12 @@ 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.get()->get_name() == next_scene;
- });
+ auto it = find_if(
+ this->scenes.begin(), this->scenes.end(),
+ [&next_scene = this->next_scene](unique_ptr<Scene> & scene) {
+ return scene.get()->get_name() == next_scene;
+ }
+ );
// next scene not found
if (it == this->scenes.end()) return;
diff --git a/src/crepe/manager/SystemManager.cpp b/src/crepe/manager/SystemManager.cpp
new file mode 100644
index 0000000..eabc022
--- /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<ParticleSystem>();
+ this->load_system<AISystem>();
+ this->load_system<PhysicsSystem>();
+ this->load_system<CollisionSystem>();
+ this->load_system<AudioSystem>();
+ this->load_system<AnimatorSystem>();
+ this->load_system<RenderSystem>();
+ this->load_system<ReplaySystem>();
+
+ this->mediator.system_manager = *this;
+}
+
+void SystemManager::fixed_update() {
+ for (System & system : this->system_order) {
+ if (!system.active) continue;
+ system.fixed_update();
+ }
+}
+
+void SystemManager::frame_update() {
+ for (System & system : this->system_order) {
+ 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..614d90c
--- /dev/null
+++ b/src/crepe/manager/SystemManager.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#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 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::vector<std::reference_wrapper<System>> system_order;
+ /**
+ * \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..addd274 100644
--- a/src/crepe/api/LoopManager.hpp
+++ b/src/crepe/manager/SystemManager.hpp
@@ -4,26 +4,20 @@
#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");
+ 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,16 +27,18 @@ 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");
+ 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);
+ this->system_order.push_back(*this->systems[type]);
}
} // namespace crepe
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 680dbb8..94445c7 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;
@@ -28,7 +28,8 @@ void AISystem::update() {
= mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
if (rigidbodies.empty()) {
throw std::runtime_error(
- "AI component must be attached to a GameObject with a Rigidbody component");
+ "AI component must be attached to a GameObject with a Rigidbody component"
+ );
}
Rigidbody & rigidbody = rigidbodies.front().get();
if (!rigidbody.active) {
@@ -110,8 +111,8 @@ bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 & forc
return true;
}
-vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody,
- const Transform & transform) const {
+vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody, const Transform & transform)
+ const {
// Calculate the desired velocity
vec2 desired_velocity = ai.seek_target - transform.position;
desired_velocity.normalize();
@@ -120,12 +121,12 @@ vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody,
return desired_velocity - rigidbody.data.linear_velocity;
}
-vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody,
- const Transform & transform) const {
+vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody, const Transform & transform)
+ const {
// Calculate the desired velocity if the entity is within the panic distance
vec2 desired_velocity = transform.position - ai.flee_target;
if (desired_velocity.length_squared() > ai.square_flee_panic_distance) {
- return vec2{0, 0};
+ return vec2 {0, 0};
}
desired_velocity.normalize();
desired_velocity *= rigidbody.data.max_linear_velocity;
@@ -133,8 +134,8 @@ vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody,
return desired_velocity - rigidbody.data.linear_velocity;
}
-vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody,
- const Transform & transform) const {
+vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody, const Transform & transform)
+ const {
// Calculate the desired velocity (taking into account the deceleration rate)
vec2 to_target = ai.arrive_target - transform.position;
float distance = to_target.length();
@@ -150,12 +151,12 @@ vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody,
return desired_velocity - rigidbody.data.linear_velocity;
}
- return vec2{0, 0};
+ return vec2 {0, 0};
}
vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform) {
if (ai.path.empty()) {
- return vec2{0, 0};
+ return vec2 {0, 0};
}
// Get the target node
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..143d5d6 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,42 +1,44 @@
-
+#include <chrono>
#include "../api/Animator.h"
#include "../manager/ComponentManager.h"
#include "../manager/LoopTimerManager.h"
-#include <chrono>
#include "AnimatorSystem.h"
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>();
- float elapsed_time = duration_cast<duration<float>>(timer.get_elapsed_time()).count();
+ duration_t elapsed_time = timer.get_delta_time();
for (Animator & a : animations) {
if (!a.active) continue;
if (a.data.fps == 0) continue;
Animator::Data & ctx = a.data;
- float frame_duration = 1.0f / ctx.fps;
- int last_frame = ctx.row;
+ a.elapsed_time += elapsed_time;
+ duration_t frame_duration = 1000ms / ctx.fps;
int cycle_end = (ctx.cycle_end == -1) ? a.grid_size.x : ctx.cycle_end;
- int total_frames = cycle_end - ctx.cycle_start;
-
- int curr_frame = static_cast<int>(elapsed_time / frame_duration) % total_frames;
+ if (a.elapsed_time >= frame_duration) {
+ a.elapsed_time = 0ms;
+ a.frame++;
+ if (a.frame == cycle_end) {
+ a.frame = ctx.cycle_start;
+ if (!ctx.looping) {
+ a.active = false;
+ continue;
+ }
+ }
+ }
- ctx.row = ctx.cycle_start + curr_frame;
+ ctx.row = ctx.cycle_start + a.frame;
a.spritesheet.mask.x = ctx.row * a.spritesheet.mask.w;
- a.spritesheet.mask.y = (ctx.col * a.spritesheet.mask.h);
-
- if (!ctx.looping && curr_frame == ctx.cycle_start && last_frame == total_frames - 1) {
- a.active = false;
- }
}
}
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..3c2232f 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
@@ -36,6 +36,8 @@ void AudioSystem::diff_update(AudioSource & component, Sound & resource) {
if (component.oneshot_play) {
component.voice = context.play(resource);
+ context.set_loop(component.voice, component.loop);
+ context.set_volume(component.voice, component.volume);
component.oneshot_play = false;
}
if (component.oneshot_stop) {
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 3653d10..571ac70 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -34,7 +34,7 @@ CollisionSystem::CollisionInfo CollisionSystem::CollisionInfo::operator-() const
};
}
-void CollisionSystem::update() {
+void CollisionSystem::fixed_update() {
std::vector<CollisionInternal> all_colliders;
game_object_id_t id = 0;
ComponentManager & mgr = this->mediator.component_manager;
@@ -50,9 +50,11 @@ void CollisionSystem::update() {
for (BoxCollider & boxcollider : boxcolliders) {
if (boxcollider.game_object_id != id) continue;
if (!boxcollider.active) continue;
- all_colliders.push_back({.id = id,
- .collider = collider_variant{boxcollider},
- .info = {transform, rigidbody, metadata}});
+ all_colliders.push_back(
+ {.id = id,
+ .collider = collider_variant {boxcollider},
+ .info = {transform, rigidbody, metadata}}
+ );
}
// Check if the circlecollider is active and has the same id as the rigidbody.
RefVector<CircleCollider> circlecolliders
@@ -60,9 +62,11 @@ void CollisionSystem::update() {
for (CircleCollider & circlecollider : circlecolliders) {
if (circlecollider.game_object_id != id) continue;
if (!circlecollider.active) continue;
- all_colliders.push_back({.id = id,
- .collider = collider_variant{circlecollider},
- .info = {transform, rigidbody, metadata}});
+ all_colliders.push_back(
+ {.id = id,
+ .collider = collider_variant {circlecollider},
+ .info = {transform, rigidbody, metadata}}
+ );
}
}
@@ -110,8 +114,9 @@ CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) {
return collisions_ret;
}
-bool CollisionSystem::should_collide(const CollisionInternal & self,
- const CollisionInternal & other) const {
+bool CollisionSystem::should_collide(
+ const CollisionInternal & self, const CollisionInternal & other
+) const {
const Rigidbody::Data & self_rigidbody = self.info.rigidbody.data;
const Rigidbody::Data & other_rigidbody = other.info.rigidbody.data;
@@ -133,9 +138,9 @@ bool CollisionSystem::should_collide(const CollisionInternal & self,
return false;
}
-CollisionSystem::CollisionInternalType
-CollisionSystem::get_collider_type(const collider_variant & collider1,
- const collider_variant & collider2) const {
+CollisionSystem::CollisionInternalType CollisionSystem::get_collider_type(
+ const collider_variant & collider1, const collider_variant & collider2
+) const {
if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) {
if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
return CollisionInternalType::CIRCLE_CIRCLE;
@@ -151,8 +156,9 @@ CollisionSystem::get_collider_type(const collider_variant & collider1,
}
}
-bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionInternal & other,
- const CollisionInternalType & type) {
+bool CollisionSystem::detect_collision(
+ CollisionInternal & self, CollisionInternal & other, const CollisionInternalType & type
+) {
vec2 resolution;
switch (type) {
case CollisionInternalType::BOX_BOX: {
@@ -177,10 +183,11 @@ bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionIntern
= {.collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider),
.transform = self.info.transform,
.rigidbody = self.info.rigidbody};
- const CircleColliderInternal CIRCLE2 = {
- .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider),
- .transform = other.info.transform,
- .rigidbody = other.info.rigidbody};
+ const CircleColliderInternal CIRCLE2
+ = {.collider
+ = std::get<std::reference_wrapper<CircleCollider>>(other.collider),
+ .transform = other.info.transform,
+ .rigidbody = other.info.rigidbody};
// Get resolution vector from box-circle collision detection
resolution = this->get_box_circle_detection(BOX1, CIRCLE2);
// If no collision (NaN values), return false
@@ -195,10 +202,11 @@ bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionIntern
= {.collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider),
.transform = self.info.transform,
.rigidbody = self.info.rigidbody};
- const CircleColliderInternal CIRCLE2 = {
- .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider),
- .transform = other.info.transform,
- .rigidbody = other.info.rigidbody};
+ const CircleColliderInternal CIRCLE2
+ = {.collider
+ = std::get<std::reference_wrapper<CircleCollider>>(other.collider),
+ .transform = other.info.transform,
+ .rigidbody = other.info.rigidbody};
// Get resolution vector from circle-circle collision detection
resolution = this->get_circle_circle_detection(CIRCLE1, CIRCLE2);
// If no collision (NaN values), return false
@@ -239,9 +247,10 @@ bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionIntern
return true;
}
-vec2 CollisionSystem::get_box_box_detection(const BoxColliderInternal & box1,
- const BoxColliderInternal & box2) const {
- vec2 resolution{NAN, NAN};
+vec2 CollisionSystem::get_box_box_detection(
+ const BoxColliderInternal & box1, const BoxColliderInternal & box2
+) const {
+ vec2 resolution {NAN, NAN};
// Get current positions of colliders
vec2 pos1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset);
vec2 pos2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset);
@@ -282,8 +291,9 @@ vec2 CollisionSystem::get_box_box_detection(const BoxColliderInternal & box1,
return resolution;
}
-vec2 CollisionSystem::get_box_circle_detection(const BoxColliderInternal & box,
- const CircleColliderInternal & circle) const {
+vec2 CollisionSystem::get_box_circle_detection(
+ const BoxColliderInternal & box, const CircleColliderInternal & circle
+) const {
/// Get current positions of colliders
vec2 box_pos = AbsolutePosition::get_position(box.transform, box.collider.offset);
vec2 circle_pos = AbsolutePosition::get_position(circle.transform, circle.collider.offset);
@@ -324,14 +334,15 @@ vec2 CollisionSystem::get_box_circle_detection(const BoxColliderInternal & box,
float penetration_depth = scaled_circle_radius - distance;
// Compute the resolution vector
- return vec2{collision_normal * penetration_depth};
+ return vec2 {collision_normal * penetration_depth};
}
// No collision
- return vec2{NAN, NAN};
+ return vec2 {NAN, NAN};
}
vec2 CollisionSystem::get_circle_circle_detection(
- const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const {
+ const CircleColliderInternal & circle1, const CircleColliderInternal & circle2
+) const {
// Get current positions of colliders
vec2 final_position1
= AbsolutePosition::get_position(circle1.transform, circle1.collider.offset);
@@ -371,7 +382,7 @@ vec2 CollisionSystem::get_circle_circle_detection(
return resolution;
}
// No collision
- return vec2{NAN, NAN};
+ return vec2 {NAN, NAN};
;
}
@@ -402,17 +413,17 @@ CollisionSystem::resolution_correction(vec2 & resolution, const Rigidbody::Data
return resolution_direction;
}
-CollisionSystem::CollisionInfo
-CollisionSystem::get_collision_info(const CollisionInternal & in_self,
- const CollisionInternal & in_other) const {
+CollisionSystem::CollisionInfo CollisionSystem::get_collision_info(
+ const CollisionInternal & in_self, const CollisionInternal & in_other
+) const {
- crepe::CollisionSystem::ColliderInfo self{
+ crepe::CollisionSystem::ColliderInfo self {
.transform = in_self.info.transform,
.rigidbody = in_self.info.rigidbody,
.metadata = in_self.info.metadata,
};
- crepe::CollisionSystem::ColliderInfo other{
+ crepe::CollisionSystem::ColliderInfo other {
.transform = in_other.info.transform,
.rigidbody = in_other.info.rigidbody,
.metadata = in_other.info.metadata,
diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h
index 7be280a..ff2d35f 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -60,8 +60,8 @@ public:
private:
//! A variant type that can hold either a BoxCollider or a CircleCollider.
- using collider_variant = std::variant<std::reference_wrapper<BoxCollider>,
- std::reference_wrapper<CircleCollider>>;
+ using collider_variant = std::variant<
+ std::reference_wrapper<BoxCollider>, std::reference_wrapper<CircleCollider>>;
//! Enum representing the types of collider pairs for collision detection.
enum class CollisionInternalType {
@@ -102,7 +102,7 @@ private:
public:
//! Updates the collision system by checking for collisions between colliders and handling them.
- void update() override;
+ void fixed_update() override;
private:
/**
@@ -114,8 +114,9 @@ private:
* \param collider2 Second collider variant (BoxCollider or CircleCollider).
* \return The combined type of the two colliders.
*/
- CollisionInternalType get_collider_type(const collider_variant & collider1,
- const collider_variant & collider2) const;
+ CollisionInternalType get_collider_type(
+ const collider_variant & collider1, const collider_variant & collider2
+ ) const;
private:
/**
@@ -128,8 +129,8 @@ private:
* \param data1 Collision data for the first collider.
* \param data2 Collision data for the second collider.
*/
- CollisionInfo get_collision_info(const CollisionInternal & data1,
- const CollisionInternal & data2) const;
+ CollisionInfo
+ get_collision_info(const CollisionInternal & data1, const CollisionInternal & data2) const;
/**
* \brief Corrects the collision resolution vector and determines its direction.
@@ -221,8 +222,10 @@ private:
* \param other_metadata Rigidbody of second object
* \return Returns true if there is at least one comparison found.
*/
- bool should_collide(const CollisionInternal & self,
- const CollisionInternal & other) const; //done
+ bool should_collide(
+ const CollisionInternal & self,
+ const CollisionInternal & other
+ ) const; //done
/**
* \brief Checks for collision between two colliders.
@@ -236,8 +239,10 @@ private:
* \param type The type of collider pair.
* \return True if a collision is detected, otherwise false.
*/
- bool detect_collision(CollisionInternal & first_info, CollisionInternal & second_info,
- const CollisionInternalType & type);
+ bool detect_collision(
+ CollisionInternal & first_info, CollisionInternal & second_info,
+ const CollisionInternalType & type
+ );
/**
* \brief Detects collisions between two BoxColliders.
@@ -250,8 +255,9 @@ private:
* \param box2 Information about the second BoxCollider.
* \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- vec2 get_box_box_detection(const BoxColliderInternal & box1,
- const BoxColliderInternal & box2) const;
+ vec2 get_box_box_detection(
+ const BoxColliderInternal & box1, const BoxColliderInternal & box2
+ ) const;
/**
* \brief Check collision for box on circle collider
@@ -264,8 +270,9 @@ private:
* \param circle2 Information about the circleCollider.
* \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- vec2 get_box_circle_detection(const BoxColliderInternal & box1,
- const CircleColliderInternal & circle2) const;
+ vec2 get_box_circle_detection(
+ const BoxColliderInternal & box1, const CircleColliderInternal & circle2
+ ) const;
/**
* \brief Check collision for circle on circle collider
@@ -278,8 +285,9 @@ private:
* \param circle2 Information about the second circleCollider.
* \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- vec2 get_circle_circle_detection(const CircleColliderInternal & circle1,
- const CircleColliderInternal & circle2) const;
+ vec2 get_circle_circle_detection(
+ const CircleColliderInternal & circle1, const CircleColliderInternal & circle2
+ ) const;
};
/**
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 60daa55..be7eda6 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -1,18 +1,18 @@
#include "../api/Button.h"
+#include "../api/Config.h"
#include "../facade/SDLContext.h"
#include "../manager/ComponentManager.h"
#include "../manager/EventManager.h"
-#include "util/Log.h"
#include "InputSystem.h"
using namespace crepe;
-void InputSystem::update() {
+void InputSystem::fixed_update() {
ComponentManager & mgr = this->mediator.component_manager;
SDLContext & context = this->mediator.sdl_context;
std::vector<EventData> event_list = context.get_events();
- RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
+ const RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
OptionalRef<Camera> curr_cam_ref;
// Find the active camera
@@ -24,9 +24,8 @@ void InputSystem::update() {
if (!curr_cam_ref) return;
Camera & current_cam = curr_cam_ref;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(current_cam.game_object_id);
- Transform & cam_transform = transform_vec.front().get();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
vec2 camera_origin = cam_transform.position + current_cam.data.postion_offset
- (current_cam.viewport_size / 2);
@@ -45,12 +44,13 @@ void InputSystem::update() {
}
}
-void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camera_origin,
- const Camera & current_cam) {
+void InputSystem::handle_mouse_event(
+ const EventData & event, const vec2 & camera_origin, const Camera & current_cam
+) {
EventManager & event_mgr = this->mediator.event_manager;
vec2 adjusted_mouse;
adjusted_mouse.x = event.data.mouse_data.mouse_position.x + camera_origin.x;
- adjusted_mouse.x = event.data.mouse_data.mouse_position.y + camera_origin.y;
+ adjusted_mouse.y = event.data.mouse_data.mouse_position.y + camera_origin.y;
// Check if the mouse is within the viewport
if ((adjusted_mouse.x < camera_origin.x
|| adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x
@@ -83,7 +83,9 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
.mouse_pos = adjusted_mouse,
.button = event.data.mouse_data.mouse_button,
});
- this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse);
+ this->handle_click(
+ event.data.mouse_data.mouse_button, adjusted_mouse, current_cam
+ );
}
break;
}
@@ -93,7 +95,7 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
.mouse_pos = adjusted_mouse,
.mouse_delta = event.data.mouse_data.rel_mouse_move,
});
- this->handle_move(event, adjusted_mouse);
+ this->handle_move(event, adjusted_mouse, current_cam);
break;
case EventType::MOUSE_WHEEL:
@@ -115,7 +117,8 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
case EventType::KEY_DOWN:
event_mgr.queue_event<KeyPressEvent>(
- {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key});
+ {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key}
+ );
break;
case EventType::KEY_UP:
event_mgr.queue_event<KeyReleaseEvent>({.key = event.data.key_data.key});
@@ -128,11 +131,13 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
break;
case EventType::WINDOW_RESIZE:
event_mgr.queue_event<WindowResizeEvent>(
- WindowResizeEvent{.dimensions = event.data.window_data.resize_dimension});
+ WindowResizeEvent {.dimensions = event.data.window_data.resize_dimension}
+ );
break;
case EventType::WINDOW_MOVE:
event_mgr.queue_event<WindowMoveEvent>(
- {.delta_move = event.data.window_data.move_delta});
+ {.delta_move = event.data.window_data.move_delta}
+ );
break;
case EventType::WINDOW_MINIMIZE:
event_mgr.queue_event<WindowMinimizeEvent>({});
@@ -151,59 +156,70 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
}
}
-void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos) {
+void InputSystem::handle_move(
+ const EventData & event_data, const vec2 & mouse_pos, const Camera & current_cam
+) {
ComponentManager & mgr = this->mediator.component_manager;
EventManager & event_mgr = this->mediator.event_manager;
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
+ const RefVector<Button> buttons = mgr.get_components_by_type<Button>();
for (Button & button : buttons) {
if (!button.active) continue;
- Metadata & metadata
- = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
- Transform & transform
+
+ const Transform & transform
= mgr.get_components_by_id<Transform>(button.game_object_id).front();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
+ const Metadata & metadata
+ = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
bool was_hovering = button.hover;
- if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
+
+ if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) {
button.hover = true;
if (!was_hovering) {
- event_mgr.trigger_event<ButtonEnterEvent>(metadata);
+ event_mgr.trigger_event<ButtonEnterEvent>(metadata, metadata.game_object_id);
}
} else {
button.hover = false;
if (was_hovering) {
- event_mgr.trigger_event<ButtonExitEvent>(metadata);
+ event_mgr.trigger_event<ButtonExitEvent>(metadata, metadata.game_object_id);
}
}
}
}
-void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) {
+void InputSystem::handle_click(
+ const MouseButton & mouse_button, const vec2 & mouse_pos, const Camera & current_cam
+) {
ComponentManager & mgr = this->mediator.component_manager;
EventManager & event_mgr = this->mediator.event_manager;
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
-
+ const RefVector<Button> buttons = mgr.get_components_by_type<Button>();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
for (Button & button : buttons) {
if (!button.active) continue;
- Metadata & metadata
+ const Metadata & metadata
= mgr.get_components_by_id<Metadata>(button.game_object_id).front();
- Transform & transform
+ const Transform & transform
= mgr.get_components_by_id<Transform>(button.game_object_id).front();
-
- if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
- event_mgr.trigger_event<ButtonPressEvent>(metadata);
+ if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) {
+ event_mgr.trigger_event<ButtonPressEvent>(metadata, metadata.game_object_id);
}
}
}
-bool InputSystem::is_mouse_inside_button(const vec2 & mouse_pos, const Button & button,
- const Transform & transform) {
- int actual_x = transform.position.x + button.offset.x;
- int actual_y = transform.position.y + button.offset.y;
-
- int half_width = button.dimensions.x / 2;
- int half_height = button.dimensions.y / 2;
+bool InputSystem::is_mouse_inside_button(
+ const vec2 & mouse_pos, const Button & button, const Transform & transform,
+ const Transform & cam_transform
+) {
+ vec2 actual_pos = transform.position + button.offset;
+ if (!button.data.world_space) {
+ actual_pos += cam_transform.position;
+ }
+ vec2 half_dimensions = button.dimensions * transform.scale / 2;
- // Check if the mouse is within the button's boundaries
- return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width
- && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height;
+ return mouse_pos.x >= actual_pos.x - half_dimensions.x
+ && mouse_pos.x <= actual_pos.x + half_dimensions.x
+ && mouse_pos.y >= actual_pos.y - half_dimensions.y
+ && mouse_pos.y <= actual_pos.y + half_dimensions.y;
}
diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h
index e580d8e..be62367 100644
--- a/src/crepe/system/InputSystem.h
+++ b/src/crepe/system/InputSystem.h
@@ -1,12 +1,9 @@
#pragma once
-#include "../api/Config.h"
-#include "../facade/EventData.h"
-
#include "../api/Event.h"
#include "../api/Metadata.h"
+#include "../facade/EventData.h"
#include "../types.h"
-#include "../util/OptionalRef.h"
#include "System.h"
@@ -23,7 +20,7 @@ public:
/**
* \param metadata Metadata of the button pressed
*/
- ButtonPressEvent(const Metadata & metadata) : metadata(metadata){};
+ ButtonPressEvent(const Metadata & metadata) : metadata(metadata) {};
};
//! Event triggered when the mouse enters a button
class ButtonEnterEvent : public Event {
@@ -33,7 +30,7 @@ public:
/**
* \param metadata Metadata of the button pressed
*/
- ButtonEnterEvent(const Metadata & metadata) : metadata(metadata){};
+ ButtonEnterEvent(const Metadata & metadata) : metadata(metadata) {};
};
//! Event triggered when the mouse leaves a button
class ButtonExitEvent : public Event {
@@ -43,7 +40,7 @@ public:
/**
* \param metadata Metadata of the button pressed
*/
- ButtonExitEvent(const Metadata & metadata) : metadata(metadata){};
+ ButtonExitEvent(const Metadata & metadata) : metadata(metadata) {};
};
/**
@@ -61,7 +58,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.
@@ -79,8 +76,9 @@ private:
* This method processes mouse events, adjusts the mouse position to world coordinates,
* and triggers the appropriate mouse-specific event handling logic.
*/
- void handle_mouse_event(const EventData & event, const vec2 & camera_origin,
- const Camera & current_cam);
+ void handle_mouse_event(
+ const EventData & event, const vec2 & camera_origin, const Camera & current_cam
+ );
/**
* \brief Handles non-mouse-related events.
* \param event The event data for the non-mouse event.
@@ -94,20 +92,26 @@ private:
* \param mouse_button The mouse button involved in the click.
* \param world_mouse_x The X coordinate of the mouse in world space.
* \param world_mouse_y The Y coordinate of the mouse in world space.
+ * \param current_cam The current active camera.
*
* This method processes the mouse click event and triggers the corresponding button action.
*/
- void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos);
+ void handle_click(
+ const MouseButton & mouse_button, const vec2 & mouse_pos, const Camera & current_cam
+ );
/**
* \brief Handles the mouse movement event.
* \param event_data The event data containing information about the mouse movement.
* \param world_mouse_x The X coordinate of the mouse in world space.
* \param world_mouse_y The Y coordinate of the mouse in world space.
+ * \param current_cam The current active camera.
*
* This method processes the mouse movement event and updates the button hover state.
*/
- void handle_move(const EventData & event_data, const vec2 & mouse_pos);
+ void handle_move(
+ const EventData & event_data, const vec2 & mouse_pos, const Camera & current_cam
+ );
/**
* \brief Checks if the mouse position is inside the bounds of the button.
@@ -115,10 +119,13 @@ private:
* \param world_mouse_y The Y coordinate of the mouse in world space.
* \param button The button to check.
* \param transform The transform component of the button.
+ * \param cam_transform the transform of the current active camera
* \return True if the mouse is inside the button, false otherwise.
*/
- bool is_mouse_inside_button(const vec2 & mouse_pos, const Button & button,
- const Transform & transform);
+ bool is_mouse_inside_button(
+ const vec2 & mouse_pos, const Button & button, const Transform & transform,
+ const Transform & cam_transform
+ );
/**
* \brief Handles the button press event, calling the on_click callback if necessary.
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index e66c603..f026390 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -13,7 +13,7 @@
using namespace crepe;
-void ParticleSystem::update() {
+void ParticleSystem::fixed_update() {
// Get all emitters
const Mediator & mediator = this->mediator;
LoopTimerManager & loop_timer = mediator.loop_timer;
@@ -50,9 +50,10 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform &
constexpr float DEG_TO_RAD = M_PI / 180.0;
vec2 initial_position = AbsolutePosition::get_position(transform, emitter.data.offset);
- float random_angle
- = this->generate_random_angle(emitter.data.min_angle + transform.rotation,
- emitter.data.max_angle + transform.rotation);
+ float random_angle = this->generate_random_angle(
+ emitter.data.min_angle + transform.rotation,
+ emitter.data.max_angle + transform.rotation
+ );
float random_speed
= this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
@@ -63,8 +64,9 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform &
for (Particle & particle : emitter.particles) {
if (!particle.active) {
- particle.reset(emitter.data.end_lifespan, initial_position, velocity,
- random_angle);
+ particle.reset(
+ emitter.data.end_lifespan, initial_position, velocity, random_angle
+ );
break;
}
}
@@ -82,8 +84,9 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & t
for (Particle & particle : emitter.particles) {
const vec2 & position = particle.position;
- bool within_bounds = (position.x >= left && position.x <= right && position.y >= top
- && position.y <= bottom);
+ bool within_bounds
+ = (position.x >= left && position.x <= right && position.y >= top
+ && position.y <= bottom);
//if not within bounds do a reset or stop velocity
if (!within_bounds) {
if (emitter.data.boundary.reset_on_exit) {
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 154521d..4296ff3 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 fixed_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 8c31743..30bb422 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -70,7 +70,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->render_text();
@@ -92,7 +92,7 @@ void RenderSystem::render_text() {
const Font & font = resource_manager.get<Font>(text.font.value());
const auto & transform
= mgr.get_components_by_id<Transform>(text.game_object_id).front().get();
- ctx.draw_text(SDLContext::RenderText{
+ ctx.draw_text(SDLContext::RenderText {
.text = text,
.font = font,
.transform = transform,
@@ -120,7 +120,7 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Transform & tran
if (!p.active) continue;
if (p.time_in_life < em.data.begin_lifespan) continue;
- ctx.draw(SDLContext::RenderContext{
+ ctx.draw(SDLContext::RenderContext {
.sprite = sprite,
.texture = res,
.pos = p.position,
@@ -136,7 +136,7 @@ void RenderSystem::render_normal(const Sprite & sprite, const Transform & transf
ResourceManager & resource_manager = this->mediator.resource_manager;
const Texture & res = resource_manager.get<Texture>(sprite.source);
vec2 pos = AbsolutePosition::get_position(transform, sprite.data.position_offset);
- ctx.draw(SDLContext::RenderContext{
+ ctx.draw(SDLContext::RenderContext {
.sprite = sprite,
.texture = res,
.pos = pos,
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index 14e5c2d..627a743 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 0605c7a..ed0c7cc 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -7,11 +7,22 @@
using namespace std;
using namespace crepe;
-void ScriptSystem::update() {
- dbg_trace();
+void ScriptSystem::fixed_update() {
+ LoopTimerManager & timer = this->mediator.loop_timer;
+ duration_t delta_time = timer.get_scaled_fixed_delta_time();
+ this->update(&Script::fixed_update, delta_time);
+}
- ComponentManager & mgr = this->mediator.component_manager;
+void ScriptSystem::frame_update() {
LoopTimerManager & timer = this->mediator.loop_timer;
+ duration_t delta_time = timer.get_delta_time();
+ this->update(&Script::frame_update, delta_time);
+}
+
+void ScriptSystem::update(
+ void (Script::*update_function)(duration_t), const duration_t & delta_time
+) {
+ ComponentManager & mgr = this->mediator.component_manager;
RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>();
for (BehaviorScript & behavior_script : behavior_scripts) {
@@ -25,7 +36,6 @@ void ScriptSystem::update() {
script->initialized = true;
}
- duration_t delta_time = timer.get_scaled_fixed_delta_time();
- script->update(delta_time);
+ (*script.*update_function)(delta_time);
}
}
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index 3db1b1e..257b615 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -1,5 +1,7 @@
#pragma once
+#include "../manager/LoopTimerManager.h"
+
#include "System.h"
namespace crepe {
@@ -9,20 +11,27 @@ class Script;
/**
* \brief Script system
*
- * The script system is responsible for all \c BehaviorScript components, and
- * calls the methods on classes derived from \c Script.
+ * The script system is responsible for all \c BehaviorScript components, and calls the methods
+ * on classes derived from \c Script.
*/
class ScriptSystem : public System {
public:
using System::System;
+
+public:
+ //! Call Script::fixed_update() on all active \c BehaviorScript instances
+ void fixed_update() override;
+ //! Call Script::frame_update() on all active \c BehaviorScript instances
+ void frame_update() override;
+
+private:
/**
- * \brief Call Script::update() on all active \c BehaviorScript instances
+ * \brief Call Script `*_update` member function on all active \c BehaviorScript instances
*
- * This routine updates all scripts sequentially using the Script::update()
- * method. It also calls Script::init() if this has not been done before on
- * the \c BehaviorScript instance.
+ * \note This routine also calls Script::init() if this has not been done before on the \c
+ * BehaviorScript instance.
*/
- void update() override;
+ void update(void (Script::*update_function)(duration_t), const duration_t & delta_time);
};
} // 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..e448070
--- /dev/null
+++ b/src/crepe/util/dbg.h
@@ -0,0 +1,21 @@
+#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