From 16444f19ae2c7c71a2be53ce6fd2e4d671aa8765 Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Wed, 4 Dec 2024 12:15:05 +0100
Subject: Modified test and setup of AISystem

---
 src/crepe/system/AISystem.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 src/crepe/system/AISystem.h

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
new file mode 100644
index 0000000..4138e01
--- /dev/null
+++ b/src/crepe/system/AISystem.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "System.h"
+
+namespace crepe {
+
+class AISystem : public System {
+public:
+	using System::System;
+
+	void update() override;
+};
+
+} // namespace crepe
-- 
cgit v1.2.3


From f9f5600b60d6944dc9a7dd502988703d59d0cd62 Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Wed, 4 Dec 2024 13:46:41 +0100
Subject: Setup some behaviors

---
 src/crepe/api/AI.cpp          |  2 +-
 src/crepe/api/AI.h            | 47 +++++++++++++++++++++++++++++++++++++++----
 src/crepe/system/AISystem.cpp | 32 +++++++++++++++++++++++++++--
 src/crepe/system/AISystem.h   |  6 ++++++
 4 files changed, 80 insertions(+), 7 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
index 6b63216..7f820a8 100644
--- a/src/crepe/api/AI.cpp
+++ b/src/crepe/api/AI.cpp
@@ -2,7 +2,7 @@
 
 namespace crepe {
 
-AI::AI(game_object_id_t id, double mass, double max_speed, double max_force)
+AI::AI(game_object_id_t id, float mass, float max_speed, float max_force)
 	: Component(id),
 	  mass(mass),
 	  max_speed(max_speed),
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index b755439..faeeba5 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -7,12 +7,51 @@ namespace crepe {
 
 class AI : public Component {
 public:
-	AI(game_object_id_t id, double mass, double max_speed, double max_force);
+	enum BehaviorType {
+		NONE = 0x00000,
+		SEEK = 0x00002,
+		FLEE = 0x00004,
+		ARRIVE = 0x00008,
+		PATH_FOLLOW = 0x00010,
+	};
 
 public:
-	double mass;
-	double max_speed;
-	double max_force;
+	AI(game_object_id_t id, float mass, float max_speed, float max_force);
+
+	bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; }
+	void seek_on() { flags |= SEEK; }
+	void seek_off() {
+		if (on(SEEK)) flags ^= SEEK;
+	}
+	void flee_on() { flags |= FLEE; }
+	void flee_off() {
+		if (on(FLEE)) flags ^= FLEE;
+	}
+	void arrive_on() { flags |= ARRIVE; }
+	void arrive_off() {
+		if (on(ARRIVE)) flags ^= ARRIVE;
+	}
+	void path_follow_on() { flags |= PATH_FOLLOW; }
+	void path_follow_off() {
+		if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW;
+	}
+
+public:
+	float mass;
+	float max_speed;
+	float max_force;
+
+	// The target to seek or arrive at
+	vec2 seek_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 = 100.0f * 100.0f;
+	// The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot))
+	float arrive_deceleration = 2.0f;
+
+private:
+	int flags = 0;
 };
 
 } // namespace crepe
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 012f4fa..12da3d9 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -1,6 +1,34 @@
+#include "../ComponentManager.h"
+
 #include "AISystem.h"
-#include <iostream>
 
 using namespace crepe;
 
-void AISystem::update() { std::cout << "AI System update" << std::endl; }
+void AISystem::update() {
+	ComponentManager & mgr = this->component_manager;
+	RefVector<AI> ai_components = mgr.get_components_by_type<AI>();
+
+	for (AI & ai : ai_components) {
+		vec2 force = this->calculate(ai);
+		//...
+	}
+}
+
+vec2 AISystem::calculate(AI & ai) {
+	vec2 force;
+
+	if (ai.on(AI::BehaviorType::SEEK)) {
+		// Seek the target
+	}
+	if (ai.on(AI::BehaviorType::FLEE)) {
+		// Flee from the target
+	}
+	if (ai.on(AI::BehaviorType::ARRIVE)) {
+		// Arrive at the target
+	}
+	if (ai.on(AI::BehaviorType::PATH_FOLLOW)) {
+		// Follow the path
+	}
+
+	return force;
+}
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 4138e01..eb8d08c 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -1,6 +1,9 @@
 #pragma once
 
+#include "api/AI.h"
+
 #include "System.h"
+#include "types.h"
 
 namespace crepe {
 
@@ -9,6 +12,9 @@ public:
 	using System::System;
 
 	void update() override;
+
+private:
+	vec2 calculate(AI & ai);
 };
 
 } // namespace crepe
-- 
cgit v1.2.3


From 121387ba92a23d6f17b36331d25757abc899f7d2 Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Wed, 4 Dec 2024 17:27:51 +0100
Subject: Implemeted seek behavior

---
 src/crepe/api/AI.h            |  3 +++
 src/crepe/system/AISystem.cpp | 45 +++++++++++++++++++++++++++++++++++++++++--
 src/crepe/system/AISystem.h   |  3 +++
 src/example/AITest.cpp        |  4 ++--
 4 files changed, 51 insertions(+), 4 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index faeeba5..242ff89 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -51,6 +51,9 @@ public:
 	float arrive_deceleration = 2.0f;
 
 private:
+	vec2 velocity;
+	friend class AISystem;
+
 	int flags = 0;
 };
 
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 12da3d9..9029f32 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -1,4 +1,7 @@
 #include "../ComponentManager.h"
+#include "api/LoopTimer.h"
+#include "api/Transform.h"
+#include "types.h"
 
 #include "AISystem.h"
 
@@ -8,9 +11,19 @@ void AISystem::update() {
 	ComponentManager & mgr = this->component_manager;
 	RefVector<AI> ai_components = mgr.get_components_by_type<AI>();
 
+	double dt = LoopTimer::get_instance().get_delta_time();
+
 	for (AI & ai : ai_components) {
 		vec2 force = this->calculate(ai);
-		//...
+		vec2 acceleration = force / ai.mass;
+		ai.velocity += acceleration * dt;
+		ai.velocity.truncate(ai.max_speed);
+
+		// Update the position
+		RefVector<Transform> transforms
+			= mgr.get_components_by_id<Transform>(ai.game_object_id);
+		Transform & transform = transforms.front().get();
+		transform.position += ai.velocity * dt;
 	}
 }
 
@@ -18,7 +31,11 @@ vec2 AISystem::calculate(AI & ai) {
 	vec2 force;
 
 	if (ai.on(AI::BehaviorType::SEEK)) {
-		// Seek the target
+		vec2 force_to_add = this->seek(ai);
+
+		if (!this->accumulate_force(force, force_to_add)) {
+			return force;
+		}
 	}
 	if (ai.on(AI::BehaviorType::FLEE)) {
 		// Flee from the target
@@ -32,3 +49,27 @@ vec2 AISystem::calculate(AI & ai) {
 
 	return force;
 }
+
+bool AISystem::accumulate_force(vec2 & running_total, vec2 force_to_add) {
+	double magnitude_remaining = running_total.length();
+	double magnitude_to_add = force_to_add.length();
+
+	if (magnitude_remaining + magnitude_to_add > 0) {
+		running_total += force_to_add;
+		return true;
+	}
+
+	return false;
+}
+
+vec2 AISystem::seek(const AI & ai) {
+	ComponentManager & mgr = this->component_manager;
+	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
+	Transform & transform = transforms.front().get();
+
+	vec2 desired_velocity = ai.seek_target - transform.position;
+	desired_velocity.normalize();
+	desired_velocity *= ai.max_speed;
+
+	return desired_velocity - ai.velocity;
+}
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index eb8d08c..5e94ccb 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -15,6 +15,9 @@ public:
 
 private:
 	vec2 calculate(AI & ai);
+	bool accumulate_force(vec2 & running_total, vec2 force_to_add);
+
+	vec2 seek(const AI & ai);
 };
 
 } // namespace crepe
diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp
index 1c4633f..341e1de 100644
--- a/src/example/AITest.cpp
+++ b/src/example/AITest.cpp
@@ -18,13 +18,13 @@ public:
 	void load_scene() override {
 		ComponentManager & mgr = this->component_manager;
 
-		GameObject game_object1 = mgr.new_object("", "", vec2{0, 0}, 0, 1);
+		GameObject game_object1 = mgr.new_object("", "", vec2{250, 250}, 0, 1);
 		GameObject game_object2 = mgr.new_object("", "", vec2{0, 0}, 0, 1);
 
 		Texture img = Texture("asset/texture/test_ap43.png");
 		game_object1.add_component<Sprite>(img, Color::MAGENTA,
 										   Sprite::FlipSettings{false, false}, 1, 1, 195);
-		game_object1.add_component<AI>(1, 1, 1);
+		game_object1.add_component<AI>(1, 200, 200).seek_on();
 
 		game_object2.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{1036, 780},
 										   1.0f);
-- 
cgit v1.2.3


From 82863204048d65073bc5598dcb62f31e32b51430 Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Fri, 6 Dec 2024 10:46:16 +0100
Subject: Corrected accumulate_force()

---
 src/crepe/system/AISystem.cpp | 21 ++++++++++++++-------
 src/crepe/system/AISystem.h   |  5 ++++-
 2 files changed, 18 insertions(+), 8 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index d496e12..4858000 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -33,7 +33,7 @@ vec2 AISystem::calculate(AI & ai) {
 	if (ai.on(AI::BehaviorType::SEEK)) {
 		vec2 force_to_add = this->seek(ai);
 
-		if (!this->accumulate_force(force, force_to_add)) {
+		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
@@ -50,16 +50,23 @@ vec2 AISystem::calculate(AI & ai) {
 	return force;
 }
 
-bool AISystem::accumulate_force(vec2 & running_total, vec2 force_to_add) {
-	double magnitude_remaining = running_total.length();
-	double magnitude_to_add = force_to_add.length();
+bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add) {
+	float magnitude = running_total.length();
+	float magnitude_remaining = ai.max_force - magnitude;
 
-	if (magnitude_remaining + magnitude_to_add > 0) {
+	if (magnitude_remaining <= 0.0f) {
+		return false;
+	}
+
+	float magnitude_to_add = force_to_add.length();
+	if (magnitude_to_add < magnitude_remaining) {
 		running_total += force_to_add;
-		return true;
+	} else {
+		force_to_add.normalize();
+		running_total += force_to_add * magnitude_remaining;
 	}
 
-	return false;
+	return true;
 }
 
 vec2 AISystem::seek(const AI & ai) {
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 5e94ccb..18f1c61 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -15,9 +15,12 @@ public:
 
 private:
 	vec2 calculate(AI & ai);
-	bool accumulate_force(vec2 & running_total, vec2 force_to_add);
+	bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add);
 
 	vec2 seek(const AI & ai);
+	vec2 flee(const AI & ai);
+	vec2 arrive(const AI & ai);
+	vec2 path_follow(const AI & ai);
 };
 
 } // namespace crepe
-- 
cgit v1.2.3


From 33a072db28d71ba65e59f9491abd42dbf9695fc4 Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Fri, 6 Dec 2024 17:14:00 +0100
Subject: Implemented path_follow

---
 src/crepe/api/AI.h            | 14 ++++++++++++++
 src/crepe/system/AISystem.cpp | 34 ++++++++++++++++++++++++++++++++--
 src/crepe/system/AISystem.h   |  2 +-
 src/example/AITest.cpp        | 21 +++++++++++++--------
 4 files changed, 60 insertions(+), 11 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index d4bd9d3..35b8998 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -36,6 +36,8 @@ public:
 		if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW;
 	}
 
+	void add_path_node(vec2 node) { path.push_back(node); }
+
 public:
 	float max_force;
 
@@ -47,9 +49,21 @@ public:
 	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
+	std::vector<vec2> path;
+	// The distance from the path node at which the entity will move to the next node
+	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 can access the private members of AI
+	friend class AISystem;
 };
 
 } // namespace crepe
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index ce3147f..55dc14c 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -55,11 +55,11 @@ vec2 AISystem::calculate(AI & ai) {
 		}
 	}
 	if (ai.on(AI::BehaviorType::PATH_FOLLOW)) {
-		/*vec2 force_to_add = this->path_follow(ai);
+		vec2 force_to_add = this->path_follow(ai);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
-		}*/
+		}
 	}
 
 	return force;
@@ -138,3 +138,33 @@ vec2 AISystem::arrive(const AI & ai) {
 
 	return vec2{0, 0};
 }
+
+vec2 AISystem::path_follow(AI & ai) {
+	const Mediator & mediator = this->mediator;
+	ComponentManager & mgr = mediator.component_manager;
+	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
+	Transform & transform = transforms.front().get();
+	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
+	Rigidbody & rigidbody = rigidbodies.front().get();
+
+	if (ai.path.empty()) {
+		return vec2{0, 0};
+	}
+
+	vec2 to_target = ai.path.at(ai.path_index) - transform.position;
+	if (to_target.length_squared() > ai.path_node_distance * ai.path_node_distance) {
+		ai.seek_target = ai.path.at(ai.path_index);
+	} else {
+		ai.path_index++;
+		if (ai.path_index >= ai.path.size()) {
+			if (ai.path_loop) {
+				ai.path_index = 0;
+			} else {
+				ai.path_index = ai.path.size() - 1;
+				return this->arrive(ai);
+			}
+		}
+	}
+
+	return this->seek(ai);
+}
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 18f1c61..27861d9 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -20,7 +20,7 @@ private:
 	vec2 seek(const AI & ai);
 	vec2 flee(const AI & ai);
 	vec2 arrive(const AI & ai);
-	vec2 path_follow(const AI & ai);
+	vec2 path_follow(AI & ai);
 };
 
 } // namespace crepe
diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp
index 319d0fe..d12a99a 100644
--- a/src/example/AITest.cpp
+++ b/src/example/AITest.cpp
@@ -25,10 +25,10 @@ class Script1 : public Script {
 	}
 
 	bool mousemove(const MouseMoveEvent & event) {
-		RefVector<AI> aivec = this->get_components<AI>();
+		/*RefVector<AI> aivec = this->get_components<AI>();
 		AI & ai = aivec.front().get();
 		ai.flee_target
-			= vec2{static_cast<float>(event.mouse_x), static_cast<float>(event.mouse_y)};
+			= vec2{static_cast<float>(event.mouse_x), static_cast<float>(event.mouse_y)};*/
 		return true;
 	}
 
@@ -46,21 +46,26 @@ public:
 		Mediator & mediator = this->mediator;
 		ComponentManager & mgr = mediator.component_manager;
 
-		GameObject game_object1 = mgr.new_object("", "", vec2{250, 250}, 0, 1);
+		GameObject game_object1 = mgr.new_object("", "", vec2{0, 0}, 0, 1);
 		GameObject game_object2 = mgr.new_object("", "", vec2{0, 0}, 0, 1);
 
 		Texture img = Texture("asset/texture/test_ap43.png");
 		game_object1.add_component<Sprite>(img, Color::MAGENTA,
 										   Sprite::FlipSettings{false, false}, 1, 1, 195);
-		AI & ai = game_object1.add_component<AI>(30);
-		ai.arrive_on();
-		ai.flee_on();
+		AI & ai = game_object1.add_component<AI>(300);
+		// ai.arrive_on();
+		// ai.flee_on();
+		ai.path_follow_on();
+		ai.add_path_node(vec2{1200, 1200});
+		ai.add_path_node(vec2{-1200, 1200});
+		ai.add_path_node(vec2{1200, -1200});
+		ai.add_path_node(vec2{-1200, -1200});
 		game_object1.add_component<Rigidbody>(Rigidbody::Data{
-			.mass = 0.5f, .max_linear_velocity = {21, 21}, // sqrt(21^2 + 21^2) = 30
+			.mass = 0.5f, .max_linear_velocity = {50, 50}, // sqrt(21^2 + 21^2) = 30
 		});
 		game_object1.add_component<BehaviorScript>().set_script<Script1>();
 
-		game_object2.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{1036, 780},
+		game_object2.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{5000, 5000},
 										   1.0f);
 	}
 
-- 
cgit v1.2.3


From 6cee1cff083fc50eeedf88537965d3c79e7b790a Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Mon, 9 Dec 2024 11:28:06 +0100
Subject: Added Doxygen

---
 src/crepe/api/AI.cpp          | 23 ++++++++++++++++++++
 src/crepe/api/AI.h            | 50 ++++++++++++++++++++++++++++++++++---------
 src/crepe/system/AISystem.cpp |  1 +
 src/crepe/system/AISystem.h   | 44 +++++++++++++++++++++++++++++++++++++
 src/example/AITest.cpp        | 10 ++++-----
 5 files changed, 112 insertions(+), 16 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
index d785bb5..49f6b92 100644
--- a/src/crepe/api/AI.cpp
+++ b/src/crepe/api/AI.cpp
@@ -4,4 +4,27 @@ 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, vec2 center, float start_angle, bool clockwise) {
+	// The step size is determined by the radius (step size is in radians)
+	float step = 400.0f / radius;
+	// Force at least 16 steps (in case of a small radius)
+	if (step > 2 * M_PI / 16) {
+		step = 2 * M_PI / 16;
+	}
+	// The path node distance is determined by the step size and the radius
+	path_node_distance = radius * step * 0.75f;
+
+	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))});
+		}
+	}
+}
+
 } // namespace crepe
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index 35b8998..9f5c0a8 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -5,8 +5,15 @@
 
 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 BehaviorType {
 		NONE = 0x00000,
 		SEEK = 0x00002,
@@ -16,53 +23,76 @@ public:
 	};
 
 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/activated
+	 *
+	 * \param behavior The behavior to check
+	 * \return true if the behavior is on, false otherwise
+	 */
 	bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; }
+	//! Turn on the seek behavior
 	void seek_on() { flags |= SEEK; }
+	//! Turn off the seek behavior
 	void seek_off() {
 		if (on(SEEK)) flags ^= SEEK;
 	}
+	//! Turn on the flee behavior
 	void flee_on() { flags |= FLEE; }
+	//! Turn off the flee behavior
 	void flee_off() {
 		if (on(FLEE)) flags ^= FLEE;
 	}
+	//! Turn on the arrive behavior
 	void arrive_on() { flags |= ARRIVE; }
+	//! Turn off the arrive behavior
 	void arrive_off() {
 		if (on(ARRIVE)) 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() {
 		if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW;
 	}
 
+	//! Add a path node to the path
 	void add_path_node(vec2 node) { path.push_back(node); }
+	//! Create a circle path
+	void make_circle_path(float radius, vec2 center = {0, 0}, float start_angle = 0,
+						  bool clockwise = true);
 
 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 or arrive at
+	//! The target to seek or arrive at
 	vec2 seek_target;
-	// The target to flee from
+	//! The target to flee from
 	vec2 flee_target;
-	// The distance at which the entity will start to flee from the 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))
+	//! 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
+	//! The path to follow
 	std::vector<vec2> path;
-	// The distance from the path node at which the entity will move to the next node
+	//! The distance from the path node at which the entity will move to the next node
 	float path_node_distance = 400.0f;
-	// Looping behavior for the path
+	//! Looping behavior for the path
 	bool path_loop = true;
 
 private:
-	// The flags for the behaviors
+	//! The flags for the behaviors
 	int flags = 0;
-	// The current path index
+	//! The current path index
 	size_t path_index = 0;
 
-	// The AISystem is the only class that can access the private members of AI
+	//! The AISystem is the only class that should access the flags and path_index variables
 	friend class AISystem;
 };
 
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 55dc14c..7b801c3 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -17,6 +17,7 @@ void AISystem::update() {
 	ComponentManager & mgr = mediator.component_manager;
 	RefVector<AI> ai_components = mgr.get_components_by_type<AI>();
 
+	//TODO: Use fixed loop dt (this is not available at master at the moment)
 	double dt = LoopTimer::get_instance().get_delta_time();
 
 	for (AI & ai : ai_components) {
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 27861d9..670d20d 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -7,19 +7,63 @@
 
 namespace crepe {
 
+/**
+ * \brief The AISystem is used to control the movement of entities using AI.
+ *
+ * The AISystem is used to control the movement of entities using AI. The AISystem can be used to
+ * implement different behaviors such as seeking, fleeing, arriving, and path following.
+ */
 class AISystem : public System {
 public:
 	using System::System;
 
+	//! Update the AI system
 	void update() override;
 
 private:
+	/**
+	 * \brief Calculate the total force to apply to the entity
+	 *
+	 * \param ai The AI component
+	 */
 	vec2 calculate(AI & ai);
+	/**
+	 * \brief Accumulate the force to apply to the entity
+	 *
+	 * \param ai The AI component
+	 * \param running_total The running total of the force
+	 * \param force_to_add The force to add
+	 * \return true if the force was added, false otherwise
+	 */
 	bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add);
 
+	/**
+	 * \brief Calculate the seek force
+	 *
+	 * \param ai The AI component
+	 * \return The seek force
+	 */
 	vec2 seek(const AI & ai);
+	/**
+	 * \brief Calculate the flee force
+	 *
+	 * \param ai The AI component
+	 * \return The flee force
+	 */
 	vec2 flee(const AI & ai);
+	/**
+	 * \brief Calculate the arrive force
+	 *
+	 * \param ai The AI component
+	 * \return The arrive force
+	 */
 	vec2 arrive(const AI & ai);
+	/**
+	 * \brief Calculate the path follow force
+	 *
+	 * \param ai The AI component
+	 * \return The path follow force
+	 */
 	vec2 path_follow(AI & ai);
 };
 
diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp
index d12a99a..72e06cf 100644
--- a/src/example/AITest.cpp
+++ b/src/example/AITest.cpp
@@ -52,16 +52,14 @@ public:
 		Texture img = Texture("asset/texture/test_ap43.png");
 		game_object1.add_component<Sprite>(img, Color::MAGENTA,
 										   Sprite::FlipSettings{false, false}, 1, 1, 195);
-		AI & ai = game_object1.add_component<AI>(300);
+		AI & ai = game_object1.add_component<AI>(3000);
 		// ai.arrive_on();
 		// ai.flee_on();
 		ai.path_follow_on();
-		ai.add_path_node(vec2{1200, 1200});
-		ai.add_path_node(vec2{-1200, 1200});
-		ai.add_path_node(vec2{1200, -1200});
-		ai.add_path_node(vec2{-1200, -1200});
+		ai.make_circle_path(1000, {0, -1000}, 1.5707, true);
+		ai.make_circle_path(1000, {0, 1000}, 4.7124, false);
 		game_object1.add_component<Rigidbody>(Rigidbody::Data{
-			.mass = 0.5f, .max_linear_velocity = {50, 50}, // sqrt(21^2 + 21^2) = 30
+			.mass = 0.5f, .max_linear_velocity = {40, 40}, // sqrt(21^2 + 21^2) = 30
 		});
 		game_object1.add_component<BehaviorScript>().set_script<Script1>();
 
-- 
cgit v1.2.3


From c8d05f759bb1be0ec99e8f23eaa65c80c36ce03d Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Tue, 10 Dec 2024 08:48:06 +0100
Subject: Implemented feedback

---
 src/crepe/api/AI.cpp          | 15 ++++++++++++--
 src/crepe/api/AI.h            |  6 +++---
 src/crepe/system/AISystem.cpp | 46 ++++++++++++++++++++++---------------------
 src/crepe/system/AISystem.h   | 18 +++++++++++------
 4 files changed, 52 insertions(+), 33 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
index 00e5b37..472550b 100644
--- a/src/crepe/api/AI.cpp
+++ b/src/crepe/api/AI.cpp
@@ -1,10 +1,17 @@
+#include <stdexcept>
+
 #include "AI.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, 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");
+	}
+
 	// The step size is determined by the radius (step size is in radians)
 	float step = 400.0f / radius;
 	// Force at least 16 steps (in case of a small radius)
@@ -27,8 +34,12 @@ void AI::make_circle_path(float radius, vec2 center, float start_angle, bool clo
 	}
 }
 
-void AI::make_oval_path(float radius_x, float radius_y, vec2 center, float start_angle,
+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 = 400.0f / max_radius;
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index c95924d..de574cd 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -68,7 +68,7 @@ public:
 	 *
 	 * \param node The path node to add
 	 */
-	void add_path_node(vec2 node) { path.push_back(node); }
+	void add_path_node(const vec2 & node) { path.push_back(node); }
 	/**
 	 * \brief Make a circle path (for the path following behavior)
 	 *
@@ -79,7 +79,7 @@ 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, vec2 center = {0, 0}, float start_angle = 0,
+	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)
@@ -93,7 +93,7 @@ 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, vec2 center = {0, 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:
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 62fa553..4d5039b 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -2,7 +2,6 @@
 #include <cmath>
 
 #include "api/LoopTimer.h"
-#include "api/Rigidbody.h"
 #include "api/Transform.h"
 #include "manager/ComponentManager.h"
 #include "manager/Mediator.h"
@@ -27,10 +26,17 @@ void AISystem::update() {
 
 		RefVector<Rigidbody> rigidbodies
 			= 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");
+		}
 		Rigidbody & rigidbody = rigidbodies.front().get();
+		if (rigidbody.data.mass <= 0) {
+			throw std::runtime_error("Mass must be greater than 0");
+		}
 
 		// Calculate the force to apply to the entity
-		vec2 force = this->calculate(ai);
+		vec2 force = this->calculate(ai, rigidbody);
 		// Calculate the acceleration (using the above calculated force)
 		vec2 acceleration = force / rigidbody.data.mass;
 		// Finally, update Rigidbody's velocity
@@ -38,33 +44,33 @@ void AISystem::update() {
 	}
 }
 
-vec2 AISystem::calculate(AI & ai) {
+vec2 AISystem::calculate(AI & ai, const Rigidbody & rigidbody) {
 	vec2 force;
 
 	// Run all the behaviors that are on, and stop if the force gets too high
 	if (ai.on(AI::BehaviorType::FLEE)) {
-		vec2 force_to_add = this->flee(ai);
+		vec2 force_to_add = this->flee(ai, rigidbody);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
 	if (ai.on(AI::BehaviorType::ARRIVE)) {
-		vec2 force_to_add = this->arrive(ai);
+		vec2 force_to_add = this->arrive(ai, rigidbody);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
 	if (ai.on(AI::BehaviorType::SEEK)) {
-		vec2 force_to_add = this->seek(ai);
+		vec2 force_to_add = this->seek(ai, rigidbody);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
 	if (ai.on(AI::BehaviorType::PATH_FOLLOW)) {
-		vec2 force_to_add = this->path_follow(ai);
+		vec2 force_to_add = this->path_follow(ai, rigidbody);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
@@ -74,7 +80,7 @@ vec2 AISystem::calculate(AI & ai) {
 	return force;
 }
 
-bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add) {
+bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 force_to_add) {
 	float magnitude = running_total.length();
 	float magnitude_remaining = ai.max_force - magnitude;
 
@@ -96,13 +102,11 @@ bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add
 	return true;
 }
 
-vec2 AISystem::seek(const AI & ai) {
+vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody) const {
 	const Mediator & mediator = this->mediator;
 	ComponentManager & mgr = mediator.component_manager;
 	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
 	Transform & transform = transforms.front().get();
-	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
-	Rigidbody & rigidbody = rigidbodies.front().get();
 
 	// Calculate the desired velocity
 	vec2 desired_velocity = ai.seek_target - transform.position;
@@ -112,13 +116,11 @@ vec2 AISystem::seek(const AI & ai) {
 	return desired_velocity - rigidbody.data.linear_velocity;
 }
 
-vec2 AISystem::flee(const AI & ai) {
+vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody) const {
 	const Mediator & mediator = this->mediator;
 	ComponentManager & mgr = mediator.component_manager;
 	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
 	Transform & transform = transforms.front().get();
-	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
-	Rigidbody & rigidbody = rigidbodies.front().get();
 
 	// Calculate the desired velocity if the entity is within the panic distance
 	vec2 desired_velocity = transform.position - ai.flee_target;
@@ -131,18 +133,20 @@ vec2 AISystem::flee(const AI & ai) {
 	return desired_velocity - rigidbody.data.linear_velocity;
 }
 
-vec2 AISystem::arrive(const AI & ai) {
+vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody) const {
 	const Mediator & mediator = this->mediator;
 	ComponentManager & mgr = mediator.component_manager;
 	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
 	Transform & transform = transforms.front().get();
-	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
-	Rigidbody & rigidbody = rigidbodies.front().get();
 
 	// Calculate the desired velocity (taking into account the deceleration rate)
 	vec2 to_target = ai.seek_target - transform.position;
 	float distance = to_target.length();
 	if (distance > 0.0f) {
+		if (ai.arrive_deceleration <= 0.0f) {
+			throw std::runtime_error("Deceleration rate must be greater than 0");
+		}
+
 		float speed = distance / ai.arrive_deceleration;
 		speed = std::min(speed, rigidbody.data.max_linear_velocity.length());
 		vec2 desired_velocity = to_target * (speed / distance);
@@ -153,13 +157,11 @@ vec2 AISystem::arrive(const AI & ai) {
 	return vec2{0, 0};
 }
 
-vec2 AISystem::path_follow(AI & ai) {
+vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody) {
 	const Mediator & mediator = this->mediator;
 	ComponentManager & mgr = mediator.component_manager;
 	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
 	Transform & transform = transforms.front().get();
-	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
-	Rigidbody & rigidbody = rigidbodies.front().get();
 
 	if (ai.path.empty()) {
 		return vec2{0, 0};
@@ -182,11 +184,11 @@ vec2 AISystem::path_follow(AI & ai) {
 			} else {
 				// If the path is not looping, arrive at the last node
 				ai.path_index = ai.path.size() - 1;
-				return this->arrive(ai);
+				return this->arrive(ai, rigidbody);
 			}
 		}
 	}
 
 	// Seek the target node
-	return this->seek(ai);
+	return this->seek(ai, rigidbody);
 }
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 670d20d..160df01 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "api/AI.h"
+#include "api/Rigidbody.h"
 
 #include "System.h"
 #include "types.h"
@@ -25,8 +26,9 @@ private:
 	 * \brief Calculate the total force to apply to the entity
 	 *
 	 * \param ai The AI component
+	 * \param rigidbody The Rigidbody component
 	 */
-	vec2 calculate(AI & ai);
+	vec2 calculate(AI & ai, const Rigidbody & rigidbody);
 	/**
 	 * \brief Accumulate the force to apply to the entity
 	 *
@@ -35,36 +37,40 @@ private:
 	 * \param force_to_add The force to add
 	 * \return true if the force was added, false otherwise
 	 */
-	bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add);
+	bool accumulate_force(const AI & ai, vec2 & running_total, vec2 force_to_add);
 
 	/**
 	 * \brief Calculate the seek force
 	 *
 	 * \param ai The AI component
+	 * \param rigidbody The Rigidbody component
 	 * \return The seek force
 	 */
-	vec2 seek(const AI & ai);
+	vec2 seek(const AI & ai, const Rigidbody & rigidbody) const;
 	/**
 	 * \brief Calculate the flee force
 	 *
 	 * \param ai The AI component
+	 * \param rigidbody The Rigidbody component
 	 * \return The flee force
 	 */
-	vec2 flee(const AI & ai);
+	vec2 flee(const AI & ai, const Rigidbody & rigidbody) const;
 	/**
 	 * \brief Calculate the arrive force
 	 *
 	 * \param ai The AI component
+	 * \param rigidbody The Rigidbody component
 	 * \return The arrive force
 	 */
-	vec2 arrive(const AI & ai);
+	vec2 arrive(const AI & ai, const Rigidbody & rigidbody) const;
 	/**
 	 * \brief Calculate the path follow force
 	 *
 	 * \param ai The AI component
+	 * \param rigidbody The Rigidbody component
 	 * \return The path follow force
 	 */
-	vec2 path_follow(AI & ai);
+	vec2 path_follow(AI & ai, const Rigidbody & rigidbody);
 };
 
 } // namespace crepe
-- 
cgit v1.2.3


From 4e7ddc9d8eb396f7160e09da8c9b3d797274600a Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Tue, 10 Dec 2024 08:53:03 +0100
Subject: Replaced copy by reference

---
 src/crepe/system/AISystem.cpp | 2 +-
 src/crepe/system/AISystem.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 4d5039b..b42cb11 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -80,7 +80,7 @@ vec2 AISystem::calculate(AI & ai, const Rigidbody & rigidbody) {
 	return force;
 }
 
-bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 force_to_add) {
+bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 & force_to_add) {
 	float magnitude = running_total.length();
 	float magnitude_remaining = ai.max_force - magnitude;
 
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 160df01..9a937d2 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -37,7 +37,7 @@ private:
 	 * \param force_to_add The force to add
 	 * \return true if the force was added, false otherwise
 	 */
-	bool accumulate_force(const AI & ai, vec2 & running_total, vec2 force_to_add);
+	bool accumulate_force(const AI & ai, vec2 & running_total, vec2 & force_to_add);
 
 	/**
 	 * \brief Calculate the seek force
-- 
cgit v1.2.3


From 6fc38e15e4b88d480d5fcb69eec36f7d8685e853 Mon Sep 17 00:00:00 2001
From: max-001 <maxsmits21@kpnmail.nl>
Date: Tue, 10 Dec 2024 09:21:58 +0100
Subject: Passing transform by reference

---
 src/crepe/system/AISystem.cpp | 49 ++++++++++++++++---------------------------
 src/crepe/system/AISystem.h   | 13 ++++++++----
 2 files changed, 27 insertions(+), 35 deletions(-)

(limited to 'src/crepe/system/AISystem.h')

diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index b42cb11..d1ebeb5 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -2,7 +2,6 @@
 #include <cmath>
 
 #include "api/LoopTimer.h"
-#include "api/Transform.h"
 #include "manager/ComponentManager.h"
 #include "manager/Mediator.h"
 
@@ -45,32 +44,37 @@ void AISystem::update() {
 }
 
 vec2 AISystem::calculate(AI & ai, const Rigidbody & rigidbody) {
+	const Mediator & mediator = this->mediator;
+	ComponentManager & mgr = mediator.component_manager;
+	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
+	Transform & transform = transforms.front().get();
+
 	vec2 force;
 
 	// Run all the behaviors that are on, and stop if the force gets too high
 	if (ai.on(AI::BehaviorType::FLEE)) {
-		vec2 force_to_add = this->flee(ai, rigidbody);
+		vec2 force_to_add = this->flee(ai, rigidbody, transform);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
 	if (ai.on(AI::BehaviorType::ARRIVE)) {
-		vec2 force_to_add = this->arrive(ai, rigidbody);
+		vec2 force_to_add = this->arrive(ai, rigidbody, transform);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
 	if (ai.on(AI::BehaviorType::SEEK)) {
-		vec2 force_to_add = this->seek(ai, rigidbody);
+		vec2 force_to_add = this->seek(ai, rigidbody, transform);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
 		}
 	}
 	if (ai.on(AI::BehaviorType::PATH_FOLLOW)) {
-		vec2 force_to_add = this->path_follow(ai, rigidbody);
+		vec2 force_to_add = this->path_follow(ai, rigidbody, transform);
 
 		if (!this->accumulate_force(ai, force, force_to_add)) {
 			return force;
@@ -102,12 +106,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 {
-	const Mediator & mediator = this->mediator;
-	ComponentManager & mgr = mediator.component_manager;
-	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
-	Transform & transform = transforms.front().get();
-
+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();
@@ -116,12 +116,8 @@ vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody) const {
 	return desired_velocity - rigidbody.data.linear_velocity;
 }
 
-vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody) const {
-	const Mediator & mediator = this->mediator;
-	ComponentManager & mgr = mediator.component_manager;
-	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
-	Transform & transform = transforms.front().get();
-
+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) {
@@ -133,12 +129,8 @@ vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody) const {
 	return desired_velocity - rigidbody.data.linear_velocity;
 }
 
-vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody) const {
-	const Mediator & mediator = this->mediator;
-	ComponentManager & mgr = mediator.component_manager;
-	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
-	Transform & transform = transforms.front().get();
-
+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.seek_target - transform.position;
 	float distance = to_target.length();
@@ -157,12 +149,7 @@ vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody) const {
 	return vec2{0, 0};
 }
 
-vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody) {
-	const Mediator & mediator = this->mediator;
-	ComponentManager & mgr = mediator.component_manager;
-	RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
-	Transform & transform = transforms.front().get();
-
+vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform) {
 	if (ai.path.empty()) {
 		return vec2{0, 0};
 	}
@@ -184,11 +171,11 @@ vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody) {
 			} else {
 				// If the path is not looping, arrive at the last node
 				ai.path_index = ai.path.size() - 1;
-				return this->arrive(ai, rigidbody);
+				return this->arrive(ai, rigidbody, transform);
 			}
 		}
 	}
 
 	// Seek the target node
-	return this->seek(ai, rigidbody);
+	return this->seek(ai, rigidbody, transform);
 }
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 9a937d2..d5f8a8e 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -4,6 +4,7 @@
 #include "api/Rigidbody.h"
 
 #include "System.h"
+#include "api/Transform.h"
 #include "types.h"
 
 namespace crepe {
@@ -44,33 +45,37 @@ private:
 	 *
 	 * \param ai The AI component
 	 * \param rigidbody The Rigidbody component
+	 * \param transform The Transform component
 	 * \return The seek force
 	 */
-	vec2 seek(const AI & ai, const Rigidbody & rigidbody) const;
+	vec2 seek(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) const;
 	/**
 	 * \brief Calculate the flee force
 	 *
 	 * \param ai The AI component
 	 * \param rigidbody The Rigidbody component
+	 * \param transform The Transform component
 	 * \return The flee force
 	 */
-	vec2 flee(const AI & ai, const Rigidbody & rigidbody) const;
+	vec2 flee(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) const;
 	/**
 	 * \brief Calculate the arrive force
 	 *
 	 * \param ai The AI component
 	 * \param rigidbody The Rigidbody component
+	 * \param transform The Transform component
 	 * \return The arrive force
 	 */
-	vec2 arrive(const AI & ai, const Rigidbody & rigidbody) const;
+	vec2 arrive(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) const;
 	/**
 	 * \brief Calculate the path follow force
 	 *
 	 * \param ai The AI component
 	 * \param rigidbody The Rigidbody component
+	 * \param transform The Transform component
 	 * \return The path follow force
 	 */
-	vec2 path_follow(AI & ai, const Rigidbody & rigidbody);
+	vec2 path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform);
 };
 
 } // namespace crepe
-- 
cgit v1.2.3