diff options
Diffstat (limited to 'src')
70 files changed, 1559 insertions, 372 deletions
| diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3f29da..97b21f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,5 +40,6 @@ install(  target_link_libraries(test_main  	PRIVATE gtest +	PRIVATE gmock  	PUBLIC crepe  ) diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 7e176e7..da9d492 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,13 +1,10 @@  target_sources(crepe PUBLIC  	Particle.cpp -	ComponentManager.cpp  	Component.cpp  	Collider.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES -	ComponentManager.h -	ComponentManager.hpp  	Component.h  	Collider.h  	ValueBroker.h @@ -16,6 +13,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  add_subdirectory(api)  add_subdirectory(facade) +add_subdirectory(manager)  add_subdirectory(system)  add_subdirectory(util) diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index 7bbace0..d22afdf 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -4,12 +4,12 @@  using namespace crepe; -BehaviorScript::BehaviorScript(game_object_id_t id, ComponentManager & mgr) +BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator)  	: Component(id), -	  component_manager(mgr) {} +	  mediator(mediator) {}  template <>  BehaviorScript & GameObject::add_component<BehaviorScript>() {  	ComponentManager & mgr = this->component_manager; -	return mgr.add_component<BehaviorScript>(this->id, mgr); +	return mgr.add_component<BehaviorScript>(this->id, mgr.mediator);  } diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index d556fe5..3909b96 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -23,14 +23,13 @@ class BehaviorScript : public Component {  protected:  	/**  	 * \param id Parent \c GameObject id -	 * \param component_manager Reference to component manager (passed through to \c Script -	 * instance) +	 * \param mediator Mediator reference  	 *  	 * \note Calls to this constructor (should) always pass through \c GameObject::add_component,  	 * which has an exception for this specific component type. This was done so the user does  	 * not have to pass references used within \c Script to each \c BehaviorScript instance.  	 */ -	BehaviorScript(game_object_id_t id, ComponentManager & component_manager); +	BehaviorScript(game_object_id_t id, Mediator & mediator);  	//! Only ComponentManager is allowed to instantiate BehaviorScript  	friend class ComponentManager; @@ -55,8 +54,8 @@ protected:  	friend class ScriptSystem;  protected: -	//! Reference to component manager (passed to Script) -	ComponentManager & component_manager; +	//! Reference mediator +	Mediator & mediator;  };  /** diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index bd59337..b9bb1e2 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -13,14 +13,12 @@ template <class T, typename... Args>  BehaviorScript & BehaviorScript::set_script(Args &&... args) {  	dbg_trace();  	static_assert(std::is_base_of<Script, T>::value); -	Script * s = new T(std::forward<Args>(args)...); +	this->script = std::unique_ptr<Script>(new T(std::forward<Args>(args)...)); -	s->game_object_id = this->game_object_id; -	s->active = this->active; -	s->component_manager = this->component_manager; -	s->event_manager = EventManager::get_instance(); +	this->script->game_object_id = this->game_object_id; +	this->script->active = this->active; +	this->script->mediator = this->mediator; -	this->script = std::unique_ptr<Script>(s);  	return *this;  } diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp new file mode 100644 index 0000000..76f74f0 --- /dev/null +++ b/src/crepe/api/Button.cpp @@ -0,0 +1,11 @@ +#include "Button.h" + +namespace crepe { + +Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, +			   const std::function<void()> & on_click, bool is_toggle) +	: UIObject(id, dimensions, offset), +	  is_toggle(is_toggle), +	  on_click(on_click) {} + +} // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h new file mode 100644 index 0000000..26e7526 --- /dev/null +++ b/src/crepe/api/Button.h @@ -0,0 +1,67 @@ +#pragma once + +#include <functional> + +#include "UIObject.h" + +namespace crepe { + +//! Represents a clickable UI button, derived from the UiObject class. +class Button : public UIObject { +public: +	/** +	 * \brief Constructs a Button with the specified game object ID and dimensions. +	 *  +	 * \param id The unique ID of the game object associated with this button. +	 * \param dimensions The width and height of the UIObject +	 * \param offset The offset relative this GameObjects Transform +	 * \param is_toggle Optional flag to indicate if the button is a toggle button. Defaults to false. +	 * \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, bool is_toggle = false); + +	/** +	 * \brief Indicates if the button is a toggle button (can be pressed and released). +	 *  +	 * A toggle button allows for a pressed/released state, whereas a regular button +	 * typically only has an on-click state. +	 */ +	bool is_toggle = false; +	// 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; + +	/** +	 * \brief Callback function to be executed when the mouse enters the button's boundaries. +	 * +	 * This function is triggered when the mouse cursor moves over the button, allowing +	 * custom actions like visual effects, highlighting, or sound effects. +	 */ +	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; + +private: +	//! friend relation for is_pressed and hover variables +	friend class InputSystem; +	//! Indicates whether the toggle button is pressed +	bool is_pressed = false; +	//! Indicates whether the mouse is currently hovering over the button +	bool hover = false; + +public: +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index d42b459..0355b72 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -9,13 +9,10 @@ target_sources(crepe PUBLIC  	Texture.cpp  	AssetManager.cpp  	Sprite.cpp -	SaveManager.cpp  	Config.cpp  	Metadata.cpp -	SceneManager.cpp  	Camera.cpp  	Animator.cpp -	EventManager.cpp  	IKeyListener.cpp  	IMouseListener.cpp  	LoopManager.cpp @@ -23,6 +20,8 @@ target_sources(crepe PUBLIC  	Asset.cpp  	EventHandler.cpp  	Script.cpp +	Button.cpp +	UIObject.cpp  	AI.cpp  ) @@ -42,15 +41,10 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	Texture.h   	AssetManager.h   	AssetManager.hpp -	SaveManager.h  	Scene.h  	Metadata.h -	SceneManager.h -	SceneManager.hpp  	Camera.h  	Animator.h -	EventManager.h -	EventManager.hpp  	EventHandler.h  	EventHandler.hpp  	Event.h @@ -59,5 +53,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	LoopManager.h  	LoopTimer.h  	Asset.h +	Button.h +	UIObject.h  	AI.h  ) diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index b267e3e..6298118 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -88,9 +88,31 @@ public:  	//! Y-coordinate of the mouse position at the time of the event.  	int mouse_y = 0; + +	// Movement since last event in x +	int delta_x = 0; + +	// Movement since last event in y +	int delta_y = 0;  };  /** + * \brief Event triggered when the mouse is moved. + */ +class MouseScrollEvent : public Event { +public: +	//! X-coordinate of the mouse position at the time of the event. +	int mouse_x = 0; + +	//! Y-coordinate of the mouse position at the time of the event. +	int mouse_y = 0; + +	//! scroll direction (-1 = down, 1 = up) +	int scroll_direction = 0; +	//! scroll amount in y axis (from and away from the person). +	float scroll_delta = 0; +}; +/**   * \brief Event triggered during a collision between objects.   */  class CollisionEvent : public Event {}; diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h index ef659fd..7bdd9a3 100644 --- a/src/crepe/api/EventHandler.h +++ b/src/crepe/api/EventHandler.h @@ -29,29 +29,29 @@ using EventHandler = std::function<bool(const EventType & e)>;  class IEventHandlerWrapper {  public:  	/** -     * \brief Virtual destructor for IEventHandlerWrapper. -     */ +	 * \brief Virtual destructor for IEventHandlerWrapper. +	 */  	virtual ~IEventHandlerWrapper() = default;  	/** -     * \brief Executes the handler with the given event. -     *  -     * This method calls the `call()` method of the derived class, passing the event to the handler. -     *  -     * \param e The event to be processed. -     * \return A boolean value indicating whether the event is handled. -     */ +	 * \brief Executes the handler with the given event. +	 *  +	 * This method calls the `call()` method of the derived class, passing the event to the handler. +	 *  +	 * \param e The event to be processed. +	 * \return A boolean value indicating whether the event is handled. +	 */  	bool exec(const Event & e);  private:  	/** -     * \brief The method responsible for handling the event. -     *  -     * This method is implemented by derived classes to process the event. -     *  -     * \param e The event to be processed. -     * \return A boolean value indicating whether the event is handled. -     */ +	 * \brief The method responsible for handling the event. +	 *  +	 * This method is implemented by derived classes to process the event. +	 *  +	 * \param e The event to be processed. +	 * \return A boolean value indicating whether the event is handled. +	 */  	virtual bool call(const Event & e) = 0;  }; @@ -69,23 +69,23 @@ template <typename EventType>  class EventHandlerWrapper : public IEventHandlerWrapper {  public:  	/** -     * \brief Constructs an EventHandlerWrapper with a given handler. -     *  -     * The constructor takes an event handler function and stores it in the wrapper. -     *  -     * \param handler The event handler function. -     */ +	 * \brief Constructs an EventHandlerWrapper with a given handler. +	 *  +	 * The constructor takes an event handler function and stores it in the wrapper. +	 *  +	 * \param handler The event handler function. +	 */  	explicit EventHandlerWrapper(const EventHandler<EventType> & handler);  private:  	/** -     * \brief Calls the stored event handler with the event. -     *  -     * This method casts the event to the appropriate type and calls the handler. -     *  -     * \param e The event to be handled. -     * \return A boolean value indicating whether the event is handled. -     */ +	 * \brief Calls the stored event handler with the event. +	 *  +	 * This method casts the event to the appropriate type and calls the handler. +	 *  +	 * \param e The event to be handled. +	 * \return A boolean value indicating whether the event is handled. +	 */  	bool call(const Event & e) override;  	//! The event handler function.  	EventHandler<EventType> handler; diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp index 17b17d7..a6b45b0 100644 --- a/src/crepe/api/GameObject.hpp +++ b/src/crepe/api/GameObject.hpp @@ -1,6 +1,6 @@  #pragma once -#include "../ComponentManager.h" +#include "../manager/ComponentManager.h"  #include "GameObject.h" diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h index 328a4c2..180a0a6 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -1,8 +1,9 @@  #pragma once +#include "../manager/EventManager.h" +  #include "Event.h"  #include "EventHandler.h" -#include "EventManager.h"  namespace crepe { @@ -13,9 +14,9 @@ namespace crepe {  class IKeyListener {  public:  	/** -     * \brief Constructs an IKeyListener with a specified channel. -     * \param channel The channel ID for event handling. -     */ +	 * \brief Constructs an IKeyListener with a specified channel. +	 * \param channel The channel ID for event handling. +	 */  	IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL);  	virtual ~IKeyListener();  	IKeyListener(const IKeyListener &) = delete; @@ -24,17 +25,17 @@ public:  	IKeyListener(IKeyListener &&) = delete;  	/** -     * \brief Pure virtual function to handle key press events. -     * \param event The key press event to handle. -     * \return True if the event was handled, false otherwise. -     */ +	 * \brief Pure virtual function to handle key press events. +	 * \param event The key press event to handle. +	 * \return True if the event was handled, false otherwise. +	 */  	virtual bool on_key_pressed(const KeyPressEvent & event) = 0;  	/** -     * \brief Pure virtual function to handle key release events. -     * \param event The key release event to handle. -     * \return True if the event was handled, false otherwise. -     */ +	 * \brief Pure virtual function to handle key release events. +	 * \param event The key release event to handle. +	 * \return True if the event was handled, false otherwise. +	 */  	virtual bool on_key_released(const KeyReleaseEvent & event) = 0;  private: diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h index 15e1619..e19897d 100644 --- a/src/crepe/api/IMouseListener.h +++ b/src/crepe/api/IMouseListener.h @@ -1,8 +1,9 @@  #pragma once +#include "../manager/EventManager.h" +  #include "Event.h"  #include "EventHandler.h" -#include "EventManager.h"  namespace crepe { @@ -13,9 +14,9 @@ namespace crepe {  class IMouseListener {  public:  	/** -     * \brief Constructs an IMouseListener with a specified channel. -     * \param channel The channel ID for event handling. -     */ +	 * \brief Constructs an IMouseListener with a specified channel. +	 * \param channel The channel ID for event handling. +	 */  	IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL);  	virtual ~IMouseListener();  	IMouseListener & operator=(const IMouseListener &) = delete; @@ -24,36 +25,36 @@ public:  	IMouseListener(IMouseListener &&) = delete;  	/** -     * \brief Move assignment operator (deleted). -     */ +	 * \brief Move assignment operator (deleted). +	 */  	IMouseListener & operator=(IMouseListener &&) = delete;  	/** -     * \brief Handles a mouse click event. -     * \param event The mouse click event to handle. -     * \return True if the event was handled, false otherwise. -     */ +	 * \brief Handles a mouse click event. +	 * \param event The mouse click event to handle. +	 * \return True if the event was handled, false otherwise. +	 */  	virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0;  	/** -     * \brief Handles a mouse press event. -     * \param event The mouse press event to handle. -     * \return True if the event was handled, false otherwise. -     */ +	 * \brief Handles a mouse press event. +	 * \param event The mouse press event to handle. +	 * \return True if the event was handled, false otherwise. +	 */  	virtual bool on_mouse_pressed(const MousePressEvent & event) = 0;  	/** -     * \brief Handles a mouse release event. -     * \param event The mouse release event to handle. -     * \return True if the event was handled, false otherwise. -     */ +	 * \brief Handles a mouse release event. +	 * \param event The mouse release event to handle. +	 * \return True if the event was handled, false otherwise. +	 */  	virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0;  	/** -     * \brief Handles a mouse move event. -     * \param event The mouse move event to handle. -     * \return True if the event was handled, false otherwise. -     */ +	 * \brief Handles a mouse move event. +	 * \param event The mouse move event to handle. +	 * \return True if the event was handled, false otherwise. +	 */  	virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0;  private: diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index 9e173e0..fcfc080 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -1,5 +1,5 @@  #pragma once - +namespace crepe {  //! Enumeration for mouse button inputs, including standard and extended buttons.  enum class MouseButton {  	NONE = 0, //!< No mouse button input. @@ -85,9 +85,9 @@ enum class Keycode {  	PRINT_SCREEN = 283, //!< Print Screen key.  	PAUSE = 284, //!< Pause key.  	/** -	 * \name Function keys (F1-F25). -	 * \{ -	 */ +		 * \name Function keys (F1-F25). +		 * \{ +		 */  	F1 = 290,  	F2 = 291,  	F3 = 292, @@ -115,9 +115,9 @@ enum class Keycode {  	F25 = 314,  	/// \}  	/** -	 * \name Keypad digits and operators. -	 * \{ -	 */ +		 * \name Keypad digits and operators. +		 * \{ +		 */  	KP0 = 320,  	KP1 = 321,  	KP2 = 322, @@ -137,9 +137,9 @@ enum class Keycode {  	KP_EQUAL = 336,  	/// \}  	/** -	 * \name Modifier keys. -	 * \{ -	 */ +		 * \name Modifier keys. +		 * \{ +		 */  	LEFT_SHIFT = 340,  	LEFT_CONTROL = 341,  	LEFT_ALT = 342, @@ -151,3 +151,4 @@ enum class Keycode {  	/// \}  	MENU = 348, //!< Menu key.  }; +} // namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 46831e8..e4b1609 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,32 +1,33 @@  #include "../facade/SDLContext.h" -  #include "../system/AISystem.h"  #include "../system/AnimatorSystem.h"  #include "../system/CollisionSystem.h" +#include "../system/InputSystem.h"  #include "../system/ParticleSystem.h"  #include "../system/PhysicsSystem.h"  #include "../system/RenderSystem.h"  #include "../system/ScriptSystem.h"  #include "LoopManager.h" -#include "LoopTimer.h"  using namespace crepe;  using namespace std;  LoopManager::LoopManager() { +	this->mediator.component_manager = this->component_manager; +	this->mediator.scene_manager = this->scene_manager; +  	this->load_system<AnimatorSystem>();  	this->load_system<CollisionSystem>();  	this->load_system<ParticleSystem>();  	this->load_system<PhysicsSystem>();  	this->load_system<RenderSystem>();  	this->load_system<ScriptSystem>(); +	this->load_system<InputSystem>();  	this->load_system<AISystem>();  } -void LoopManager::process_input() { -	SDLContext::get_instance().handle_events(this->game_running); -} +void LoopManager::process_input() { this->get_system<InputSystem>().update(); }  void LoopManager::start() {  	this->setup(); @@ -37,7 +38,7 @@ void LoopManager::set_running(bool running) { this->game_running = running; }  void LoopManager::fixed_update() {}  void LoopManager::loop() { -	LoopTimer & timer = LoopTimer::get_instance(); +	LoopTimer & timer = this->loop_timer;  	timer.start();  	while (game_running) { @@ -62,20 +63,21 @@ void LoopManager::loop() {  }  void LoopManager::setup() { +	LoopTimer & timer = this->loop_timer; +  	this->game_running = true; -	LoopTimer::get_instance().start(); -	LoopTimer::get_instance().set_fps(200); +	timer.start(); +	timer.set_fps(200);  	this->scene_manager.load_next_scene();  }  void LoopManager::render() { -	if (this->game_running) { -		this->get_system<RenderSystem>().update(); -	} +	if (!this->game_running) return; + +	this->get_system<RenderSystem>().update();  }  void LoopManager::update() { -	LoopTimer & timer = LoopTimer::get_instance();  	this->get_system<AnimatorSystem>().update();  	this->get_system<AISystem>().update();  } diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 13e6dac..d8910a0 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -2,9 +2,12 @@  #include <memory> -#include "../ComponentManager.h" +#include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h" +#include "../manager/SceneManager.h"  #include "../system/System.h" -#include "api/SceneManager.h" + +#include "LoopTimer.h"  namespace crepe { @@ -85,10 +88,18 @@ private:  	bool game_running = false;  private: +	//! Global context +	Mediator mediator; +  	//! Component manager instance -	ComponentManager component_manager{}; +	ComponentManager component_manager{mediator};  	//! Scene manager instance -	SceneManager scene_manager{component_manager}; +	SceneManager scene_manager{mediator}; + +	//! SDL context \todo no more singletons! +	SDLContext & sdl_context = SDLContext::get_instance(); +	//! Loop timer \todo no more singletons! +	LoopTimer & loop_timer = LoopTimer::get_instance();  private:  	/** diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp index 9cf470b..266758a 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/api/LoopManager.hpp @@ -38,8 +38,11 @@ void LoopManager::load_system() {  	static_assert(is_base_of<System, T>::value,  				  "load_system must recieve a derivative class of System"); -	System * system = new T(this->component_manager); -	this->systems[typeid(T)] = unique_ptr<System>(system); +	const type_info & type = typeid(T); +	if (this->systems.contains(type)) +		throw runtime_error(format("LoopManager: {} is already initialized", type.name())); +	System * system = new T(this->mediator); +	this->systems[type] = unique_ptr<System>(system);  }  } // namespace crepe diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index f6fdb2a..9f1e8ce 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -2,6 +2,7 @@  #include <string> +#include "../manager/Mediator.h"  #include "../util/OptionalRef.h"  namespace crepe { @@ -34,6 +35,9 @@ public:  	 */  	virtual std::string get_name() const = 0; +	// TODO: Late references should ALWAYS be private! This is currently kept as-is so unit tests +	// keep passing, but this reference should not be directly accessible by the user!!! +  protected:  	/**  	 * \name Late references @@ -46,8 +50,8 @@ protected:  	 *  	 * \{  	 */ -	//! Reference to the ComponentManager -	OptionalRef<ComponentManager> component_manager; +	//! Mediator reference +	OptionalRef<Mediator> mediator;  	//! \}  }; diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index fcbe4c7..4091fd4 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -1,11 +1,17 @@ +#include <string> + +#include "../manager/SceneManager.h" +  #include "Script.h"  using namespace crepe; +using namespace std;  Script::~Script() { -	EventManager & evmgr = this->event_manager; +	Mediator & mediator = this->mediator; +	EventManager & mgr = mediator.event_manager;  	for (auto id : this->listeners) { -		evmgr.unsubscribe(id); +		mgr.unsubscribe(id);  	}  } @@ -13,3 +19,9 @@ template <>  void Script::subscribe(const EventHandler<CollisionEvent> & callback) {  	this->subscribe_internal(callback, this->game_object_id);  } + +void Script::set_next_scene(const string & name) { +	Mediator & mediator = this->mediator; +	SceneManager & mgr = mediator.scene_manager; +	mgr.set_next_scene(name); +} diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index a0870cb..1b339b0 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,11 +2,11 @@  #include <vector> +#include "../manager/EventManager.h" +#include "../manager/Mediator.h"  #include "../types.h"  #include "../util/OptionalRef.h" -#include "EventManager.h" -  namespace crepe {  class ScriptSystem; @@ -106,6 +106,12 @@ protected:  	template <typename EventType>  	void subscribe(const EventHandler<EventType> & callback); +	/** +	 * \brief Set the next scene using SceneManager +	 * \see SceneManager::set_next_scene +	 */ +	void set_next_scene(const std::string & name); +  	//! \}  private: @@ -160,10 +166,8 @@ private:  	game_object_id_t game_object_id;  	//! Reference to parent component  	OptionalRef<bool> active; -	//! Reference to component manager instance -	OptionalRef<ComponentManager> component_manager; -	//! Reference to event manager instance -	OptionalRef<EventManager> event_manager; +	//! Mediator reference +	OptionalRef<Mediator> mediator;  	//! \}  private: diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index a2463bf..45f1ff1 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -1,6 +1,6 @@  #pragma once -#include "../ComponentManager.h" +#include "../manager/ComponentManager.h"  #include "BehaviorScript.h"  #include "Script.h" @@ -20,7 +20,8 @@ T & Script::get_component() const {  template <typename T>  RefVector<T> Script::get_components() const { -	ComponentManager & mgr = this->component_manager; +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager;  	return mgr.get_components_by_id<T>(this->game_object_id);  } @@ -33,7 +34,8 @@ void Script::logf(Args &&... args) {  template <typename EventType>  void Script::subscribe_internal(const EventHandler<EventType> & callback,  								event_channel_t channel) { -	EventManager & mgr = this->event_manager; +	Mediator & mediator = this->mediator; +	EventManager & mgr = mediator.event_manager;  	subscription_t listener = mgr.subscribe<EventType>(  		[this, callback](const EventType & data) -> bool {  			bool & active = this->active; diff --git a/src/crepe/api/UIObject.cpp b/src/crepe/api/UIObject.cpp new file mode 100644 index 0000000..d239b89 --- /dev/null +++ b/src/crepe/api/UIObject.cpp @@ -0,0 +1,8 @@ +#include "UIObject.h" + +using namespace crepe; + +UIObject::UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset) +	: Component(id), +	  dimensions(dimensions), +	  offset(offset) {} diff --git a/src/crepe/api/UIObject.h b/src/crepe/api/UIObject.h new file mode 100644 index 0000000..f7f4fba --- /dev/null +++ b/src/crepe/api/UIObject.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../Component.h" + +namespace crepe { + +/** + * \brief Represents a UI object in the game, derived from the Component class. + */ +class UIObject : public Component { +public: +	/** +	 * \brief Constructs a UiObject with the specified game object ID. +	 * \param id The unique ID of the game object associated with this UI object. +	 * \param dimensions width and height of the UIObject +	 * \param offset Offset relative to the GameObject Transform +	 */ +	UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset); +	//! Width and height of the UIObject +	vec2 dimensions; +	//! Position offset relative to this GameObjects Transform +	vec2 offset; +}; + +} // namespace crepe diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index e8be7ca..ad9f1f0 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -6,6 +6,7 @@  #include <SDL2/SDL_render.h>  #include <SDL2/SDL_surface.h>  #include <SDL2/SDL_video.h> +#include <array>  #include <cmath>  #include <cstddef>  #include <cstdint> @@ -18,6 +19,7 @@  #include "../api/Config.h"  #include "../api/Sprite.h"  #include "../api/Texture.h" +#include "../manager/EventManager.h"  #include "../util/Log.h"  #include "SDLContext.h" @@ -76,25 +78,141 @@ SDLContext::~SDLContext() {  	IMG_Quit();  	SDL_Quit();  } -void SDLContext::handle_events(bool & running) { -	//TODO: wouter i need events -	/* -	SDL_Event event; -	SDL_PollEvent(&event); -	switch (event.type) { -		case SDL_QUIT: -			running = false; -			break; -		case SDL_KEYDOWN: -			triggerEvent(KeyPressedEvent(getCustomKey(event.key.keysym.sym))); -			break; -		case SDL_MOUSEBUTTONDOWN: -			int x, y; -			SDL_GetMouseState(&x, &y); -			triggerEvent(MousePressedEvent(x, y)); -			break; + +Keycode SDLContext::sdl_to_keycode(SDL_Keycode sdl_key) { +	static const std::array<Keycode, SDL_NUM_SCANCODES> LOOKUP_TABLE = [] { +		std::array<Keycode, SDL_NUM_SCANCODES> table{}; +		table.fill(Keycode::NONE); + +		table[SDL_SCANCODE_SPACE] = Keycode::SPACE; +		table[SDL_SCANCODE_APOSTROPHE] = Keycode::APOSTROPHE; +		table[SDL_SCANCODE_COMMA] = Keycode::COMMA; +		table[SDL_SCANCODE_MINUS] = Keycode::MINUS; +		table[SDL_SCANCODE_PERIOD] = Keycode::PERIOD; +		table[SDL_SCANCODE_SLASH] = Keycode::SLASH; +		table[SDL_SCANCODE_0] = Keycode::D0; +		table[SDL_SCANCODE_1] = Keycode::D1; +		table[SDL_SCANCODE_2] = Keycode::D2; +		table[SDL_SCANCODE_3] = Keycode::D3; +		table[SDL_SCANCODE_4] = Keycode::D4; +		table[SDL_SCANCODE_5] = Keycode::D5; +		table[SDL_SCANCODE_6] = Keycode::D6; +		table[SDL_SCANCODE_7] = Keycode::D7; +		table[SDL_SCANCODE_8] = Keycode::D8; +		table[SDL_SCANCODE_9] = Keycode::D9; +		table[SDL_SCANCODE_SEMICOLON] = Keycode::SEMICOLON; +		table[SDL_SCANCODE_EQUALS] = Keycode::EQUAL; +		table[SDL_SCANCODE_A] = Keycode::A; +		table[SDL_SCANCODE_B] = Keycode::B; +		table[SDL_SCANCODE_C] = Keycode::C; +		table[SDL_SCANCODE_D] = Keycode::D; +		table[SDL_SCANCODE_E] = Keycode::E; +		table[SDL_SCANCODE_F] = Keycode::F; +		table[SDL_SCANCODE_G] = Keycode::G; +		table[SDL_SCANCODE_H] = Keycode::H; +		table[SDL_SCANCODE_I] = Keycode::I; +		table[SDL_SCANCODE_J] = Keycode::J; +		table[SDL_SCANCODE_K] = Keycode::K; +		table[SDL_SCANCODE_L] = Keycode::L; +		table[SDL_SCANCODE_M] = Keycode::M; +		table[SDL_SCANCODE_N] = Keycode::N; +		table[SDL_SCANCODE_O] = Keycode::O; +		table[SDL_SCANCODE_P] = Keycode::P; +		table[SDL_SCANCODE_Q] = Keycode::Q; +		table[SDL_SCANCODE_R] = Keycode::R; +		table[SDL_SCANCODE_S] = Keycode::S; +		table[SDL_SCANCODE_T] = Keycode::T; +		table[SDL_SCANCODE_U] = Keycode::U; +		table[SDL_SCANCODE_V] = Keycode::V; +		table[SDL_SCANCODE_W] = Keycode::W; +		table[SDL_SCANCODE_X] = Keycode::X; +		table[SDL_SCANCODE_Y] = Keycode::Y; +		table[SDL_SCANCODE_Z] = Keycode::Z; +		table[SDL_SCANCODE_LEFTBRACKET] = Keycode::LEFT_BRACKET; +		table[SDL_SCANCODE_BACKSLASH] = Keycode::BACKSLASH; +		table[SDL_SCANCODE_RIGHTBRACKET] = Keycode::RIGHT_BRACKET; +		table[SDL_SCANCODE_GRAVE] = Keycode::GRAVE_ACCENT; +		table[SDL_SCANCODE_ESCAPE] = Keycode::ESCAPE; +		table[SDL_SCANCODE_RETURN] = Keycode::ENTER; +		table[SDL_SCANCODE_TAB] = Keycode::TAB; +		table[SDL_SCANCODE_BACKSPACE] = Keycode::BACKSPACE; +		table[SDL_SCANCODE_INSERT] = Keycode::INSERT; +		table[SDL_SCANCODE_DELETE] = Keycode::DELETE; +		table[SDL_SCANCODE_RIGHT] = Keycode::RIGHT; +		table[SDL_SCANCODE_LEFT] = Keycode::LEFT; +		table[SDL_SCANCODE_DOWN] = Keycode::DOWN; +		table[SDL_SCANCODE_UP] = Keycode::UP; +		table[SDL_SCANCODE_PAGEUP] = Keycode::PAGE_UP; +		table[SDL_SCANCODE_PAGEDOWN] = Keycode::PAGE_DOWN; +		table[SDL_SCANCODE_HOME] = Keycode::HOME; +		table[SDL_SCANCODE_END] = Keycode::END; +		table[SDL_SCANCODE_CAPSLOCK] = Keycode::CAPS_LOCK; +		table[SDL_SCANCODE_SCROLLLOCK] = Keycode::SCROLL_LOCK; +		table[SDL_SCANCODE_NUMLOCKCLEAR] = Keycode::NUM_LOCK; +		table[SDL_SCANCODE_PRINTSCREEN] = Keycode::PRINT_SCREEN; +		table[SDL_SCANCODE_PAUSE] = Keycode::PAUSE; +		table[SDL_SCANCODE_F1] = Keycode::F1; +		table[SDL_SCANCODE_F2] = Keycode::F2; +		table[SDL_SCANCODE_F3] = Keycode::F3; +		table[SDL_SCANCODE_F4] = Keycode::F4; +		table[SDL_SCANCODE_F5] = Keycode::F5; +		table[SDL_SCANCODE_F6] = Keycode::F6; +		table[SDL_SCANCODE_F7] = Keycode::F7; +		table[SDL_SCANCODE_F8] = Keycode::F8; +		table[SDL_SCANCODE_F9] = Keycode::F9; +		table[SDL_SCANCODE_F10] = Keycode::F10; +		table[SDL_SCANCODE_F11] = Keycode::F11; +		table[SDL_SCANCODE_F12] = Keycode::F12; +		table[SDL_SCANCODE_KP_0] = Keycode::KP0; +		table[SDL_SCANCODE_KP_1] = Keycode::KP1; +		table[SDL_SCANCODE_KP_2] = Keycode::KP2; +		table[SDL_SCANCODE_KP_3] = Keycode::KP3; +		table[SDL_SCANCODE_KP_4] = Keycode::KP4; +		table[SDL_SCANCODE_KP_5] = Keycode::KP5; +		table[SDL_SCANCODE_KP_6] = Keycode::KP6; +		table[SDL_SCANCODE_KP_7] = Keycode::KP7; +		table[SDL_SCANCODE_KP_8] = Keycode::KP8; +		table[SDL_SCANCODE_KP_9] = Keycode::KP9; +		table[SDL_SCANCODE_LSHIFT] = Keycode::LEFT_SHIFT; +		table[SDL_SCANCODE_LCTRL] = Keycode::LEFT_CONTROL; +		table[SDL_SCANCODE_LALT] = Keycode::LEFT_ALT; +		table[SDL_SCANCODE_LGUI] = Keycode::LEFT_SUPER; +		table[SDL_SCANCODE_RSHIFT] = Keycode::RIGHT_SHIFT; +		table[SDL_SCANCODE_RCTRL] = Keycode::RIGHT_CONTROL; +		table[SDL_SCANCODE_RALT] = Keycode::RIGHT_ALT; +		table[SDL_SCANCODE_RGUI] = Keycode::RIGHT_SUPER; +		table[SDL_SCANCODE_MENU] = Keycode::MENU; + +		return table; +	}(); + +	if (sdl_key < 0 || sdl_key >= SDL_NUM_SCANCODES) { +		return Keycode::NONE;  	} -	*/ + +	return LOOKUP_TABLE[sdl_key]; +} + +MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) { +	static const std::array<MouseButton, 5> MOUSE_BUTTON_LOOKUP_TABLE = [] { +		std::array<MouseButton, 5> table{}; +		table.fill(MouseButton::NONE); + +		table[SDL_BUTTON_LEFT] = MouseButton::LEFT_MOUSE; +		table[SDL_BUTTON_RIGHT] = MouseButton::RIGHT_MOUSE; +		table[SDL_BUTTON_MIDDLE] = MouseButton::MIDDLE_MOUSE; +		table[SDL_BUTTON_X1] = MouseButton::X1_MOUSE; +		table[SDL_BUTTON_X2] = MouseButton::X2_MOUSE; + +		return table; +	}(); + +	if (sdl_button >= MOUSE_BUTTON_LOOKUP_TABLE.size()) { +		// Return NONE for invalid or unmapped button +		return MouseButton::NONE; +	} + +	return MOUSE_BUTTON_LOOKUP_TABLE[sdl_button];  }  void SDLContext::clear_screen() { @@ -224,6 +342,66 @@ ivec2 SDLContext::get_size(const Texture & ctx) {  void SDLContext::delay(int ms) const { SDL_Delay(ms); } +std::vector<SDLContext::EventData> SDLContext::get_events() { +	std::vector<SDLContext::EventData> event_list; +	SDL_Event event; +	while (SDL_PollEvent(&event)) { +		switch (event.type) { +			case SDL_QUIT: +				event_list.push_back(EventData{ +					.event_type = SDLContext::EventType::SHUTDOWN, +				}); +				break; +			case SDL_KEYDOWN: +				event_list.push_back(EventData{ +					.event_type = SDLContext::EventType::KEYDOWN, +					.key = sdl_to_keycode(event.key.keysym.scancode), +					.key_repeat = (event.key.repeat != 0), +				}); +				break; +			case SDL_KEYUP: +				event_list.push_back(EventData{ +					.event_type = SDLContext::EventType::KEYUP, +					.key = sdl_to_keycode(event.key.keysym.scancode), +				}); +				break; +			case SDL_MOUSEBUTTONDOWN: +				event_list.push_back(EventData{ +					.event_type = SDLContext::EventType::MOUSEDOWN, +					.mouse_button = sdl_to_mousebutton(event.button.button), +					.mouse_position = {event.button.x, event.button.y}, +				}); +				break; +			case SDL_MOUSEBUTTONUP: { +				int x, y; +				SDL_GetMouseState(&x, &y); +				event_list.push_back(EventData{ +					.event_type = SDLContext::EventType::MOUSEUP, +					.mouse_button = sdl_to_mousebutton(event.button.button), +					.mouse_position = {event.button.x, event.button.y}, +				}); +			} break; + +			case SDL_MOUSEMOTION: { +				event_list.push_back( +					EventData{.event_type = SDLContext::EventType::MOUSEMOVE, +							  .mouse_position = {event.motion.x, event.motion.y}, +							  .rel_mouse_move = {event.motion.xrel, event.motion.yrel}}); +			} break; + +			case SDL_MOUSEWHEEL: { +				event_list.push_back(EventData{ +					.event_type = SDLContext::EventType::MOUSEWHEEL, +					.mouse_position = {event.motion.x, event.motion.y}, +					// TODO: why is this needed? +					.scroll_direction = event.wheel.y < 0 ? -1 : 1, +					.scroll_delta = event.wheel.preciseY, +				}); +			} break; +		} +	} +	return event_list; +}  void SDLContext::set_color_texture(const Texture & texture, const Color & color) {  	SDL_SetTextureColorMod(texture.texture.get(), color.r, color.g, color.b);  	SDL_SetTextureAlphaMod(texture.texture.get(), color.a); diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index e49ca78..a2b34c1 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -1,5 +1,6 @@  #pragma once +#include <SDL2/SDL.h>  #include <SDL2/SDL_keycode.h>  #include <SDL2/SDL_rect.h>  #include <SDL2/SDL_render.h> @@ -8,20 +9,21 @@  #include <functional>  #include <memory>  #include <string> +#include <utility> -#include "../api/Camera.h" -#include "../api/Sprite.h" - +#include "api/Camera.h"  #include "api/Color.h" +#include "api/Event.h" +#include "api/KeyCodes.h" +#include "api/Sprite.h"  #include "api/Texture.h" +#include "api/Transform.h"  #include "types.h"  namespace crepe { -// TODO: SDL_Keycode is defined in a header not distributed with crepe, which means this -// typedef is unusable when crepe is packaged. Wouter will fix this later. -typedef SDL_Keycode CREPE_KEYCODES; - +class LoopManager; +class InputSystem;  /**   * \class SDLContext   * \brief Facade for the SDL library @@ -41,6 +43,29 @@ public:  	};  public: +	//! EventType enum for passing eventType +	enum EventType { +		NONE = 0, +		MOUSEDOWN, +		MOUSEUP, +		MOUSEMOVE, +		MOUSEWHEEL, +		KEYUP, +		KEYDOWN, +		SHUTDOWN, + +	}; +	//! EventData struct for passing event data from facade +	struct EventData { +		SDLContext::EventType event_type = SDLContext::EventType::NONE; +		Keycode key = Keycode::NONE; +		bool key_repeat = false; +		MouseButton mouse_button = MouseButton::NONE; +		ivec2 mouse_position = {-1, -1}; +		int scroll_direction = -1; +		float scroll_delta = INFINITY; +		ivec2 rel_mouse_move = {-1, -1}; +	};  	/**  	 * \brief Gets the singleton instance of SDLContext.  	 * \return Reference to the SDLContext instance. @@ -53,13 +78,40 @@ public:  	SDLContext & operator=(SDLContext &&) = delete;  private: -	//! will only use handle_events -	friend class LoopManager; +	//! will only use get_events +	friend class InputSystem; +	/** +	 * \brief Retrieves a list of all events from the SDL context. +	 *  +	 * This method retrieves all the events from the SDL context that are currently +	 * available. It is primarily used by the InputSystem to process various +	 * input events such as mouse clicks, mouse movements, and keyboard presses. +	 *  +	 * \return Events that occurred since last call to `get_events()` +	 */ +	std::vector<SDLContext::EventData> get_events(); + +	/** +	 * \brief Converts an SDL key code to the custom Keycode type. +	 *  +	 * This method maps an SDL key code to the corresponding `Keycode` enum value, +	 * which is used internally by the system to identify the keys. +	 *  +	 * \param sdl_key The SDL key code to convert. +	 * \return The corresponding `Keycode` value or `Keycode::NONE` if the key is unrecognized. +	 */ +	Keycode sdl_to_keycode(SDL_Keycode sdl_key); +  	/** -	 * \brief Handles SDL events such as window close and input. -	 * \param running Reference to a boolean flag that controls the main loop. +	 * \brief Converts an SDL mouse button code to the custom MouseButton type. +	 *  +	 * This method maps an SDL mouse button code to the corresponding `MouseButton`  +	 * enum value, which is used internally by the system to identify mouse buttons. +	 *  +	 * \param sdl_button The SDL mouse button code to convert. +	 * \return The corresponding `MouseButton` value or `MouseButton::NONE` if the key is unrecognized  	 */ -	void handle_events(bool & running); +	MouseButton sdl_to_mousebutton(Uint8 sdl_button);  private:  	//! Will only use get_ticks diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt new file mode 100644 index 0000000..517b8a2 --- /dev/null +++ b/src/crepe/manager/CMakeLists.txt @@ -0,0 +1,20 @@ +target_sources(crepe PUBLIC +	ComponentManager.cpp +	EventManager.cpp +	Manager.cpp +	SaveManager.cpp +	SceneManager.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES +	ComponentManager.h +	ComponentManager.hpp +	EventManager.h +	EventManager.hpp +	Manager.h +	Mediator.h +	SaveManager.h +	SceneManager.h +	SceneManager.hpp +) + diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 5b73009..80cf8b4 100644 --- a/src/crepe/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,13 +1,16 @@ -#include "api/GameObject.h" -#include "util/Log.h" +#include "../api/GameObject.h" +#include "../types.h" +#include "../util/Log.h"  #include "ComponentManager.h" -#include "types.h"  using namespace crepe;  using namespace std; -ComponentManager::ComponentManager() { dbg_trace(); } +ComponentManager::ComponentManager(Mediator & mediator) : Manager(mediator) { +	mediator.component_manager = *this; +	dbg_trace(); +}  ComponentManager::~ComponentManager() { dbg_trace(); }  void ComponentManager::delete_all_components_of_id(game_object_id_t id) { diff --git a/src/crepe/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 480124f..ad37586 100644 --- a/src/crepe/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -5,8 +5,10 @@  #include <unordered_map>  #include <vector> -#include "Component.h" -#include "types.h" +#include "../Component.h" +#include "../types.h" + +#include "Manager.h"  namespace crepe { @@ -17,7 +19,7 @@ class GameObject;   *    * This class manages all components. It provides methods to add, delete and get components.   */ -class ComponentManager { +class ComponentManager : public Manager {  	// TODO: This relation should be removed! I (loek) believe that the scene manager should  	// create/destroy components because the GameObject's are stored in concrete Scene classes,  	// which will in turn call GameObject's destructor, which will in turn call @@ -26,7 +28,7 @@ class ComponentManager {  	friend class SceneManager;  public: -	ComponentManager(); // dbg_trace +	ComponentManager(Mediator & mediator);  	~ComponentManager(); // dbg_trace  	/** diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index ffb38ec..ffb38ec 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/manager/EventManager.cpp index 20f0dd3..20f0dd3 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/manager/EventManager.cpp diff --git a/src/crepe/api/EventManager.h b/src/crepe/manager/EventManager.h index 1a33023..d634f54 100644 --- a/src/crepe/api/EventManager.h +++ b/src/crepe/manager/EventManager.h @@ -5,8 +5,8 @@  #include <unordered_map>  #include <vector> -#include "Event.h" -#include "EventHandler.h" +#include "../api/Event.h" +#include "../api/EventHandler.h"  namespace crepe { diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/manager/EventManager.hpp index a5f4556..a5f4556 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/manager/EventManager.hpp diff --git a/src/crepe/manager/Manager.cpp b/src/crepe/manager/Manager.cpp new file mode 100644 index 0000000..1182785 --- /dev/null +++ b/src/crepe/manager/Manager.cpp @@ -0,0 +1,5 @@ +#include "Manager.h" + +using namespace crepe; + +Manager::Manager(Mediator & mediator) : mediator(mediator) {} diff --git a/src/crepe/manager/Manager.h b/src/crepe/manager/Manager.h new file mode 100644 index 0000000..84d80fe --- /dev/null +++ b/src/crepe/manager/Manager.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Mediator.h" + +namespace crepe { + +/** + * \brief Base manager class + * + * Managers are used for various tasks that fall outside the ECS system category. All managers + * are required to register themselves to the mediator passed to the constructor, and this + * mutable reference is saved for convenience, even though not all managers use the mediator + * directly. + */ +class Manager { +public: +	Manager(Mediator & mediator); +	virtual ~Manager() = default; + +protected: +	Mediator & mediator; +}; + +} // namespace crepe diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h new file mode 100644 index 0000000..71bd1c9 --- /dev/null +++ b/src/crepe/manager/Mediator.h @@ -0,0 +1,33 @@ +#pragma once + +#include "../util/OptionalRef.h" + +// TODO: remove these singletons: +#include "EventManager.h" +#include "SaveManager.h" + +namespace crepe { + +class ComponentManager; +class SceneManager; + +/** + * Struct to pass references to classes that would otherwise need to be singletons down to + * other classes within the engine hierarchy. Made to prevent constant changes to subclasses to + * pass specific references through dependency injection. All references on this struct + * *should* be explicitly checked for availability as this struct does not guarantee anything. + * + * \note Dereferencing members of this struct should be deferred. If you are a user of this + * class, keep a reference to this mediator instead of just picking references from it when you + * receive an instance. + * + * \warning This class should never be directly accessible from the API + */ +struct Mediator { +	OptionalRef<ComponentManager> component_manager; +	OptionalRef<SceneManager> scene_manager; +	OptionalRef<SaveManager> save_manager = SaveManager::get_instance(); +	OptionalRef<EventManager> event_manager = EventManager::get_instance(); +}; + +} // namespace crepe diff --git a/src/crepe/api/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index c5f43ea..d4ed1c1 100644 --- a/src/crepe/api/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,9 +1,9 @@ +#include "../ValueBroker.h" +#include "../api/Config.h"  #include "../facade/DB.h"  #include "../util/Log.h" -#include "Config.h"  #include "SaveManager.h" -#include "ValueBroker.h"  using namespace std;  using namespace crepe; diff --git a/src/crepe/api/SaveManager.h b/src/crepe/manager/SaveManager.h index 3d8c852..3d8c852 100644 --- a/src/crepe/api/SaveManager.h +++ b/src/crepe/manager/SaveManager.h diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp index 1f783ad..50a9fbb 100644 --- a/src/crepe/api/SceneManager.cpp +++ b/src/crepe/manager/SceneManager.cpp @@ -1,14 +1,15 @@  #include <algorithm>  #include <memory> -#include "../ComponentManager.h" - +#include "ComponentManager.h"  #include "SceneManager.h"  using namespace crepe;  using namespace std; -SceneManager::SceneManager(ComponentManager & mgr) : component_manager(mgr) {} +SceneManager::SceneManager(Mediator & mediator) : Manager(mediator) { +	mediator.scene_manager = *this; +}  void SceneManager::set_next_scene(const string & name) { next_scene = name; } @@ -26,7 +27,7 @@ void SceneManager::load_next_scene() {  	unique_ptr<Scene> & scene = *it;  	// Delete all components of the current scene -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	mgr.delete_all_components();  	// Load the new scene diff --git a/src/crepe/api/SceneManager.h b/src/crepe/manager/SceneManager.h index f6f62cd..e0955c2 100644 --- a/src/crepe/api/SceneManager.h +++ b/src/crepe/manager/SceneManager.h @@ -3,7 +3,9 @@  #include <memory>  #include <vector> -#include "Scene.h" +#include "../api/Scene.h" + +#include "Manager.h"  namespace crepe { @@ -15,10 +17,9 @@ class ComponentManager;   * This class manages scenes. It can add new scenes and load them. It also manages the current scene   * and the next scene.   */ -class SceneManager { +class SceneManager : public Manager {  public: -	//! \param mgr  Reference to the ComponentManager -	SceneManager(ComponentManager & mgr); +	SceneManager(Mediator & mediator);  public:  	/** @@ -44,8 +45,6 @@ private:  	std::vector<std::unique_ptr<Scene>> scenes;  	//! Next scene to load  	std::string next_scene; -	//! Reference to the ComponentManager -	ComponentManager & component_manager;  };  } // namespace crepe diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/manager/SceneManager.hpp index 5c8e417..dff4e51 100644 --- a/src/crepe/api/SceneManager.hpp +++ b/src/crepe/manager/SceneManager.hpp @@ -12,7 +12,7 @@ void SceneManager::add_scene(Args &&... args) {  	Scene * scene = new T(std::forward<Args>(args)...);  	unique_ptr<Scene> unique_scene(scene); -	unique_scene->component_manager = this->component_manager; +	unique_scene->mediator = this->mediator;  	this->scenes.emplace_back(std::move(unique_scene)); diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index 4c40940..8bb6465 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -1,15 +1,15 @@  #include <cstdint> -#include "api/Animator.h" -#include "facade/SDLContext.h" +#include "../api/Animator.h" +#include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h"  #include "AnimatorSystem.h" -#include "ComponentManager.h"  using namespace crepe;  void AnimatorSystem::update() { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	RefVector<Animator> animations = mgr.get_components_by_type<Animator>(); diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index ca89d4d..7de5198 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources(crepe PUBLIC  	CollisionSystem.cpp  	RenderSystem.cpp  	AnimatorSystem.cpp +	InputSystem.cpp  	AISystem.cpp  ) @@ -16,5 +17,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	CollisionSystem.h  	RenderSystem.h  	AnimatorSystem.h +	InputSystem.h  	AISystem.h  ) diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp new file mode 100644 index 0000000..7cc8d30 --- /dev/null +++ b/src/crepe/system/InputSystem.cpp @@ -0,0 +1,187 @@ +#include "../api/Button.h" +#include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" + +#include "InputSystem.h" + +using namespace crepe; + +void InputSystem::update() { +	ComponentManager & mgr = this->mediator.component_manager; +	EventManager & event_mgr = this->mediator.event_manager; +	std::vector<SDLContext::EventData> event_list = SDLContext::get_instance().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; +	// Find the active camera +	for (Camera & cam : cameras) { +		if (!cam.active) continue; +		curr_cam_ref = cam; +		break; +	} +	if (!curr_cam_ref) return; +	Camera & current_cam = curr_cam_ref; +	RefVector<Transform> transform_vec +		= mgr.get_components_by_id<Transform>(current_cam.game_object_id); +	Transform & cam_transform = transform_vec.front().get(); +	int camera_origin_x +		= cam_transform.position.x + current_cam.offset.x - (current_cam.viewport_size.x / 2); +	int camera_origin_y +		= cam_transform.position.y + current_cam.offset.y - (current_cam.viewport_size.y / 2); + +	for (const SDLContext::EventData & event : event_list) { +		int world_mouse_x = event.mouse_position.x + camera_origin_x; +		int world_mouse_y = event.mouse_position.y + camera_origin_y; +		// check if the mouse is within the viewport +		bool mouse_in_viewport +			= !(world_mouse_x < camera_origin_x +				|| world_mouse_x > camera_origin_x + current_cam.viewport_size.x +				|| world_mouse_y < camera_origin_y +				|| world_mouse_y > camera_origin_y + current_cam.viewport_size.y); + +		switch (event.event_type) { +			case SDLContext::EventType::KEYDOWN: +				event_mgr.queue_event<KeyPressEvent>(KeyPressEvent{ +					.repeat = event.key_repeat, +					.key = event.key, +				}); +				break; +			case SDLContext::EventType::KEYUP: +				event_mgr.queue_event<KeyReleaseEvent>(KeyReleaseEvent{ +					.key = event.key, +				}); +				break; +			case SDLContext::EventType::MOUSEDOWN: +				if (!mouse_in_viewport) { +					break; +				} +				event_mgr.queue_event<MousePressEvent>(MousePressEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.button = event.mouse_button, +				}); +				this->last_mouse_down_position = {world_mouse_x, world_mouse_y}; +				this->last_mouse_button = event.mouse_button; +				break; +			case SDLContext::EventType::MOUSEUP: { +				if (!mouse_in_viewport) { +					break; +				} +				event_mgr.queue_event<MouseReleaseEvent>(MouseReleaseEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.button = event.mouse_button, +				}); +				//check if its a click by checking the last button down +				int delta_x = world_mouse_x - this->last_mouse_down_position.x; +				int delta_y = world_mouse_y - this->last_mouse_down_position.y; + +				if (this->last_mouse_button == event.mouse_button +					&& std::abs(delta_x) <= click_tolerance +					&& std::abs(delta_y) <= click_tolerance) { +					event_mgr.queue_event<MouseClickEvent>(MouseClickEvent{ +						.mouse_x = world_mouse_x, +						.mouse_y = world_mouse_y, +						.button = event.mouse_button, +					}); + +					this->handle_click(event.mouse_button, world_mouse_x, world_mouse_y); +				} +			} break; +			case SDLContext::EventType::MOUSEMOVE: +				if (!mouse_in_viewport) { +					break; +				} +				event_mgr.queue_event<MouseMoveEvent>(MouseMoveEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.delta_x = event.rel_mouse_move.x, +					.delta_y = event.rel_mouse_move.y, +				}); +				this->handle_move(event, world_mouse_x, world_mouse_y); +				break; +			case SDLContext::EventType::MOUSEWHEEL: +				event_mgr.queue_event<MouseScrollEvent>(MouseScrollEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.scroll_direction = event.scroll_direction, +					.scroll_delta = event.scroll_delta, +				}); +				break; +			case SDLContext::EventType::SHUTDOWN: +				event_mgr.queue_event<ShutDownEvent>(ShutDownEvent{}); +				break; +			default: +				break; +		} +	} +} +void InputSystem::handle_move(const SDLContext::EventData & event_data, +							  const int world_mouse_x, const int world_mouse_y) { +	ComponentManager & mgr = this->mediator.component_manager; + +	RefVector<Button> buttons = mgr.get_components_by_type<Button>(); + +	for (Button & button : buttons) { +		RefVector<Transform> transform_vec +			= mgr.get_components_by_id<Transform>(button.game_object_id); +		Transform & transform(transform_vec.front().get()); + +		bool was_hovering = button.hover; +		if (button.active +			&& this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { +			button.hover = true; +			if (!was_hovering && button.on_mouse_enter) { +				button.on_mouse_enter(); +			} +		} else { +			button.hover = false; +			// Trigger the on_exit callback if the hover state just changed to false +			if (was_hovering && button.on_mouse_exit) { +				button.on_mouse_exit(); +			} +		} +	} +} + +void InputSystem::handle_click(const MouseButton & mouse_button, const int world_mouse_x, +							   const int world_mouse_y) { +	ComponentManager & mgr = this->mediator.component_manager; + +	RefVector<Button> buttons = mgr.get_components_by_type<Button>(); + +	for (Button & button : buttons) { +		RefVector<Transform> transform_vec +			= mgr.get_components_by_id<Transform>(button.game_object_id); +		Transform & transform = transform_vec.front().get(); + +		if (button.active +			&& this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { +			this->handle_button_press(button); +		} +	} +} + +bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, +										 const Button & button, const Transform & transform) { +	int actual_x = transform.position.x + button.offset.x; +	int actual_y = transform.position.y + button.offset.y; + +	int half_width = button.dimensions.x / 2; +	int half_height = button.dimensions.y / 2; + +	// Check if the mouse is within the button's boundaries +	return mouse_x >= actual_x - half_width && mouse_x <= actual_x + half_width +		   && mouse_y >= actual_y - half_height && mouse_y <= actual_y + half_height; +} + +void InputSystem::handle_button_press(Button & button) { +	if (button.is_toggle) { +		if (!button.is_pressed && button.on_click) { +			button.on_click(); +		} +		button.is_pressed = !button.is_pressed; +	} else if (button.on_click) { +		button.on_click(); +	} +} diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h new file mode 100644 index 0000000..87e86f8 --- /dev/null +++ b/src/crepe/system/InputSystem.h @@ -0,0 +1,85 @@ +#pragma once + +#include "../facade/SDLContext.h" +#include "../types.h" +#include "../util/OptionalRef.h" + +#include "System.h" + +namespace crepe { + +class Camera; +class Button; +class Transform; + +/** + * \brief Handles the processing of input events created by SDLContext + * + * This system processes events such as mouse clicks, mouse movement, and keyboard + * actions. It is responsible for detecting interactions with UI buttons and + * passing the corresponding events to the registered listeners. + */ +class InputSystem : public System { +public: +	using System::System; + +	/** +	 * \brief Updates the system, processing all input events. +	 * This method processes all events and triggers corresponding actions. +	 */ +	void update() override; + +private: +	//! Stores the last position of the mouse when the button was pressed. +	ivec2 last_mouse_down_position; +	// TODO: specify world/hud space and make regular `vec2` + +	//! Stores the last mouse button pressed. +	MouseButton last_mouse_button = MouseButton::NONE; + +	//! The maximum allowable distance between mouse down and mouse up to register as a click. +	const int click_tolerance = 5; + +	/** +	* \brief Handles the mouse click event. +	* \param mouse_button The mouse button involved in the click. +	* \param world_mouse_x The X coordinate of the mouse in world space. +	* \param world_mouse_y The Y coordinate of the mouse in world space. +	* +	* This method processes the mouse click event and triggers the corresponding button action. +	*/ +	void handle_click(const MouseButton & mouse_button, const int world_mouse_x, +					  const int world_mouse_y); + +	/** +	* \brief Handles the mouse movement event. +	* \param event_data The event data containing information about the mouse movement. +	* \param world_mouse_x The X coordinate of the mouse in world space. +	* \param world_mouse_y The Y coordinate of the mouse in world space. +	* +	* This method processes the mouse movement event and updates the button hover state. +	*/ +	void handle_move(const SDLContext::EventData & event_data, const int world_mouse_x, +					 const int world_mouse_y); + +	/** +	* \brief Checks if the mouse position is inside the bounds of the button. +	* \param world_mouse_x The X coordinate of the mouse in world space. +	* \param world_mouse_y The Y coordinate of the mouse in world space. +	* \param button The button to check. +	* \param transform The transform component of the button. +	* \return True if the mouse is inside the button, false otherwise. +	*/ +	bool is_mouse_inside_button(const int world_mouse_x, const int world_mouse_y, +								const Button & button, const Transform & transform); + +	/** +	* \brief Handles the button press event, calling the on_click callback if necessary. +	* \param button The button being pressed. +	* +	* This method triggers the on_click action for the button when it is pressed. +	*/ +	void handle_button_press(Button & button); +}; + +} // namespace crepe diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index 0e62a57..b14c52f 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -2,17 +2,17 @@  #include <cstdlib>  #include <ctime> -#include "api/ParticleEmitter.h" -#include "api/Transform.h" +#include "../api/ParticleEmitter.h" +#include "../api/Transform.h" +#include "../manager/ComponentManager.h" -#include "ComponentManager.h"  #include "ParticleSystem.h"  using namespace crepe;  void ParticleSystem::update() {  	// Get all emitters -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>();  	for (ParticleEmitter & emitter : emitters) { diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index 514a4b3..bebcf3d 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -1,17 +1,17 @@  #include <cmath> -#include "../ComponentManager.h"  #include "../api/Config.h"  #include "../api/Rigidbody.h"  #include "../api/Transform.h"  #include "../api/Vector2.h" +#include "../manager/ComponentManager.h"  #include "PhysicsSystem.h"  using namespace crepe;  void PhysicsSystem::update() { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_type<Rigidbody>();  	RefVector<Transform> transforms = mgr.get_components_by_type<Transform>(); diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 11c9669..92dba43 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -5,12 +5,12 @@  #include <stdexcept>  #include <vector> -#include "../ComponentManager.h"  #include "../api/Camera.h"  #include "../api/ParticleEmitter.h"  #include "../api/Sprite.h"  #include "../api/Transform.h"  #include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h"  #include "RenderSystem.h" @@ -22,7 +22,7 @@ void RenderSystem::clear_screen() { this->context.clear_screen(); }  void RenderSystem::present_screen() { this->context.present_screen(); }  const Camera & RenderSystem::update_camera() { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); @@ -62,7 +62,7 @@ void RenderSystem::update() {  bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam,  								   const double & scale) { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	vector<reference_wrapper<ParticleEmitter>> emitters  		= mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id); @@ -102,7 +102,7 @@ void RenderSystem::render_normal(const Sprite & sprite, const Camera & cam,  }  void RenderSystem::render() { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	const Camera & cam = this->update_camera();  	RefVector<Sprite> sprites = mgr.get_components_by_type<Sprite>(); diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp index 20a83f7..d6b2ca1 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -1,6 +1,6 @@ -#include "../ComponentManager.h"  #include "../api/BehaviorScript.h"  #include "../api/Script.h" +#include "../manager/ComponentManager.h"  #include "ScriptSystem.h" @@ -10,7 +10,7 @@ using namespace crepe;  void ScriptSystem::update() {  	dbg_trace(); -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>();  	for (BehaviorScript & behavior_script : behavior_scripts) { diff --git a/src/crepe/system/System.cpp b/src/crepe/system/System.cpp index 937a423..f68549b 100644 --- a/src/crepe/system/System.cpp +++ b/src/crepe/system/System.cpp @@ -4,4 +4,4 @@  using namespace crepe; -System::System(ComponentManager & mgr) : component_manager(mgr) { dbg_trace(); } +System::System(const Mediator & mediator) : mediator(mediator) { dbg_trace(); } diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h index 28ea20e..063dfbf 100644 --- a/src/crepe/system/System.h +++ b/src/crepe/system/System.h @@ -1,5 +1,7 @@  #pragma once +#include "../manager/Mediator.h" +  namespace crepe {  class ComponentManager; @@ -7,9 +9,8 @@ class ComponentManager;  /**   * \brief Base ECS system class   * - * This class is used as the base for all system classes. Classes derived from - * System must implement the System::update() method and copy Script::Script - * with the `using`-syntax. + * This class is used as the base for all system classes. Classes derived from System must + * implement the System::update() method and copy Script::Script with the `using`-syntax.   */  class System {  public: @@ -19,11 +20,11 @@ public:  	virtual void update() = 0;  public: -	System(ComponentManager &); +	System(const Mediator & m);  	virtual ~System() = default;  protected: -	ComponentManager & component_manager; +	const Mediator & mediator;  };  } // namespace crepe diff --git a/src/doc/features.dox b/src/doc/features.dox index 96b7c6c..21a040a 100644 --- a/src/doc/features.dox +++ b/src/doc/features.dox @@ -17,12 +17,12 @@ feature.  \par Features  - Scripting -	- \ref feature_script <br>\copybrief feature_script +	- \ref feature_script \n\copybrief feature_script  - Game flow management -	- \ref feature_scene <br>\copybrief feature_scene +	- \ref feature_scene \n\copybrief feature_scene  - Entity -	- \ref feature_gameobject <br>\copybrief feature_gameobject +	- \ref feature_gameobject \n\copybrief feature_gameobject  */ diff --git a/src/doc/index.dox b/src/doc/index.dox index 5ec7889..7796f34 100644 --- a/src/doc/index.dox +++ b/src/doc/index.dox @@ -8,3 +8,10 @@ Welcome to the documentation for the crêpe game engine.  \see feature  */ + +/** + +\namespace crepe +\brief Engine namespace + +*/ diff --git a/src/doc/layout.xml b/src/doc/layout.xml index fb4cc0c..6336655 100644 --- a/src/doc/layout.xml +++ b/src/doc/layout.xml @@ -11,7 +11,7 @@  			<tab type="modulelist" visible="yes" title="" intro=""/>  			<tab type="modulemembers" visible="yes" title="" intro=""/>  		</tab> -		<tab type="namespaces" visible="no" title=""> +		<tab type="namespaces" visible="yes" title="">  			<tab type="namespacelist" visible="yes" title="" intro=""/>  			<tab type="namespacemembers" visible="yes" title="" intro=""/>  		</tab> @@ -56,10 +56,10 @@  			<interfaces title=""/>  			<publicslots title=""/>  			<signals title=""/> -			<publicmethods title=""/> -			<publicstaticmethods title=""/>  			<publicattributes title=""/>  			<publicstaticattributes title=""/> +			<publicmethods title=""/> +			<publicstaticmethods title=""/>  			<protectedtypes title=""/>  			<protectedslots title=""/>  			<protectedmethods title=""/> @@ -102,11 +102,12 @@  	</class>  	<namespace>  		<briefdescription visible="yes"/> +		<detaileddescription title=""/>  		<memberdecl>  			<nestednamespaces visible="yes" title=""/>  			<constantgroups visible="yes" title=""/>  			<interfaces visible="yes" title=""/> -			<classes visible="yes" title=""/> +			<classes visible="no" title=""/>  			<concepts visible="yes" title=""/>  			<structs visible="yes" title=""/>  			<exceptions visible="yes" title=""/> @@ -119,7 +120,6 @@  			<properties title=""/>  			<membergroups visible="yes"/>  		</memberdecl> -		<detaileddescription title=""/>  		<memberdef>  			<inlineclasses title=""/>  			<typedefs title=""/> diff --git a/src/doc/style.css b/src/doc/style.css index daabd39..c12240c 100644 --- a/src/doc/style.css +++ b/src/doc/style.css @@ -1,6 +1,6 @@  #titlearea, -address { -	display: none; -} +address, +a[href="namespaces.html"] +{ display: none; }  h2.groupheader { margin-top: revert; } diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index ef770ae..0158d67 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -19,5 +19,5 @@ endfunction()  add_example(asset_manager)  add_example(savemgr)  add_example(rendering_particle) -add_example(gameloop) +add_example(button)  add_example(AITest) diff --git a/src/example/button.cpp b/src/example/button.cpp new file mode 100644 index 0000000..00bdc28 --- /dev/null +++ b/src/example/button.cpp @@ -0,0 +1,54 @@ +#include <SDL2/SDL_timer.h> +#include <chrono> +#include <crepe/Component.h> +#include <crepe/ComponentManager.h> +#include <crepe/api/Animator.h> +#include <crepe/api/Button.h> +#include <crepe/api/Camera.h> +#include <crepe/api/Color.h> +#include <crepe/api/EventManager.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Sprite.h> +#include <crepe/api/Texture.h> +#include <crepe/api/Transform.h> +#include <crepe/system/AnimatorSystem.h> +#include <crepe/system/InputSystem.h> +#include <crepe/system/RenderSystem.h> +#include <crepe/types.h> +#include <iostream> +using namespace crepe; +using namespace std; + +int main(int argc, char * argv[]) { +	ComponentManager mgr; +	RenderSystem sys{mgr}; +	EventManager & event_mgr = EventManager::get_instance(); +	InputSystem input_sys{mgr}; +	AnimatorSystem asys{mgr}; +	GameObject camera_obj = mgr.new_object("", "", vec2{1000, 1000}, 0, 1); +	camera_obj.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{2000, 2000}, 1.0f); + +	GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); +	auto s2 = Texture("asset/texture/test_ap43.png"); +	bool button_clicked = false; +	auto & sprite2 = button_obj.add_component<Sprite>( +		s2, Color::GREEN, Sprite::FlipSettings{false, false}, 2, 1, 100); +	std::function<void()> on_click = [&]() { std::cout << "button clicked" << std::endl; }; +	std::function<void()> on_enter = [&]() { std::cout << "enter" << std::endl; }; +	std::function<void()> on_exit = [&]() { std::cout << "exit" << std::endl; }; +	auto & button +		= button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click, false); +	button.on_mouse_enter = on_enter; +	button.on_mouse_exit = on_exit; +	button.is_toggle = true; +	button.active = true; +	auto start = std::chrono::steady_clock::now(); +	while (true) { +		input_sys.update(); +		sys.update(); +		asys.update(); +		event_mgr.dispatch_events(); +		SDL_Delay(30); +	} +	return 0; +} diff --git a/src/example/gameloop.cpp b/src/example/gameloop.cpp deleted file mode 100644 index a676f20..0000000 --- a/src/example/gameloop.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "crepe/api/LoopManager.h" -using namespace crepe; -int main() { -	LoopManager gameloop; -	gameloop.start(); -	return 1; -} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index d310f6a..e19d7de 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -12,4 +12,7 @@ target_sources(test_main PUBLIC  	ValueBrokerTest.cpp  	DBTest.cpp  	Vector2Test.cpp +	InputTest.cpp +	ScriptEventTest.cpp +	ScriptSceneTest.cpp  ) diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp index e80814c..99dedff 100644 --- a/src/test/DBTest.cpp +++ b/src/test/DBTest.cpp @@ -1,6 +1,7 @@ -#include <crepe/facade/DB.h>  #include <gtest/gtest.h> +#include <crepe/facade/DB.h> +  using namespace std;  using namespace crepe;  using namespace testing; diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index af9d6f2..3e6c61c 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -2,18 +2,20 @@  #define protected public -#include <crepe/ComponentManager.h>  #include <crepe/api/GameObject.h>  #include <crepe/api/Metadata.h>  #include <crepe/api/Transform.h>  #include <crepe/api/Vector2.h> +#include <crepe/manager/ComponentManager.h>  using namespace std;  using namespace crepe;  class ECSTest : public ::testing::Test { +	Mediator m; +  public: -	ComponentManager mgr{}; +	ComponentManager mgr{m};  };  TEST_F(ECSTest, createGameObject) { diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index b0e6c9c..4a4872d 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,10 +1,11 @@ - -#include "api/Event.h" -#include "api/EventManager.h" -#include "api/IKeyListener.h" -#include "api/IMouseListener.h"  #include <gmock/gmock.h>  #include <gtest/gtest.h> + +#include <crepe/api/Event.h> +#include <crepe/api/IKeyListener.h> +#include <crepe/api/IMouseListener.h> +#include <crepe/manager/EventManager.h> +  using namespace std;  using namespace std::chrono_literals;  using namespace crepe; @@ -36,10 +37,7 @@ public:  };  TEST_F(EventManagerTest, EventSubscription) { -	EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) { -		std::cout << "Key Event Triggered" << std::endl; -		return true; -	}; +	EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) { return true; };  	// Subscribe to KeyPressEvent  	EventManager::get_instance().subscribe<KeyPressEvent>(key_handler, 1); @@ -158,29 +156,37 @@ TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) {  	bool triggered1 = false;  	bool triggered2 = false;  	int test_channel = 1; -	EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) { + +	// Adjusted to use KeyPressEvent with repeat as the first variable +	EventHandler<KeyPressEvent> key_handler1 = [&](const KeyPressEvent & e) {  		triggered1 = true; -		EXPECT_EQ(e.mouse_x, 100); -		EXPECT_EQ(e.mouse_y, 200); -		EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); +		EXPECT_EQ(e.repeat, false); // Expecting repeat to be false +		EXPECT_EQ(e.key, Keycode::A); // Adjust expected key code  		return false; // Allows propagation  	}; -	EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) { + +	EventHandler<KeyPressEvent> key_handler2 = [&](const KeyPressEvent & e) {  		triggered2 = true; -		EXPECT_EQ(e.mouse_x, 100); -		EXPECT_EQ(e.mouse_y, 200); -		EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); +		EXPECT_EQ(e.repeat, false); // Expecting repeat to be false +		EXPECT_EQ(e.key, Keycode::A); // Adjust expected key code  		return false; // Allows propagation  	}; -	event_manager.subscribe<MouseClickEvent>(mouse_handler1); -	event_manager.subscribe<MouseClickEvent>(mouse_handler2, test_channel); -	event_manager.queue_event<MouseClickEvent>( -		MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); -	event_manager.queue_event<MouseClickEvent>( -		MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, +	// Subscribe handlers to KeyPressEvent +	event_manager.subscribe<KeyPressEvent>(key_handler1); +	event_manager.subscribe<KeyPressEvent>(key_handler2, test_channel); + +	// Queue a KeyPressEvent instead of KeyDownEvent +	event_manager.queue_event<KeyPressEvent>(KeyPressEvent{ +		.repeat = false, .key = Keycode::A}); // Adjust event with repeat flag first + +	event_manager.queue_event<KeyPressEvent>( +		KeyPressEvent{.repeat = false, +					  .key = Keycode::A}, // Adjust event for second subscription  		test_channel); +  	event_manager.dispatch_events(); +  	EXPECT_TRUE(triggered1);  	EXPECT_TRUE(triggered2);  } diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp new file mode 100644 index 0000000..cb9833f --- /dev/null +++ b/src/test/InputTest.cpp @@ -0,0 +1,293 @@ +#include <gtest/gtest.h> +#define protected public +#define private public +#include "api/KeyCodes.h" +#include "manager/ComponentManager.h" +#include "manager/EventManager.h" +#include "manager/Mediator.h" +#include "system/InputSystem.h" +#include <SDL2/SDL.h> +#include <SDL2/SDL_keycode.h> +#include <crepe/api/Button.h> +#include <crepe/api/Camera.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Metadata.h> +#include <crepe/api/Transform.h> +#include <crepe/api/Vector2.h> +#include <gmock/gmock.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; + +class InputTest : public ::testing::Test { +public: +	Mediator mediator; +	ComponentManager mgr{mediator}; + +	InputSystem input_system{mediator}; + +	EventManager & event_manager = EventManager::get_instance(); +	//GameObject camera; + +protected: +	void SetUp() override { +		mediator.event_manager = event_manager; +		mediator.component_manager = mgr; +		event_manager.clear(); +	} + +	void simulate_mouse_click(int mouse_x, int mouse_y, Uint8 mouse_button) { +		SDL_Event event; + +		// Simulate Mouse Button Down event +		SDL_zero(event); +		event.type = SDL_MOUSEBUTTONDOWN; +		event.button.x = mouse_x; +		event.button.y = mouse_y; +		event.button.button = mouse_button; +		SDL_PushEvent(&event); + +		// Simulate Mouse Button Up event +		SDL_zero(event); +		event.type = SDL_MOUSEBUTTONUP; +		event.button.x = mouse_x; +		event.button.y = mouse_y; +		event.button.button = mouse_button; +		SDL_PushEvent(&event); +	} +}; + +TEST_F(InputTest, MouseDown) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	bool mouse_triggered = false; +	EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent & event) { +		mouse_triggered = true; +		//middle of the screen = 0,0 +		EXPECT_EQ(event.mouse_x, 0); +		EXPECT_EQ(event.mouse_y, 0); +		EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); +		return false; +	}; +	event_manager.subscribe<MousePressEvent>(on_mouse_down); + +	SDL_Event event; +	SDL_zero(event); +	event.type = SDL_MOUSEBUTTONDOWN; +	// middle of the screen of a 500*500 camera = 250*250 +	event.button.x = 250; +	event.button.y = 250; +	event.button.button = SDL_BUTTON_LEFT; +	SDL_PushEvent(&event); + +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(mouse_triggered); +} + +TEST_F(InputTest, MouseUp) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	bool function_triggered = false; +	EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent & e) { +		function_triggered = true; +		EXPECT_EQ(e.mouse_x, 0); +		EXPECT_EQ(e.mouse_y, 0); +		EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); +		return false; +	}; +	event_manager.subscribe<MouseReleaseEvent>(on_mouse_release); + +	SDL_Event event; +	SDL_zero(event); +	event.type = SDL_MOUSEBUTTONUP; +	event.button.x = 250; +	event.button.y = 250; +	event.button.button = SDL_BUTTON_LEFT; +	SDL_PushEvent(&event); + +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(function_triggered); +} + +TEST_F(InputTest, MouseMove) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	bool function_triggered = false; +	EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent & e) { +		function_triggered = true; +		EXPECT_EQ(e.mouse_x, 0); +		EXPECT_EQ(e.mouse_y, 0); +		EXPECT_EQ(e.delta_x, 10); +		EXPECT_EQ(e.delta_y, 10); +		return false; +	}; +	event_manager.subscribe<MouseMoveEvent>(on_mouse_move); + +	SDL_Event event; +	SDL_zero(event); +	event.type = SDL_MOUSEMOTION; +	event.motion.x = 250; +	event.motion.y = 250; +	event.motion.xrel = 10; +	event.motion.yrel = 10; +	SDL_PushEvent(&event); + +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(function_triggered); +} + +TEST_F(InputTest, KeyDown) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	bool function_triggered = false; + +	// Define event handler for KeyPressEvent +	EventHandler<KeyPressEvent> on_key_press = [&](const KeyPressEvent & event) { +		function_triggered = true; +		EXPECT_EQ(event.key, Keycode::B); // Validate the key is 'B' +		EXPECT_EQ(event.repeat, true); // Validate repeat flag +		return false; +	}; + +	event_manager.subscribe<KeyPressEvent>(on_key_press); + +	// Simulate SDL_KEYDOWN event +	SDL_Event test_event; +	SDL_zero(test_event); +	test_event.type = SDL_KEYDOWN; // Key down event +	test_event.key.keysym.scancode = SDL_SCANCODE_B; // Set scancode for 'B' +	test_event.key.repeat = 1; // Set repeat flag +	SDL_PushEvent(&test_event); + +	input_system.update(); // Process the event +	event_manager.dispatch_events(); // Dispatch events to handlers + +	EXPECT_TRUE(function_triggered); // Check if the handler was triggered +} + +TEST_F(InputTest, KeyUp) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	bool function_triggered = false; +	EventHandler<KeyReleaseEvent> on_key_release = [&](const KeyReleaseEvent & event) { +		function_triggered = true; +		EXPECT_EQ(event.key, Keycode::B); +		return false; +	}; +	event_manager.subscribe<KeyReleaseEvent>(on_key_release); + +	SDL_Event event; +	SDL_zero(event); +	event.type = SDL_KEYUP; +	event.key.keysym.scancode = SDL_SCANCODE_B; +	SDL_PushEvent(&event); + +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(function_triggered); +} + +TEST_F(InputTest, MouseClick) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	bool on_click_triggered = false; +	EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent & event) { +		on_click_triggered = true; +		EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); +		EXPECT_EQ(event.mouse_x, 0); +		EXPECT_EQ(event.mouse_y, 0); +		return false; +	}; +	event_manager.subscribe<MouseClickEvent>(on_mouse_click); + +	this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(on_click_triggered); +} + +TEST_F(InputTest, testButtonClick) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	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, false); + +	bool hover = false; +	button.active = true; + +	button.is_pressed = false; +	button.is_toggle = false; +	this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_FALSE(button_clicked); + +	this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(button_clicked); +} + +TEST_F(InputTest, testButtonHover) { +	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); +	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, +											  vec2{0, 0}); +	camera.active = true; +	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, false); +	button.active = true; +	button.is_pressed = false; +	button.is_toggle = false; + +	// 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); +	hover_event.type = SDL_MOUSEMOTION; +	hover_event.motion.x = 250; +	hover_event.motion.y = 250; +	hover_event.motion.xrel = 10; +	hover_event.motion.yrel = 10; +	SDL_PushEvent(&hover_event); + +	input_system.update(); +	event_manager.dispatch_events(); +	EXPECT_TRUE(button.hover); +} diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index 976f9a1..a659fe5 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -1,12 +1,12 @@ -#include "api/Texture.h" -#include <crepe/ComponentManager.h>  #include <crepe/Particle.h>  #include <crepe/api/Config.h>  #include <crepe/api/GameObject.h>  #include <crepe/api/ParticleEmitter.h>  #include <crepe/api/Rigidbody.h>  #include <crepe/api/Sprite.h> +#include <crepe/api/Texture.h>  #include <crepe/api/Transform.h> +#include <crepe/manager/ComponentManager.h>  #include <crepe/system/ParticleSystem.h>  #include <gtest/gtest.h>  #include <math.h> @@ -16,9 +16,11 @@ using namespace std::chrono_literals;  using namespace crepe;  class ParticlesTest : public ::testing::Test { +	Mediator m; +  public: -	ComponentManager component_manager; -	ParticleSystem particle_system{component_manager}; +	ComponentManager component_manager{m}; +	ParticleSystem particle_system{m};  	void SetUp() override {  		ComponentManager & mgr = this->component_manager; diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 33b6020..43af8e4 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -1,8 +1,8 @@ -#include <crepe/ComponentManager.h>  #include <crepe/api/Config.h>  #include <crepe/api/GameObject.h>  #include <crepe/api/Rigidbody.h>  #include <crepe/api/Transform.h> +#include <crepe/manager/ComponentManager.h>  #include <crepe/system/PhysicsSystem.h>  #include <gtest/gtest.h> @@ -11,9 +11,11 @@ using namespace std::chrono_literals;  using namespace crepe;  class PhysicsTest : public ::testing::Test { +	Mediator m; +  public: -	ComponentManager component_manager; -	PhysicsSystem system{component_manager}; +	ComponentManager component_manager{m}; +	PhysicsSystem system{m};  	void SetUp() override {  		ComponentManager & mgr = this->component_manager; diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp index bb5b81a..c105dcb 100644 --- a/src/test/RenderSystemTest.cpp +++ b/src/test/RenderSystemTest.cpp @@ -1,4 +1,3 @@ -#include "types.h"  #include <functional>  #include <gtest/gtest.h>  #include <memory> @@ -7,12 +6,12 @@  #define private public  #define protected public -#include "crepe/api/Camera.h" -#include <crepe/ComponentManager.h> +#include <crepe/api/Camera.h>  #include <crepe/api/Color.h>  #include <crepe/api/GameObject.h>  #include <crepe/api/Sprite.h>  #include <crepe/api/Texture.h> +#include <crepe/manager/ComponentManager.h>  #include <crepe/system/RenderSystem.h> @@ -21,9 +20,11 @@ using namespace crepe;  using namespace testing;  class RenderSystemTest : public Test { +	Mediator m; +  public: -	ComponentManager mgr{}; -	RenderSystem sys{mgr}; +	ComponentManager mgr{m}; +	RenderSystem sys{m};  	GameObject entity1 = this->mgr.new_object("name");  	GameObject entity2 = this->mgr.new_object("name");  	GameObject entity3 = this->mgr.new_object("name"); diff --git a/src/test/SceneManagerTest.cpp b/src/test/SceneManagerTest.cpp index 62b7d33..9bb260c 100644 --- a/src/test/SceneManagerTest.cpp +++ b/src/test/SceneManagerTest.cpp @@ -1,12 +1,13 @@ -#include "types.h" -#include <crepe/ComponentManager.h> +#include <gtest/gtest.h> +  #include <crepe/api/GameObject.h>  #include <crepe/api/Metadata.h>  #include <crepe/api/Scene.h> -#include <crepe/api/SceneManager.h>  #include <crepe/api/Transform.h>  #include <crepe/api/Vector2.h> -#include <gtest/gtest.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/SceneManager.h> +#include <crepe/types.h>  using namespace std;  using namespace crepe; @@ -14,7 +15,8 @@ using namespace crepe;  class ConcreteScene1 : public Scene {  public:  	void load_scene() { -		ComponentManager & mgr = this->component_manager; +		Mediator & mediator = this->mediator; +		ComponentManager & mgr = mediator.component_manager;  		GameObject object1 = mgr.new_object("scene_1", "tag_scene_1", vec2{0, 0}, 0, 1);  		GameObject object2 = mgr.new_object("scene_1", "tag_scene_1", vec2{1, 0}, 0, 1);  		GameObject object3 = mgr.new_object("scene_1", "tag_scene_1", vec2{2, 0}, 0, 1); @@ -26,7 +28,8 @@ public:  class ConcreteScene2 : public Scene {  public:  	void load_scene() { -		ComponentManager & mgr = this->component_manager; +		Mediator & mediator = this->mediator; +		ComponentManager & mgr = mediator.component_manager;  		GameObject object1 = mgr.new_object("scene_2", "tag_scene_2", vec2{0, 0}, 0, 1);  		GameObject object2 = mgr.new_object("scene_2", "tag_scene_2", vec2{0, 1}, 0, 1);  		GameObject object3 = mgr.new_object("scene_2", "tag_scene_2", vec2{0, 2}, 0, 1); @@ -41,7 +44,8 @@ public:  	ConcreteScene3(const string & name) : name(name) {}  	void load_scene() { -		ComponentManager & mgr = this->component_manager; +		Mediator & mediator = this->mediator; +		ComponentManager & mgr = mediator.component_manager;  		GameObject object1 = mgr.new_object("scene_3", "tag_scene_3", vec2{0, 0}, 0, 1);  	} @@ -52,9 +56,11 @@ private:  };  class SceneManagerTest : public ::testing::Test { +	Mediator m; +  public: -	ComponentManager component_mgr{}; -	SceneManager scene_mgr{component_mgr}; +	ComponentManager component_mgr{m}; +	SceneManager scene_mgr{m};  };  TEST_F(SceneManagerTest, loadScene) { diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp new file mode 100644 index 0000000..5da31e7 --- /dev/null +++ b/src/test/ScriptEventTest.cpp @@ -0,0 +1,50 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Event.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Script.h> +#include <crepe/api/Vector2.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/system/ScriptSystem.h> + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptEventTest : public ScriptTest { +public: +	EventManager & event_manager = mediator.event_manager; + +	class MyEvent : public Event {}; +}; + +TEST_F(ScriptEventTest, Inactive) { +	BehaviorScript & behaviorscript = this->behaviorscript; +	MyScript & script = this->script; +	EventManager & evmgr = this->event_manager; + +	unsigned event_count = 0; +	script.subscribe<MyEvent>([&](const MyEvent &) { +		event_count++; +		return true; +	}); + +	system.update(); +	behaviorscript.active = false; +	EXPECT_EQ(0, event_count); + +	evmgr.trigger_event<MyEvent>(); +	EXPECT_EQ(0, event_count); + +	behaviorscript.active = true; +	evmgr.trigger_event<MyEvent>(); +	EXPECT_EQ(1, event_count); +} diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp new file mode 100644 index 0000000..9ee1e52 --- /dev/null +++ b/src/test/ScriptSceneTest.cpp @@ -0,0 +1,30 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include "ScriptTest.h" +#include <crepe/manager/SceneManager.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptSceneTest : public ScriptTest { +public: +	SceneManager scene_manager{mediator}; + +	class MyScene : public Scene {}; +}; + +TEST_F(ScriptSceneTest, Inactive) { +	BehaviorScript & behaviorscript = this->behaviorscript; +	MyScript & script = this->script; + +	const char * non_default_value = "foo"; +	ASSERT_NE(non_default_value, scene_manager.next_scene); + +	script.set_next_scene(non_default_value); +	EXPECT_EQ(non_default_value, scene_manager.next_scene); +} diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 78d5061..1d2d6dd 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -1,129 +1,77 @@ +#include <gmock/gmock.h>  #include <gtest/gtest.h>  // stupid hack to allow access to private/protected members under test  #define private public  #define protected public -#include <crepe/ComponentManager.h> -#include <crepe/api/BehaviorScript.h> -#include <crepe/api/Event.h> -#include <crepe/api/EventManager.h> +#include "ScriptTest.h"  #include <crepe/api/GameObject.h> -#include <crepe/api/Script.h> -#include <crepe/api/Vector2.h> -#include <crepe/system/ScriptSystem.h>  using namespace std;  using namespace crepe;  using namespace testing; -class MyEvent : public Event {}; - -class ScriptTest : public Test { -public: -	ComponentManager component_manager{}; -	ScriptSystem system{component_manager}; -	EventManager & event_manager = EventManager::get_instance(); - -	class MyScript : public Script { -		// NOTE: default (private) visibility of init and update shouldn't cause -		// issues! -		void init() { -			this->init_count++; - -			subscribe<MyEvent>([this](const MyEvent &) { -				this->event_count++; -				return true; -			}); - -			// init should never be called more than once -			EXPECT_LE(this->init_count, 1); -		} -		void update() { this->update_count++; } - -	public: -		unsigned init_count = 0; -		unsigned update_count = 0; -		unsigned event_count = 0; -	}; - -	OptionalRef<BehaviorScript> behaviorscript; -	OptionalRef<MyScript> script; - -	void SetUp() override { -		auto & mgr = this->component_manager; -		GameObject entity = mgr.new_object("name"); -		BehaviorScript & component = entity.add_component<BehaviorScript>(); - -		this->behaviorscript = component; -		ASSERT_TRUE(this->behaviorscript); -		EXPECT_EQ(component.script.get(), nullptr); -		component.set_script<MyScript>(); -		ASSERT_NE(component.script.get(), nullptr); - -		this->script = *(MyScript *) component.script.get(); -		ASSERT_TRUE(this->script); - -		// sanity -		MyScript & script = this->script; -		ASSERT_EQ(script.init_count, 0); -		ASSERT_EQ(script.update_count, 0); -		ASSERT_EQ(script.event_count, 0); -	} -}; +void ScriptTest::SetUp() { +	auto & mgr = this->component_manager; +	GameObject entity = mgr.new_object("name"); +	BehaviorScript & component = entity.add_component<BehaviorScript>(); + +	this->behaviorscript = component; +	ASSERT_TRUE(this->behaviorscript); +	EXPECT_EQ(component.script.get(), nullptr); +	component.set_script<NiceMock<MyScript>>(); +	ASSERT_NE(component.script.get(), nullptr); + +	this->script = *(MyScript *) component.script.get(); +	ASSERT_TRUE(this->script); +}  TEST_F(ScriptTest, Default) {  	MyScript & script = this->script; -	EXPECT_EQ(0, script.init_count); -	EXPECT_EQ(0, script.update_count); -	EXPECT_EQ(0, script.event_count); +	EXPECT_CALL(script, init()).Times(0); +	EXPECT_CALL(script, update()).Times(0);  }  TEST_F(ScriptTest, UpdateOnce) {  	MyScript & script = this->script; -	system.update(); -	EXPECT_EQ(1, script.init_count); -	EXPECT_EQ(1, script.update_count); -	EXPECT_EQ(0, script.event_count); +	{ +		InSequence seq; + +		EXPECT_CALL(script, init()).Times(1); +		EXPECT_CALL(script, update()).Times(1); +		system.update(); +	} + +	{ +		InSequence seq; + +		EXPECT_CALL(script, init()).Times(0); +		EXPECT_CALL(script, update()).Times(1); +		system.update(); +	}  }  TEST_F(ScriptTest, UpdateInactive) {  	BehaviorScript & behaviorscript = this->behaviorscript;  	MyScript & script = this->script; -	behaviorscript.active = false; -	system.update(); -	EXPECT_EQ(0, script.init_count); -	EXPECT_EQ(0, script.update_count); -	EXPECT_EQ(0, script.event_count); - -	behaviorscript.active = true; -	system.update(); -	EXPECT_EQ(1, script.init_count); -	EXPECT_EQ(1, script.update_count); -	EXPECT_EQ(0, script.event_count); -} +	{ +		InSequence seq; -TEST_F(ScriptTest, EventInactive) { -	BehaviorScript & behaviorscript = this->behaviorscript; -	MyScript & script = this->script; -	EventManager & evmgr = this->event_manager; - -	system.update(); -	behaviorscript.active = false; -	EXPECT_EQ(1, script.init_count); -	EXPECT_EQ(1, script.update_count); -	EXPECT_EQ(0, script.event_count); - -	evmgr.trigger_event<MyEvent>(); -	EXPECT_EQ(1, script.init_count); -	EXPECT_EQ(1, script.update_count); -	EXPECT_EQ(0, script.event_count); - -	behaviorscript.active = true; -	evmgr.trigger_event<MyEvent>(); -	EXPECT_EQ(1, script.init_count); -	EXPECT_EQ(1, script.update_count); -	EXPECT_EQ(1, script.event_count); +		EXPECT_CALL(script, init()).Times(0); +		EXPECT_CALL(script, update()).Times(0); +		behaviorscript.active = false; +		system.update(); +	} + +	{ +		InSequence seq; + +		EXPECT_CALL(script, init()).Times(1); +		EXPECT_CALL(script, update()).Times(1); +		behaviorscript.active = true; +		system.update(); +	}  } diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h new file mode 100644 index 0000000..1bbfdd3 --- /dev/null +++ b/src/test/ScriptTest.h @@ -0,0 +1,31 @@ +#pragma once + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/system/ScriptSystem.h> + +class ScriptTest : public testing::Test { +protected: +	crepe::Mediator mediator; + +public: +	crepe::ComponentManager component_manager{mediator}; +	crepe::ScriptSystem system{mediator}; + +	class MyScript : public crepe::Script { +		// NOTE: explicitly stating `public:` is not required on actual scripts + +	public: +		MOCK_METHOD(void, init, (), (override)); +		MOCK_METHOD(void, update, (), (override)); +	}; + +	crepe::OptionalRef<crepe::BehaviorScript> behaviorscript; +	crepe::OptionalRef<MyScript> script; + +	virtual void SetUp(); +}; |