aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/api
diff options
context:
space:
mode:
authorheavydemon21 <nielsstunnebrink1@gmail.com>2024-12-11 14:21:34 +0100
committerheavydemon21 <nielsstunnebrink1@gmail.com>2024-12-11 14:21:34 +0100
commite6a5bb579d0969245f34119ef2489f47039c523e (patch)
treee8b0b922cd28d6f83d7bf59ea1b69bbb84d0fd04 /src/crepe/api
parent7b8de90699aea153e73b5f2cee05c69b966b81be (diff)
parent78c4a8772526f40c531b5402b56932b0a41e22e8 (diff)
Merge branch 'master' into niels/remove_singleton
Diffstat (limited to 'src/crepe/api')
-rw-r--r--src/crepe/api/AI.cpp89
-rw-r--r--src/crepe/api/AI.h128
-rw-r--r--src/crepe/api/CMakeLists.txt2
-rw-r--r--src/crepe/api/LoopManager.cpp6
-rw-r--r--src/crepe/api/LoopManager.h6
-rw-r--r--src/crepe/api/Script.cpp8
-rw-r--r--src/crepe/api/Script.h22
-rw-r--r--src/crepe/api/Script.hpp30
-rw-r--r--src/crepe/api/Vector2.h24
-rw-r--r--src/crepe/api/Vector2.hpp48
10 files changed, 353 insertions, 10 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
new file mode 100644
index 0000000..2195249
--- /dev/null
+++ b/src/crepe/api/AI.cpp
@@ -0,0 +1,89 @@
+#include <stdexcept>
+#include <type_traits>
+
+#include "AI.h"
+#include "types.h"
+
+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) {
+ if (radius <= 0) {
+ throw std::runtime_error("Radius must be greater than 0");
+ }
+
+ // The step size is determined by the radius (step size is in radians)
+ float step = RADIUS_TO_STEP / radius;
+ // Force at least MIN_STEP steps (in case of a small radius)
+ if (step > 2 * M_PI / MIN_STEP) {
+ step = 2 * M_PI / MIN_STEP;
+ }
+ // The path node distance is determined by the step size and the radius
+ this->path_node_distance = radius * step * PATH_NODE_DISTANCE_FACTOR;
+
+ 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))});
+ }
+ } 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))});
+ }
+ }
+}
+
+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");
+ }
+
+ float max_radius = std::max(radius_x, radius_y);
+ // The step size is determined by the radius (step size is in radians)
+ float step = RADIUS_TO_STEP / max_radius;
+ // Force at least MIN_STEP steps (in case of a small radius)
+ if (step > 2 * M_PI / MIN_STEP) {
+ step = 2 * M_PI / MIN_STEP;
+ }
+ // The path node distance is determined by the step size and the radius
+ this->path_node_distance = max_radius * step * PATH_NODE_DISTANCE_FACTOR;
+
+ std::function<vec2(vec2, vec2)> rotate_point = [rotation](vec2 point, vec2 center) {
+ float s = sin(rotation);
+ float c = cos(rotation);
+
+ // Translate point back to origin
+ point.x -= center.x;
+ point.y -= center.y;
+
+ // Rotate point
+ float xnew = point.x * c - point.y * s;
+ float ynew = point.x * s + point.y * c;
+
+ // Translate point back
+ point.x = xnew + center.x;
+ point.y = ynew + center.y;
+
+ return point;
+ };
+
+ 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))};
+ 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))};
+ path.push_back(rotate_point(point, center));
+ }
+ }
+}
+
+} // namespace crepe
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
new file mode 100644
index 0000000..c780a91
--- /dev/null
+++ b/src/crepe/api/AI.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "Component.h"
+#include "types.h"
+
+namespace crepe {
+
+/**
+ * \brief The AI component is used to control the movement of an entity using AI.
+ *
+ * The AI component can be used to control the movement of an entity. The AI component can be used
+ * to implement different behaviors such as seeking, fleeing, arriving, and path following.
+ */
+class AI : public Component {
+public:
+ //! The different types of behaviors that can be used
+ enum BehaviorTypeMask {
+ SEEK = 0x00002,
+ FLEE = 0x00004,
+ ARRIVE = 0x00008,
+ PATH_FOLLOW = 0x00010,
+ };
+
+public:
+ /**
+ * \param id The id of the game object
+ * \param max_force The maximum force that can be applied to the entity
+ */
+ AI(game_object_id_t id, float max_force);
+
+ /**
+ * \brief Check if a behavior is on (aka activated)
+ *
+ * \param behavior The behavior to check
+ * \return true if the behavior is on, false otherwise
+ */
+ bool on(BehaviorTypeMask behavior) const { return (flags & behavior); }
+ //! Turn on the seek behavior
+ void seek_on() { flags |= SEEK; }
+ //! Turn off the seek behavior
+ void seek_off() { flags &= ~SEEK; }
+ //! Turn on the flee behavior
+ void flee_on() { flags |= FLEE; }
+ //! Turn off the flee behavior
+ void flee_off() { flags &= ~FLEE; }
+ //! Turn on the arrive behavior
+ void arrive_on() { flags |= ARRIVE; }
+ //! Turn off the arrive behavior
+ void arrive_off() { flags &= ~ARRIVE; }
+ //! Turn on the path follow behavior
+ void path_follow_on() { flags |= PATH_FOLLOW; }
+ //! Turn off the path follow behavior
+ void path_follow_off() { flags &= ~PATH_FOLLOW; }
+
+ /**
+ * \brief Add a path node (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param node The path node to add
+ */
+ void add_path_node(const vec2 & node) { path.push_back(node); }
+ /**
+ * \brief Make a circle path (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param radius The radius of the circle (in game units)
+ * \param center The center of the circle (in game units)
+ * \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);
+ /**
+ * \brief Make an oval path (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param radius_x The x radius of the oval (in game units)
+ * \param radius_y The y radius of the oval (in game units)
+ * \param center The center of the oval (in game units)
+ * \param start_angle The start angle of the oval (in radians)
+ * \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);
+
+public:
+ //! The maximum force that can be applied to the entity (higher values will make the entity adjust faster)
+ float max_force;
+
+ //! The target to seek at
+ vec2 seek_target;
+ //! The target to arrive at
+ vec2 arrive_target;
+ //! The target to flee from
+ vec2 flee_target;
+ //! The distance at which the entity will start to flee from the target
+ float square_flee_panic_distance = 200.0f * 200.0f;
+ //! The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot))
+ float arrive_deceleration = 40.0f;
+ //! The path to follow (for the path following behavior)
+ std::vector<vec2> path;
+ //! The distance from the path node at which the entity will move to the next node (automatically set by make_circle_path())
+ float path_node_distance = 400.0f;
+ //! Looping behavior for the path
+ bool path_loop = true;
+
+private:
+ //! The flags for the behaviors
+ int flags = 0;
+ //! The current path index
+ size_t path_index = 0;
+
+ //! The AISystem is the only class that should access the flags and path_index variables
+ friend class AISystem;
+
+ //! The minimum amount of steps for the path following behavior
+ static constexpr int MIN_STEP = 16;
+ //! The radius to step size ratio for the path following behavior
+ static constexpr float RADIUS_TO_STEP = 400.0f;
+ //! The path node distance factor for the path following behavior
+ static constexpr float PATH_NODE_DISTANCE_FACTOR = 0.75f;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 718e497..46deb67 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -22,6 +22,7 @@ target_sources(crepe PUBLIC
Script.cpp
Button.cpp
UIObject.cpp
+ AI.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -53,4 +54,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Asset.h
Button.h
UIObject.h
+ AI.h
)
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index 88243c4..1f0ba72 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -1,4 +1,6 @@
+#include "../system/AISystem.h"
#include "../system/AnimatorSystem.h"
+#include "../system/AudioSystem.h"
#include "../system/CollisionSystem.h"
#include "../system/InputSystem.h"
#include "../system/ParticleSystem.h"
@@ -20,6 +22,8 @@ LoopManager::LoopManager() {
this->load_system<RenderSystem>();
this->load_system<ScriptSystem>();
this->load_system<InputSystem>();
+ this->load_system<AudioSystem>();
+ this->load_system<AISystem>();
}
void LoopManager::process_input() { this->get_system<InputSystem>().update(); }
@@ -35,8 +39,10 @@ void LoopManager::fixed_update() {
EventManager & ev = this->mediator.event_manager;
ev.dispatch_events();
this->get_system<ScriptSystem>().update();
+ this->get_system<AISystem>().update();
this->get_system<PhysicsSystem>().update();
this->get_system<CollisionSystem>().update();
+ this->get_system<AudioSystem>().update();
}
void LoopManager::loop() {
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index b800f5b..7389124 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -4,6 +4,8 @@
#include "../facade/SDLContext.h"
#include "../manager/ComponentManager.h"
+#include "../manager/ResourceManager.h"
+#include "../manager/SaveManager.h"
#include "../manager/SceneManager.h"
#include "../system/System.h"
@@ -96,6 +98,10 @@ private:
ComponentManager component_manager{mediator};
//! Scene manager instance
SceneManager scene_manager{mediator};
+ //! Resource manager instance
+ ResourceManager resource_manager{mediator};
+ //! Save manager instance
+ SaveManager save_manager{mediator};
SDLContext sdl_context {mediator};
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index 4091fd4..753a9e3 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -8,8 +8,7 @@ using namespace crepe;
using namespace std;
Script::~Script() {
- Mediator & mediator = this->mediator;
- EventManager & mgr = mediator.event_manager;
+ EventManager & mgr = this->mediator->event_manager;
for (auto id : this->listeners) {
mgr.unsubscribe(id);
}
@@ -21,7 +20,8 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
}
void Script::set_next_scene(const string & name) {
- Mediator & mediator = this->mediator;
- SceneManager & mgr = mediator.scene_manager;
+ SceneManager & mgr = this->mediator->scene_manager;
mgr.set_next_scene(name);
}
+
+SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; }
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index d99ab0e..668e5d1 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -86,6 +86,25 @@ protected:
RefVector<T> get_components() const;
/**
+ * \copydoc ComponentManager::get_components_by_id
+ * \see 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
+ */
+ 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
+ */
+ 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
@@ -113,6 +132,9 @@ protected:
*/
void set_next_scene(const std::string & name);
+ //! Retrieve SaveManager reference
+ SaveManager & get_save_manager() const;
+
//! \}
private:
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index 45f1ff1..225a51c 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -20,10 +20,7 @@ T & Script::get_component() const {
template <typename T>
RefVector<T> Script::get_components() const {
- Mediator & mediator = this->mediator;
- ComponentManager & mgr = mediator.component_manager;
-
- return mgr.get_components_by_id<T>(this->game_object_id);
+ return this->get_components_by_id<T>(this->game_object_id);
}
template <typename... Args>
@@ -34,8 +31,7 @@ void Script::logf(Args &&... args) {
template <typename EventType>
void Script::subscribe_internal(const EventHandler<EventType> & callback,
event_channel_t channel) {
- Mediator & mediator = this->mediator;
- EventManager & mgr = mediator.event_manager;
+ EventManager & mgr = this->mediator->event_manager;
subscription_t listener = mgr.subscribe<EventType>(
[this, callback](const EventType & data) -> bool {
bool & active = this->active;
@@ -56,4 +52,26 @@ void Script::subscribe(const EventHandler<EventType> & callback) {
this->subscribe_internal(callback, EventManager::CHANNEL_ALL);
}
+template <typename T>
+RefVector<T> Script::get_components_by_id(game_object_id_t id) const {
+ Mediator & mediator = this->mediator;
+ ComponentManager & mgr = mediator.component_manager;
+
+ return mgr.get_components_by_id<T>(id);
+}
+template <typename T>
+RefVector<T> Script::get_components_by_name(const std::string & name) const {
+ Mediator & mediator = this->mediator;
+ ComponentManager & mgr = mediator.component_manager;
+
+ return mgr.get_components_by_name<T>(name);
+}
+template <typename T>
+RefVector<T> Script::get_components_by_tag(const std::string & tag) const {
+ Mediator & mediator = this->mediator;
+ ComponentManager & mgr = mediator.component_manager;
+
+ return mgr.get_components_by_tag<T>(tag);
+}
+
} // namespace crepe
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index c278c87..bf9d124 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -66,6 +66,30 @@ struct Vector2 {
//! Checks if this vector is not equal to another vector.
bool operator!=(const Vector2<T> & other) const;
+
+ //! Truncates the vector to a maximum length.
+ void truncate(T max);
+
+ //! Normalizes the vector (resulting in vector with a length of 1).
+ void normalize();
+
+ //! Returns the length of the vector.
+ T length() const;
+
+ //! Returns the squared length of the vector.
+ T length_squared() const;
+
+ //! Returns the dot product (inwendig product) of this vector and another vector.
+ T dot(const Vector2<T> & other) const;
+
+ //! Returns the distance between this vector and another vector.
+ T distance(const Vector2<T> & other) const;
+
+ //! Returns the squared distance between this vector and another vector.
+ T distance_squared(const Vector2<T> & other) const;
+
+ //! Returns the perpendicular vector to this vector.
+ Vector2 perpendicular() const;
};
} // namespace crepe
diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp
index cad15f8..ff53cb0 100644
--- a/src/crepe/api/Vector2.hpp
+++ b/src/crepe/api/Vector2.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <cmath>
+
#include "Vector2.h"
namespace crepe {
@@ -115,4 +117,50 @@ bool Vector2<T>::operator!=(const Vector2<T> & other) const {
return !(*this == other);
}
+template <class T>
+void Vector2<T>::truncate(T max) {
+ if (length() > max) {
+ normalize();
+ *this *= max;
+ }
+}
+
+template <class T>
+void Vector2<T>::normalize() {
+ T len = length();
+ if (len > 0) {
+ *this /= len;
+ }
+}
+
+template <class T>
+T Vector2<T>::length() const {
+ return std::sqrt(x * x + y * y);
+}
+
+template <class T>
+T Vector2<T>::length_squared() const {
+ return x * x + y * y;
+}
+
+template <class T>
+T Vector2<T>::dot(const Vector2<T> & other) const {
+ return x * other.x + y * other.y;
+}
+
+template <class T>
+T Vector2<T>::distance(const Vector2<T> & other) const {
+ return (*this - other).length();
+}
+
+template <class T>
+T Vector2<T>::distance_squared(const Vector2<T> & other) const {
+ return (*this - other).length_squared();
+}
+
+template <class T>
+Vector2<T> Vector2<T>::perpendicular() const {
+ return {-y, x};
+}
+
} // namespace crepe