aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorheavydemon21 <nielsstunnebrink1@gmail.com>2024-12-19 13:58:58 +0100
committerheavydemon21 <nielsstunnebrink1@gmail.com>2024-12-19 13:58:58 +0100
commitb5c4879189a8ff55434c8c04c14163dfda83de65 (patch)
tree17595782d04038818f7ed4f6bb2199949ebd30a0 /src
parentebe2fcfce52d9a303c5fa19d79554e20ac7f0bac (diff)
parent794efc4ef7a44b190a4d9ecc2dd84a66c62ab005 (diff)
Merge branch 'master' into niels/UI
Diffstat (limited to 'src')
-rw-r--r--src/crepe/api/Button.cpp6
-rw-r--r--src/crepe/api/Button.h49
-rw-r--r--src/crepe/api/Event.h12
-rw-r--r--src/crepe/api/LoopManager.h19
-rw-r--r--src/crepe/api/Script.cpp3
-rw-r--r--src/crepe/api/Script.h10
-rw-r--r--src/crepe/api/Sprite.h10
-rw-r--r--src/crepe/facade/DB.cpp12
-rw-r--r--src/crepe/facade/DB.h2
-rw-r--r--src/crepe/facade/SDLContext.cpp15
-rw-r--r--src/crepe/manager/SaveManager.cpp49
-rw-r--r--src/crepe/manager/SaveManager.h6
-rw-r--r--src/crepe/system/InputSystem.cpp32
-rw-r--r--src/crepe/system/InputSystem.h33
-rw-r--r--src/crepe/system/RenderSystem.cpp3
-rw-r--r--src/crepe/system/ScriptSystem.cpp5
-rw-r--r--src/doc/feature/proxy.dox43
-rw-r--r--src/doc/feature/savemgr.dox80
-rw-r--r--src/example/game.cpp2
-rw-r--r--src/example/rendering_particle.cpp2
-rw-r--r--src/test/CollisionTest.cpp1
-rw-r--r--src/test/DBTest.cpp8
-rw-r--r--src/test/InputTest.cpp54
-rw-r--r--src/test/SaveManagerTest.cpp17
-rw-r--r--src/test/ScriptTest.cpp22
-rw-r--r--src/test/ScriptTest.h7
-rw-r--r--src/test/main.cpp3
27 files changed, 356 insertions, 149 deletions
diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp
index 305922c..40153c9 100644
--- a/src/crepe/api/Button.cpp
+++ b/src/crepe/api/Button.cpp
@@ -2,9 +2,7 @@
namespace crepe {
-Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
- const std::function<void()> & on_click)
- : UIObject(id, dimensions, offset),
- on_click(on_click) {}
+Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset)
+ : UIObject(id, dimensions, offset) {}
} // namespace crepe
diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h
index 08f5dec..d42527e 100644
--- a/src/crepe/api/Button.h
+++ b/src/crepe/api/Button.h
@@ -2,11 +2,23 @@
#include <functional>
+#include "Event.h"
#include "UIObject.h"
namespace crepe {
-//! Represents a clickable UI button, derived from the UiObject class.
+/**
+ * \brief Button component.
+ *
+ * This component creates a clickable surface at the transform location with the specified width and height.
+ *
+ * The Button can be used in scripts by subscribing a EventHandler to the following events:
+ * - ButtonPressEvent
+ * - ButtonEnterEvent
+ * - ButtonExitEvent
+ * \see EventManager
+ *
+ */
class Button : public UIObject {
public:
/**
@@ -15,43 +27,22 @@ public:
* \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 on_click callback function that will be invoked when the button is clicked.
*/
- Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
- const std::function<void()> & on_click);
-
- // TODO: create separate toggle button class
- /**
- * \brief The callback function to be executed when the button is clicked.
- *
- * This function is invoked whenever the button is clicked. It can be set to any
- * function that matches the signature `void()`.
- */
- std::function<void()> on_click = nullptr;
-
+ Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset);
/**
- * \brief Callback function to be executed when the mouse enters the button's boundaries.
+ * \brief Get the maximum number of instances for this component
*
- * This function is triggered when the mouse cursor moves over the button, allowing
- * custom actions like visual effects, highlighting, or sound effects.
+ * Since the button Event transfers the GameObject Metadata it will be the same for each button so only one button is allowed per GameObject
+ *
+ * \return 1
*/
- std::function<void()> on_mouse_enter = nullptr;
-
- /**
- * \brief Callback function to be executed when the mouse exits the button's boundaries.
- *
- * This function is triggered when the mouse cursor moves out of the button's area,
- * allowing custom actions like resetting visual effects or playing exit-related effects.
- */
- std::function<void()> on_mouse_exit = nullptr;
+ virtual int get_instances_max() const { return 1; }
private:
- //! friend relation for is_pressed and hover variables
+ //! friend relation hover variable
friend class InputSystem;
//! Indicates whether the mouse is currently hovering over the button
bool hover = false;
-
-public:
};
} // namespace crepe
diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h
index 73bf461..8e38280 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -3,9 +3,10 @@
#include <string>
-#include "api/KeyCodes.h"
#include "types.h"
+#include "KeyCodes.h"
+
namespace crepe {
/**
@@ -95,15 +96,6 @@ public:
};
/**
- * \brief Event triggered when text is submitted, e.g., from a text input.
- */
-class TextSubmitEvent : public Event {
-public:
- //! The submitted text.
- std::string text = "";
-};
-
-/**
* \brief Event triggered to indicate the application is shutting down.
*/
class ShutDownEvent : public Event {};
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index 1d23cbf..124cd3a 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -71,9 +71,19 @@ private:
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
@@ -82,8 +92,6 @@ private:
LoopTimerManager loop_timer{mediator};
//! EventManager instance
EventManager event_manager{mediator};
- //! Resource manager instance
- ResourceManager resource_manager{mediator};
//! Save manager instance
SaveManager save_manager{mediator};
@@ -95,13 +103,6 @@ private:
*/
bool on_shutdown(const ShutDownEvent & e);
/**
- * \brief Collection of System instances
- *
- * This map holds System instances indexed by the system's class typeid. It is filled in the
- * constructor of LoopManager using LoopManager::load_system.
- */
- std::unordered_map<std::type_index, std::unique_ptr<System>> systems;
- /**
* \brief Initialize a system
* \tparam T System type (must be derivative of \c System)
*/
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index 583c04f..85016f5 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -26,6 +26,8 @@ void Script::set_next_scene(const string & name) {
SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; }
+LoopTimerManager & Script::get_loop_timer() const { return this->mediator->loop_timer; }
+
const keyboard_state_t & Script::get_keyboard_state() const {
SDLContext & sdl_context = this->mediator->sdl_context;
return sdl_context.get_keyboard_state();
@@ -38,3 +40,4 @@ bool Script::get_key_state(Keycode key) const noexcept {
return false;
}
}
+
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index 65306cd..a87af4e 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -4,6 +4,7 @@
#include "../api/KeyCodes.h"
#include "../manager/EventManager.h"
+#include "../manager/LoopTimerManager.h"
#include "../manager/Mediator.h"
#include "../system/CollisionSystem.h"
#include "../types.h"
@@ -47,10 +48,12 @@ protected:
/**
* \brief Script 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.
*/
- virtual void update() {}
+ virtual void update(duration_t delta_time) {}
//! \}
//! ScriptSystem calls \c init() and \c update()
@@ -135,6 +138,10 @@ protected:
//! Retrieve SaveManager reference
SaveManager & get_save_manager() const;
+
+ //! Retrieve LoopTimerManager reference
+ LoopTimerManager & get_loop_timer() const;
+
/**
* \brief Utility function to retrieve the keyboard state
* \see SDLContext::get_keyboard_state
@@ -151,7 +158,6 @@ protected:
*
*/
bool get_key_state(Keycode key) const noexcept;
- //! \}
private:
/**
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index a2409c2..a3fc319 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -66,6 +66,16 @@ public:
//! independent sprite offset position
vec2 position_offset;
+
+ /**
+ * \brief gives the user the option to render this in world space or in camera space
+ *
+ * - if true will this be rendered in world space this means that the sprite can be
+ * rendered off the screen
+ * - if false --> will the sprite be rendered in camera space. this means that the
+ * coordinates given on the \c Sprite and \c Transform will be inside the camera
+ */
+ bool world_space = true;
};
public:
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index 95cf606..ae2d4bc 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -21,12 +21,6 @@ DB::DB(const string & path) {
const char * file = path.empty() ? NULL : path.c_str();
ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0);
if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret)));
-
- // create cursor
- libdb::DBC * cursor;
- ret = this->db->cursor(this->db.get(), NULL, &cursor, 0);
- if (ret != 0) throw runtime_error(format("db->cursor: {}", libdb::db_strerror(ret)));
- this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }};
}
libdb::DBT DB::to_thing(const string & thing) const noexcept {
@@ -42,10 +36,10 @@ string DB::get(const string & key) {
libdb::DBT db_val;
memset(&db_val, 0, sizeof(libdb::DBT));
- int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST);
+ int ret = this->db->get(this->db.get(), NULL, &db_key, &db_val, 0);
if (ret == 0) return {static_cast<char *>(db_val.data), db_val.size};
- string err = format("cursor->get: {}", libdb::db_strerror(ret));
+ string err = format("db->get: {}", libdb::db_strerror(ret));
if (ret == DB_NOTFOUND) throw out_of_range(err);
else throw runtime_error(err);
}
@@ -54,7 +48,7 @@ void DB::set(const string & key, const string & value) {
libdb::DBT db_key = this->to_thing(key);
libdb::DBT db_val = this->to_thing(value);
int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0);
- if (ret != 0) throw runtime_error(format("cursor->get: {}", libdb::db_strerror(ret)));
+ if (ret != 0) throw runtime_error(format("db->get: {}", libdb::db_strerror(ret)));
}
bool DB::has(const std::string & key) {
diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h
index 115c0f1..84cdf19 100644
--- a/src/crepe/facade/DB.h
+++ b/src/crepe/facade/DB.h
@@ -61,8 +61,6 @@ public:
private:
//! RAII wrapper around \c DB struct
std::unique_ptr<libdb::DB, std::function<void(libdb::DB *)>> db;
- //! RAII wrapper around \c DBC struct
- std::unique_ptr<libdb::DBC, std::function<void(libdb::DBC *)>> cursor;
private:
/**
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index d352adc..642aef8 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -141,6 +141,8 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
= (ctx.sprite.aspect_ratio == 0) ? ctx.texture.get_ratio() : ctx.sprite.aspect_ratio;
vec2 size = data.size;
+ vec2 screen_pos = ctx.pos + data.position_offset;
+
if (data.size.x == 0 && data.size.y != 0) {
size.x = data.size.y * aspect_ratio;
}
@@ -149,9 +151,16 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
}
size *= cam_aux_data.render_scale * ctx.img_scale * data.scale_offset;
- vec2 screen_pos = (ctx.pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2)
- * cam_aux_data.render_scale
- - size / 2 + cam_aux_data.bar_size;
+ if (ctx.sprite.data.world_space) {
+ vec2 multiplier = cam_aux_data.cam_pos
+ + (cam_aux_data.zoomed_viewport / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ screen_pos += multiplier;
+ } else {
+ vec2 multiplier = (cam_aux_data.zoomed_viewport / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ screen_pos += multiplier;
+ }
return SDL_FRect{
.x = screen_pos.x,
diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp
index 691ea2f..f313ed2 100644
--- a/src/crepe/manager/SaveManager.cpp
+++ b/src/crepe/manager/SaveManager.cpp
@@ -129,9 +129,32 @@ template void SaveManager::set(const string &, const float &);
template void SaveManager::set(const string &, const double &);
template <typename T>
+T SaveManager::get(const string & key) {
+ return this->deserialize<T>(this->get_db().get(key));
+}
+template uint8_t SaveManager::get(const string &);
+template int8_t SaveManager::get(const string &);
+template uint16_t SaveManager::get(const string &);
+template int16_t SaveManager::get(const string &);
+template uint32_t SaveManager::get(const string &);
+template int32_t SaveManager::get(const string &);
+template uint64_t SaveManager::get(const string &);
+template int64_t SaveManager::get(const string &);
+template float SaveManager::get(const string &);
+template double SaveManager::get(const string &);
+template string SaveManager::get(const string &);
+
+template <typename T>
ValueBroker<T> SaveManager::get(const string & key, const T & default_value) {
if (!this->has(key)) this->set<T>(key, default_value);
- return this->get<T>(key);
+ T value;
+ return {
+ [this, key](const T & target) { this->set<T>(key, target); },
+ [this, key, value]() mutable -> const T & {
+ value = this->get<T>(key);
+ return value;
+ },
+ };
}
template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);
template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &);
@@ -144,27 +167,3 @@ template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);
template ValueBroker<float> SaveManager::get(const string &, const float &);
template ValueBroker<double> SaveManager::get(const string &, const double &);
template ValueBroker<string> SaveManager::get(const string &, const string &);
-
-template <typename T>
-ValueBroker<T> SaveManager::get(const string & key) {
- T value;
- return {
- [this, key](const T & target) { this->set<T>(key, target); },
- [this, key, value]() mutable -> const T & {
- DB & db = this->get_db();
- value = this->deserialize<T>(db.get(key));
- return value;
- },
- };
-}
-template ValueBroker<uint8_t> SaveManager::get(const string &);
-template ValueBroker<int8_t> SaveManager::get(const string &);
-template ValueBroker<uint16_t> SaveManager::get(const string &);
-template ValueBroker<int16_t> SaveManager::get(const string &);
-template ValueBroker<uint32_t> SaveManager::get(const string &);
-template ValueBroker<int32_t> SaveManager::get(const string &);
-template ValueBroker<uint64_t> SaveManager::get(const string &);
-template ValueBroker<int64_t> SaveManager::get(const string &);
-template ValueBroker<float> SaveManager::get(const string &);
-template ValueBroker<double> SaveManager::get(const string &);
-template ValueBroker<string> SaveManager::get(const string &);
diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h
index 61a978d..1e34bc0 100644
--- a/src/crepe/manager/SaveManager.h
+++ b/src/crepe/manager/SaveManager.h
@@ -36,17 +36,17 @@ public:
ValueBroker<T> get(const std::string & key, const T & default_value);
/**
- * \brief Get a read/write reference to a value
+ * \brief Get a value directly
*
* \param key The value key
*
- * \return Read/write reference to the value
+ * \return The value
*
* \note Attempting to read this value before it is initialized (i.e. set) will result in an
* exception
*/
template <typename T>
- ValueBroker<T> get(const std::string & key);
+ T get(const std::string & key);
/**
* \brief Set a value directly
diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp
index fca540f..60daa55 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -10,10 +10,8 @@ using namespace crepe;
void InputSystem::update() {
ComponentManager & mgr = this->mediator.component_manager;
-
SDLContext & context = this->mediator.sdl_context;
std::vector<EventData> event_list = context.get_events();
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
OptionalRef<Camera> curr_cam_ref;
@@ -155,28 +153,25 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos) {
ComponentManager & mgr = this->mediator.component_manager;
-
+ EventManager & event_mgr = this->mediator.event_manager;
RefVector<Button> buttons = mgr.get_components_by_type<Button>();
for (Button & button : buttons) {
if (!button.active) continue;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(button.game_object_id);
- Transform & transform(transform_vec.front().get());
-
+ Metadata & metadata
+ = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
+ Transform & transform
+ = mgr.get_components_by_id<Transform>(button.game_object_id).front();
bool was_hovering = button.hover;
if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
button.hover = true;
- if (!button.on_mouse_enter) continue;
if (!was_hovering) {
- button.on_mouse_enter();
+ event_mgr.trigger_event<ButtonEnterEvent>(metadata);
}
} else {
button.hover = false;
- // Trigger the on_exit callback if the hover state just changed to false
- if (!button.on_mouse_exit) continue;
if (was_hovering) {
- button.on_mouse_exit();
+ event_mgr.trigger_event<ButtonExitEvent>(metadata);
}
}
}
@@ -184,19 +179,18 @@ void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_p
void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) {
ComponentManager & mgr = this->mediator.component_manager;
-
+ EventManager & event_mgr = this->mediator.event_manager;
RefVector<Button> buttons = mgr.get_components_by_type<Button>();
for (Button & button : buttons) {
if (!button.active) continue;
- if (!button.on_click) continue;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(button.game_object_id);
- Transform & transform = transform_vec.front().get();
+ Metadata & metadata
+ = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
+ Transform & transform
+ = mgr.get_components_by_id<Transform>(button.game_object_id).front();
if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
-
- button.on_click();
+ event_mgr.trigger_event<ButtonPressEvent>(metadata);
}
}
}
diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h
index eefd9fe..e580d8e 100644
--- a/src/crepe/system/InputSystem.h
+++ b/src/crepe/system/InputSystem.h
@@ -3,6 +3,8 @@
#include "../api/Config.h"
#include "../facade/EventData.h"
+#include "../api/Event.h"
+#include "../api/Metadata.h"
#include "../types.h"
#include "../util/OptionalRef.h"
@@ -13,6 +15,37 @@ namespace crepe {
class Camera;
class Button;
class Transform;
+//! Event triggered when a button is clicked
+class ButtonPressEvent : public Event {
+public:
+ //! Metadata of the button.
+ const Metadata & metadata;
+ /**
+ * \param metadata Metadata of the button pressed
+ */
+ ButtonPressEvent(const Metadata & metadata) : metadata(metadata){};
+};
+//! Event triggered when the mouse enters a button
+class ButtonEnterEvent : public Event {
+public:
+ //! Metadata of the button.
+ const Metadata & metadata;
+ /**
+ * \param metadata Metadata of the button pressed
+ */
+ ButtonEnterEvent(const Metadata & metadata) : metadata(metadata){};
+};
+//! Event triggered when the mouse leaves a button
+class ButtonExitEvent : public Event {
+public:
+ //! Metadata of the button.
+ const Metadata & metadata;
+ /**
+ * \param metadata Metadata of the button pressed
+ */
+ ButtonExitEvent(const Metadata & metadata) : metadata(metadata){};
+};
+
/**
* \brief Handles the processing of input events created by SDLContext
*
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index b4b745f..3ec1922 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -118,12 +118,13 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Transform & tm){
for (const Particle & p : em.particles) {
if (!p.active) continue;
+ if (p.time_in_life < em.data.begin_lifespan) continue;
ctx.draw(SDLContext::RenderContext{
.sprite = sprite,
.texture = res,
.pos = p.position,
- .angle = p.angle,
+ .angle = p.angle + transform_angle,
.scale = tm.scale,
});
}
diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp
index d6b2ca1..0605c7a 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -11,6 +11,7 @@ void ScriptSystem::update() {
dbg_trace();
ComponentManager & mgr = this->mediator.component_manager;
+ LoopTimerManager & timer = this->mediator.loop_timer;
RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>();
for (BehaviorScript & behavior_script : behavior_scripts) {
@@ -23,6 +24,8 @@ void ScriptSystem::update() {
script->init();
script->initialized = true;
}
- script->update();
+
+ duration_t delta_time = timer.get_scaled_fixed_delta_time();
+ script->update(delta_time);
}
}
diff --git a/src/doc/feature/proxy.dox b/src/doc/feature/proxy.dox
new file mode 100644
index 0000000..66bbd2f
--- /dev/null
+++ b/src/doc/feature/proxy.dox
@@ -0,0 +1,43 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_proxy Proxy utility
+\ingroup feature
+\brief Use ValueBroker as if it were a regular variable
+
+Proxy provides operators that allow you to use a ValueBroker instance as if it
+were a regular variable. Proxy implements a constructor that allows it to be
+used as a substitute return type for any function that returns ValueBroker.
+
+\see ValueBroker
+\see Proxy
+
+\par Example
+
+```cpp
+#include <crepe/util/Proxy.h>
+#include <crepe/ValueBroker.h>
+
+int calculation(int value) {
+ return 3 * value;
+}
+
+void anywhere() {
+ crepe::ValueBroker<int> foo_handle;
+ crepe::Proxy foo = foo_handle;
+
+ // implicitly calls .set()
+ foo += 10;
+
+ // implicitly calls .get()
+ int out = calculation(foo);
+
+ // explicitly cast (also calls .get())
+ int casted = int(foo);
+}
+
+```
+
+*/
+}
diff --git a/src/doc/feature/savemgr.dox b/src/doc/feature/savemgr.dox
new file mode 100644
index 0000000..6aeab03
--- /dev/null
+++ b/src/doc/feature/savemgr.dox
@@ -0,0 +1,80 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_savemgr Save data
+\ingroup feature
+\brief Functions to persistently store and retrieve arbitrary values
+
+The SaveManager may be used to persistently store game state such as player
+progress, statistics, achievements, etc. It works like a key-value store, where
+the key is a string and the value is an arbitrary type.
+
+SaveManager implements the following:
+
+- Storage and retrieval of primitive types and strings.
+- Automatic initialization of the database using default values.
+- The underlying database format is journaled, which reduces the likelihood of
+ players losing save data when an unexpected crash occurs while the SaveManager
+ is writing to disk.
+
+\see SaveManager
+
+\par Example
+
+The SaveManager instance reference may be retrieved by calling \c
+get_save_manager(). This function is available---
+
+- Within (derivatives of) Script
+
+- \todo Within (derivatives of) Scene
+
+- \todo As a public member function of LoopManager
+
+```cpp
+// Retrieve save manager
+crepe::SaveManager & save_manager = get_save_manager();
+```
+
+SaveManager may be used *explicitly*, using the \ref SaveManager::set "set()",
+\ref SaveManager::get "get()" and \ref SaveManager::has "has()" methods:
+```cpp
+// Check if the key "foo" exists, and initialize it to 3 if it doesn't
+if (!save_manager.has("foo"))
+ save_manager.set<int>("foo", 3);
+// Get value of key "foo"
+int foo = save_manager.get<int>("foo");
+
+// ~~~ arbitrary game code ~~~
+foo += 10;
+// ~~~ arbitrary game code ~~~
+
+// Save "foo" back to database
+save_manager.set<int>("foo", foo);
+```
+
+Alternatively, SaveManager::get may be called with a default value as second
+parameter. This changes its return type to ValueBroker, which acts as a
+read/write handle to the specific key requested, and remembers the key and its
+value type for you:
+```cpp
+// Get a read/write handle to the value stored in key "foo", and initialize it
+// to 3 if it doesn't exist yet
+ValueBroker foo_handle = save_manager.get<int>("foo", 3);
+int foo = foo_handle.get();
+
+// ~~~ arbitrary game code ~~~
+foo += 10;
+// ~~~ arbitrary game code ~~~
+
+// Save back to database
+foo_handle.set(foo);
+```
+
+To further simplify game code, the return value of SaveManager::get may be
+implicitly cast to Proxy instead of ValueBroker. This allows the database value
+to be used as if it were a regular variable. This usage is detailed separately
+in \"\ref feature_proxy\".
+
+*/
+}
diff --git a/src/example/game.cpp b/src/example/game.cpp
index 22effd2..3975650 100644
--- a/src/example/game.cpp
+++ b/src/example/game.cpp
@@ -86,7 +86,7 @@ class MyScript1 : public Script {
subscribe<KeyPressEvent>(
[this](const KeyPressEvent & ev) -> bool { return this->keypressed(ev); });
}
- void update() {
+ void update(duration_t) {
Rigidbody & tf = this->get_component<Rigidbody>();
Log::logf("linear_velocity.x {}", tf.data.linear_velocity.x);
Log::logf("linear_velocity.y {}", tf.data.linear_velocity.y);
diff --git a/src/example/rendering_particle.cpp b/src/example/rendering_particle.cpp
index 9cf25d7..07cfae2 100644
--- a/src/example/rendering_particle.cpp
+++ b/src/example/rendering_particle.cpp
@@ -39,6 +39,8 @@ public:
.angle_offset = 0,
.position_offset = {0, 1},
});
+ auto & emitter
+ = game_object.add_component<ParticleEmitter>(test_sprite, ParticleEmitter::Data{});
Sprite & test_sprite1
= game_object.add_component<Sprite>(img, Sprite::Data{
diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp
index ff9e7cc..baa95c1 100644
--- a/src/test/CollisionTest.cpp
+++ b/src/test/CollisionTest.cpp
@@ -54,6 +54,7 @@ public:
ComponentManager mgr{m};
CollisionSystem collision_sys{m};
ScriptSystem script_sys{m};
+ LoopTimerManager loop_timer{m};
GameObject world = mgr.new_object("world", "", {50, 50});
GameObject game_object1 = mgr.new_object("object1", "", {50, 50});
diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp
index 99dedff..7f2c339 100644
--- a/src/test/DBTest.cpp
+++ b/src/test/DBTest.cpp
@@ -27,3 +27,11 @@ TEST_F(DBTest, Has) {
db.set("foo", "bar");
EXPECT_EQ(db.has("foo"), true);
}
+
+TEST_F(DBTest, MultipleKeys) {
+ db.set("foo", "foo");
+ db.set("bar", "bar");
+
+ EXPECT_EQ(db.get("foo"), "foo");
+ EXPECT_EQ(db.get("bar"), "bar");
+}
diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp
index 2d844d4..ce8ea44 100644
--- a/src/test/InputTest.cpp
+++ b/src/test/InputTest.cpp
@@ -2,7 +2,6 @@
#include <crepe/manager/ResourceManager.h>
#include <crepe/system/RenderSystem.h>
-
#define protected public
#define private public
@@ -214,8 +213,12 @@ TEST_F(InputTest, MouseClick) {
TEST_F(InputTest, testButtonClick) {
GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1);
bool button_clicked = false;
- std::function<void()> on_click = [&]() { button_clicked = true; };
- auto & button = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click);
+ event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) {
+ button_clicked = true;
+ EXPECT_EQ(event.metadata.game_object_id, button_obj.id);
+ return false;
+ });
+ auto & button = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0});
bool hover = false;
button.active = true;
@@ -232,25 +235,19 @@ TEST_F(InputTest, testButtonClick) {
TEST_F(InputTest, testButtonHover) {
GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1);
- bool button_clicked = false;
- std::function<void()> on_click = [&]() { button_clicked = true; };
- auto & button = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click);
+ bool button_hover = false;
+ event_manager.subscribe<ButtonEnterEvent>([&](const ButtonEnterEvent & event) {
+ button_hover = true;
+ EXPECT_EQ(event.metadata.game_object_id, button_obj.id);
+ return false;
+ });
+ event_manager.subscribe<ButtonExitEvent>([&](const ButtonExitEvent & event) {
+ button_hover = false;
+ EXPECT_EQ(event.metadata.game_object_id, button_obj.id);
+ return false;
+ });
+ auto & button = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0});
button.active = true;
-
- // Mouse not on button
- SDL_Event event;
- SDL_zero(event);
- event.type = SDL_MOUSEMOTION;
- event.motion.x = 700;
- event.motion.y = 700;
- event.motion.xrel = 10;
- event.motion.yrel = 10;
- SDL_PushEvent(&event);
-
- input_system.update();
- event_manager.dispatch_events();
- EXPECT_FALSE(button.hover);
-
// Mouse on button
SDL_Event hover_event;
SDL_zero(hover_event);
@@ -264,4 +261,19 @@ TEST_F(InputTest, testButtonHover) {
input_system.update();
event_manager.dispatch_events();
EXPECT_TRUE(button.hover);
+ EXPECT_TRUE(button_hover);
+ // Mouse not on button
+ SDL_Event event;
+ SDL_zero(event);
+ event.type = SDL_MOUSEMOTION;
+ event.motion.x = 500;
+ event.motion.y = 500;
+ event.motion.xrel = 10;
+ event.motion.yrel = 10;
+ SDL_PushEvent(&event);
+
+ input_system.update();
+ event_manager.dispatch_events();
+ EXPECT_FALSE(button.hover);
+ EXPECT_FALSE(button_hover);
}
diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp
index e9b0c29..7609e69 100644
--- a/src/test/SaveManagerTest.cpp
+++ b/src/test/SaveManagerTest.cpp
@@ -27,8 +27,8 @@ TEST_F(SaveManagerTest, ReadWrite) {
mgr.set<string>("foo", "bar");
ASSERT_TRUE(mgr.has("foo"));
- ValueBroker value = mgr.get<string>("foo");
- EXPECT_EQ(value.get(), "bar");
+ string value = mgr.get<string>("foo");
+ EXPECT_EQ(value, "bar");
}
TEST_F(SaveManagerTest, DefaultValue) {
@@ -36,5 +36,16 @@ TEST_F(SaveManagerTest, DefaultValue) {
ASSERT_EQ(value.get(), 3);
value.set(5);
- ASSERT_EQ(value.get(), 5);
+ EXPECT_EQ(value.get(), 5);
+}
+
+TEST_F(SaveManagerTest, MultipleKeys) {
+ ValueBroker foo = mgr.get<int>("foo", 1);
+ ValueBroker bar = mgr.get<int>("bar", 2);
+
+ EXPECT_EQ(foo.get(), 1);
+ EXPECT_EQ(bar.get(), 2);
+
+ EXPECT_EQ(mgr.get<int>("foo"), 1);
+ EXPECT_EQ(mgr.get<int>("bar"), 2);
}
diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp
index acdae70..846e398 100644
--- a/src/test/ScriptTest.cpp
+++ b/src/test/ScriptTest.cpp
@@ -28,7 +28,7 @@ void ScriptTest::SetUp() {
TEST_F(ScriptTest, Default) {
MyScript & script = this->script;
EXPECT_CALL(script, init()).Times(0);
- EXPECT_CALL(script, update()).Times(0);
+ EXPECT_CALL(script, update(_)).Times(0);
}
TEST_F(ScriptTest, UpdateOnce) {
@@ -38,7 +38,7 @@ TEST_F(ScriptTest, UpdateOnce) {
InSequence seq;
EXPECT_CALL(script, init()).Times(1);
- EXPECT_CALL(script, update()).Times(1);
+ EXPECT_CALL(script, update(_)).Times(1);
system.update();
}
@@ -46,7 +46,7 @@ TEST_F(ScriptTest, UpdateOnce) {
InSequence seq;
EXPECT_CALL(script, init()).Times(0);
- EXPECT_CALL(script, update()).Times(1);
+ EXPECT_CALL(script, update(_)).Times(1);
system.update();
}
}
@@ -59,7 +59,7 @@ TEST_F(ScriptTest, UpdateInactive) {
InSequence seq;
EXPECT_CALL(script, init()).Times(0);
- EXPECT_CALL(script, update()).Times(0);
+ EXPECT_CALL(script, update(_)).Times(0);
behaviorscript.active = false;
system.update();
}
@@ -68,8 +68,20 @@ TEST_F(ScriptTest, UpdateInactive) {
InSequence seq;
EXPECT_CALL(script, init()).Times(1);
- EXPECT_CALL(script, update()).Times(1);
+ EXPECT_CALL(script, update(_)).Times(1);
behaviorscript.active = true;
system.update();
}
}
+
+TEST_F(ScriptTest, SaveManager) {
+ MyScript & script = this->script;
+
+ EXPECT_EQ(&script.get_save_manager(), &this->save_manager);
+}
+
+TEST_F(ScriptTest, LoopTimerManager) {
+ MyScript & script = this->script;
+
+ EXPECT_EQ(&script.get_loop_timer(), &this->loop_timer);
+}
diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h
index 31fa7c9..f3dbda4 100644
--- a/src/test/ScriptTest.h
+++ b/src/test/ScriptTest.h
@@ -7,7 +7,10 @@
#include <crepe/api/Script.h>
#include <crepe/manager/ComponentManager.h>
#include <crepe/manager/EventManager.h>
+#include <crepe/manager/LoopTimerManager.h>
+#include <crepe/manager/SaveManager.h>
#include <crepe/system/ScriptSystem.h>
+
class ScriptTest : public testing::Test {
protected:
crepe::Mediator mediator;
@@ -17,6 +20,8 @@ public:
crepe::ComponentManager component_manager{mediator};
crepe::ScriptSystem system{mediator};
crepe::EventManager event_mgr{mediator};
+ crepe::LoopTimerManager loop_timer{mediator};
+ crepe::SaveManager save_manager{mediator};
crepe::GameObject entity = component_manager.new_object(OBJ_NAME);
class MyScript : public crepe::Script {
@@ -24,7 +29,7 @@ public:
public:
MOCK_METHOD(void, init, (), (override));
- MOCK_METHOD(void, update, (), (override));
+ MOCK_METHOD(void, update, (crepe::duration_t), (override));
};
crepe::OptionalRef<crepe::BehaviorScript> behaviorscript;
diff --git a/src/test/main.cpp b/src/test/main.cpp
index ed2aed5..0e1bc75 100644
--- a/src/test/main.cpp
+++ b/src/test/main.cpp
@@ -1,6 +1,7 @@
-#include <crepe/api/Config.h>
#include <gtest/gtest.h>
+#include <crepe/api/Config.h>
+
using namespace crepe;
using namespace testing;