diff options
| author | heavydemon21 <nielsstunnebrink1@gmail.com> | 2024-11-21 20:35:09 +0100 | 
|---|---|---|
| committer | heavydemon21 <nielsstunnebrink1@gmail.com> | 2024-11-21 20:35:09 +0100 | 
| commit | ad0dcad1f11d698abf71bf69fb0927c26298d253 (patch) | |
| tree | 3e8a501a84682ca2b6c085a987666c9b777bc1c7 /src/crepe/api | |
| parent | bdc81e355e5bee5d2a3e29346ba08f7bc55196ca (diff) | |
| parent | 115d6f50152dc018073345800ca90b85846ebaa9 (diff) | |
Merge branch 'master' into niels/decoupling_pixel_and_pos
Diffstat (limited to 'src/crepe/api')
30 files changed, 1049 insertions, 86 deletions
| diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..e148367 --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,54 @@ +#include <filesystem> +#include <stdexcept> +#include <whereami.h> + +#include "api/Config.h" + +#include "Asset.h" + +using namespace crepe; +using namespace std; + +Asset::Asset(const string & src) : src(find_asset(src)) {} +Asset::Asset(const char * src) : src(find_asset(src)) {} + +const string & Asset::get_path() const noexcept { return this->src; } + +string Asset::find_asset(const string & src) const { +	auto & cfg = Config::get_instance(); +	string & root_pattern = cfg.asset.root_pattern; + +	// if root_pattern is empty, find_asset must return all paths as-is +	if (root_pattern.empty()) return src; + +	// absolute paths do not need to be resolved, only canonicalized +	filesystem::path path = src; +	if (path.is_absolute()) return filesystem::canonical(path); + +	// find directory matching root_pattern +	filesystem::path root = this->whereami(); +	while (1) { +		if (filesystem::exists(root / root_pattern)) break; +		if (!root.has_parent_path()) +			throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); +		root = root.parent_path(); +	} + +	// join path to root (base directory) and canonicalize +	return filesystem::canonical(root / path); +} + +string Asset::whereami() const noexcept { +	string path; +	size_t path_length = wai_getExecutablePath(NULL, 0, NULL); +	path.resize(path_length + 1); // wai writes null byte +	wai_getExecutablePath(path.data(), path_length, NULL); +	path.resize(path_length); +	return path; +} + +bool Asset::operator==(const Asset & other) const noexcept { return this->src == other.src; } + +size_t std::hash<const Asset>::operator()(const Asset & asset) const noexcept { +	return std::hash<string>{}(asset.get_path()); +}; diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..bfd0ac7 --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,84 @@ +#pragma once + +#include <string> + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate game asset files, and should *always* be used + * instead of reading file paths directly. + */ +class Asset { +public: +	/** +	 * \param src  Unique identifier to asset +	 */ +	Asset(const std::string & src); +	/** +	 * \param src  Unique identifier to asset +	 */ +	Asset(const char * src); + +public: +	/** +	 * \brief Get the path to this asset +	 * \return path to this asset +	 */ +	const std::string & get_path() const noexcept; + +	/** +	 * \brief Comparison operator +	 * \param other Possibly different instance of \c Asset to test equality against +	 * \return True if \c this and \c other are equal +	 */ +	bool operator==(const Asset & other) const noexcept; + +private: +	//! path to asset +	const std::string src; + +private: +	/** +	 * \brief Locate asset path, or throw exception if it cannot be found +	 * +	 * This function resolves asset locations relative to crepe::Config::root_pattern if it is +	 * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized. +	 * This function only returns if the file can be found. +	 * +	 * \param src Arbitrary path to resource file +	 * +	 * \returns \p src if crepe::Config::root_pattern is empty +	 * \returns Canonical path to \p src +	 * +	 * \throws std::runtime_error if root_pattern cannot be found +	 * \throws std::filesystem::filesystem_error if the resolved path does not exist +	 * \throws std::filesystem::filesystem_error if the path cannot be canonicalized +	 */ +	std::string find_asset(const std::string & src) const; +	/** +	 * \returns The path to the current executable +	 */ +	std::string whereami() const noexcept; +}; + +} // namespace crepe + +namespace std { + +//! Hash helper struct +template <> +struct hash<const crepe::Asset> { +	/** +	 * \brief Hash operator for crepe::Asset +	 * +	 * This function hashes a crepe::Asset instance, allowing it to be used as a key in an \c +	 * std::unordered_map. +	 * +	 * \returns Hash value +	 */ +	size_t operator()(const crepe::Asset & asset) const noexcept; +}; + +} // namespace std diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index f9b370f..d6b6801 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -17,8 +17,13 @@ target_sources(crepe PUBLIC  	Vector2.cpp  	Camera.cpp  	Animator.cpp +	EventManager.cpp +	IKeyListener.cpp +	IMouseListener.cpp  	LoopManager.cpp  	LoopTimer.cpp +	Asset.cpp +	EventHandler.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -43,6 +48,14 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	SceneManager.hpp  	Camera.h  	Animator.h +	EventManager.h +	EventManager.hpp +	EventHandler.h +	EventHandler.hpp +	Event.h +	IKeyListener.h +	IMouseListener.h  	LoopManager.h  	LoopTimer.h +	Asset.h  ) diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp index 9e5f187..29bd77a 100644 --- a/src/crepe/api/Color.cpp +++ b/src/crepe/api/Color.cpp @@ -2,32 +2,11 @@  using namespace crepe; -Color Color::white = Color(255, 255, 255, 0); -Color Color::red = Color(255, 0, 0, 0); -Color Color::green = Color(0, 255, 0, 0); -Color Color::blue = Color(0, 0, 255, 0); -Color Color::black = Color(0, 0, 0, 0); -Color Color::cyan = Color(0, 255, 255, 0); -Color Color::yellow = Color(255, 255, 0, 0); -Color Color::magenta = Color(255, 0, 255, 0); - -Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { -	this->a = alpha; -	this->r = red; -	this->g = green; -	this->b = blue; -}; - -const Color & Color::get_white() { return Color::white; }; - -const Color & Color::get_red() { return Color::red; }; -const Color & Color::get_green() { return Color::green; }; -const Color & Color::get_blue() { return Color::blue; }; - -const Color & Color::get_black() { return Color::black; }; - -const Color & Color::get_cyan() { return Color::cyan; }; - -const Color & Color::get_yellow() { return Color::yellow; }; - -const Color & Color::get_magenta() { return Color::magenta; }; +const Color Color::WHITE{0xff, 0xff, 0xff}; +const Color Color::RED{0xff, 0x00, 0x00}; +const Color Color::GREEN{0x00, 0xff, 0x00}; +const Color Color::BLUE{0x00, 0x00, 0xff}; +const Color Color::BLACK{0x00, 0x00, 0x00}; +const Color Color::CYAN{0x00, 0xff, 0xff}; +const Color Color::YELLOW{0xff, 0xff, 0x00}; +const Color Color::MAGENTA{0xff, 0x00, 0xff}; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h index aa47bf4..84edb5c 100644 --- a/src/crepe/api/Color.h +++ b/src/crepe/api/Color.h @@ -4,41 +4,20 @@  namespace crepe { -// TODO: make Color a struct w/o constructors/destructors -class Color { - -	// FIXME: can't these colors be defined as a `static constexpr const Color` -	// instead? - -public: -	Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); -	static const Color & get_white(); -	static const Color & get_red(); -	static const Color & get_green(); -	static const Color & get_blue(); -	static const Color & get_cyan(); -	static const Color & get_magenta(); -	static const Color & get_yellow(); -	static const Color & get_black(); - -private: -	// TODO: why are these private!? -	uint8_t r; -	uint8_t g; -	uint8_t b; -	uint8_t a; - -	static Color white; -	static Color red; -	static Color green; -	static Color blue; -	static Color cyan; -	static Color magenta; -	static Color yellow; -	static Color black; - -private: -	friend class SDLContext; +struct Color { +	uint8_t r = 0x00; +	uint8_t g = 0x00; +	uint8_t b = 0x00; +	uint8_t a = 0xff; + +	static const Color WHITE; +	static const Color RED; +	static const Color GREEN; +	static const Color BLUE; +	static const Color CYAN; +	static const Color MAGENTA; +	static const Color YELLOW; +	static const Color BLACK;  };  } // namespace crepe diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 3ab877a..13eabd1 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -62,6 +62,20 @@ public:  		 */  		double gravity = 1;  	} physics; + +	//! Asset loading options +	struct { +		/** +		 * \brief Pattern to match for Asset base directory +		 * +		 * All non-absolute paths resolved using \c Asset will be made relative to +		 * the first parent directory relative to the calling executable where +		 * appending this pattern results in a path that exists. If this string is +		 * empty, path resolution is disabled, and Asset will return all paths +		 * as-is. +		 */ +		std::string root_pattern = ".crepe-root"; +	} asset;  };  } // namespace crepe diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h new file mode 100644 index 0000000..06cf7f3 --- /dev/null +++ b/src/crepe/api/Event.h @@ -0,0 +1,112 @@ +// TODO discussing the location of these events +#pragma once + +#include <string> + +#include "KeyCodes.h" + +/** + * \brief Base class for all event types in the system. + */ +class Event {}; + +/** + * \brief Event triggered when a key is pressed. + */ +class KeyPressEvent : public Event { +public: +	//! false if first time press, true if key is repeated +	bool repeat = false; + +	//! The key that was pressed. +	Keycode key = Keycode::NONE; +}; + +/** + * \brief Event triggered when a key is released. + */ +class KeyReleaseEvent : public Event { +public: +	//! The key that was released. +	Keycode key = Keycode::NONE; +}; + +/** + * \brief Event triggered when a mouse button is pressed. + */ +class MousePressEvent : 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; + +	//! The mouse button that was pressed. +	MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when a mouse button is clicked (press and release). + */ +class MouseClickEvent : 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; + +	//! The mouse button that was clicked. +	MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when a mouse button is released. + */ +class MouseReleaseEvent : 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; + +	//! The mouse button that was released. +	MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when the mouse is moved. + */ +class MouseMoveEvent : 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; +}; + +/** + * \brief Event triggered during a collision between objects. + */ +class CollisionEvent : public Event { +public: +	//! Data describing the collision (currently not implemented). +	// Collision collisionData; +}; + +/** + * \brief Event triggered when text is submitted, e.g., from a text input. + */ +class TextSubmitEvent : public Event { +public: +	//! The submitted text. +	std::string text = ""; +}; + +/** + * \brief Event triggered to indicate the application is shutting down. + */ +class ShutDownEvent : public Event {}; diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp new file mode 100644 index 0000000..4dc232f --- /dev/null +++ b/src/crepe/api/EventHandler.cpp @@ -0,0 +1,5 @@ +#include "EventHandler.h" + +using namespace crepe; + +bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); } diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h new file mode 100644 index 0000000..ef659fd --- /dev/null +++ b/src/crepe/api/EventHandler.h @@ -0,0 +1,96 @@ +#pragma once + +#include <functional> +#include <string> + +#include "Event.h" + +namespace crepe { +/** + * \brief A type alias for an event handler function. + *  + * The EventHandler is a std::function that takes an EventType reference and returns a boolean value  + * indicating whether the event is handled. + *  + * \tparam EventType The type of event this handler will handle. + *  + * Returning \c false from an event handler results in the event being propogated to other listeners for the same event type, while returning \c true stops propogation altogether. + */ +template <typename EventType> +using EventHandler = std::function<bool(const EventType & e)>; + +/** + * \class IEventHandlerWrapper + * \brief An abstract base class for event handler wrappers. + *  + * This class provides the interface for handling events. Derived classes must implement the + * `call()` method to process events + */ +class IEventHandlerWrapper { +public: +	/** +     * \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. +     */ +	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. +     */ +	virtual bool call(const Event & e) = 0; +}; + +/** + * \class EventHandlerWrapper + * \brief A wrapper for event handler functions. + *  + * This class wraps an event handler function of a specific event type. It implements the  + * `call()` and `get_type()` methods to allow the handler to be executed and its type to be  + * queried. + *  + * \tparam EventType The type of event this handler will handle. + */ +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. +     */ +	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. +     */ +	bool call(const Event & e) override; +	//! The event handler function. +	EventHandler<EventType> handler; +}; + +} // namespace crepe + +#include "EventHandler.hpp" diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp new file mode 100644 index 0000000..391dcca --- /dev/null +++ b/src/crepe/api/EventHandler.hpp @@ -0,0 +1,18 @@ +#include <typeindex> + +#include "EventHandler.h" + +namespace crepe { + +// Implementation of EventHandlerWrapper constructor +template <typename EventType> +EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler) +	: handler(handler) {} + +// Implementation of EventHandlerWrapper::call +template <typename EventType> +bool EventHandlerWrapper<EventType>::call(const Event & e) { +	return this->handler(static_cast<const EventType &>(e)); +} + +} //namespace crepe diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp new file mode 100644 index 0000000..20f0dd3 --- /dev/null +++ b/src/crepe/api/EventManager.cpp @@ -0,0 +1,46 @@ +#include "EventManager.h" + +using namespace crepe; +using namespace std; + +EventManager & EventManager::get_instance() { +	static EventManager instance; +	return instance; +} + +void EventManager::dispatch_events() { +	for (auto & event : this->events_queue) { +		this->handle_event(event.type, event.channel, *event.event.get()); +	} +	this->events_queue.clear(); +} + +void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) { +	auto handlers_it = this->subscribers.find(type); +	if (handlers_it == this->subscribers.end()) return; + +	vector<CallbackEntry> & handlers = handlers_it->second; +	for (auto & handler : handlers) { +		bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL; +		if (check_channel && handler.channel != channel) continue; + +		bool handled = handler.callback->exec(data); +		if (handled) return; +	} +} + +void EventManager::clear() { +	this->subscribers.clear(); +	this->events_queue.clear(); +} + +void EventManager::unsubscribe(subscription_t id) { +	for (auto & [event_type, handlers] : this->subscribers) { +		for (auto it = handlers.begin(); it != handlers.end(); it++) { +			// find listener with subscription id +			if ((*it).id != id) continue; +			it = handlers.erase(it); +			return; +		} +	} +} diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h new file mode 100644 index 0000000..348a04d --- /dev/null +++ b/src/crepe/api/EventManager.h @@ -0,0 +1,161 @@ +#pragma once + +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <vector> + +#include "Event.h" +#include "EventHandler.h" + +namespace crepe { + +//! Event listener unique ID +typedef size_t subscription_t; + +/** + * \brief Event channel + * + * Events can be sent to a specific channel, which prevents listeners on other channels from + * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners. + */ +typedef size_t event_channel_t; + +/** + * \class EventManager + * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling. + *  + * The `EventManager` acts as a centralized event system. It allows for registering callbacks + * for specific event types, triggering events synchronously, queueing events for later + * processing, and managing subscriptions via unique identifiers. + */ +class EventManager { +public: +	static constexpr const event_channel_t CHANNEL_ALL = -1; + +	/** +	 * \brief Get the singleton instance of the EventManager. +	 *  +	 * This method returns the unique instance of the EventManager, creating it if it +	 * doesn't already exist. Ensures only one instance is active in the program. +	 *  +	 * \return Reference to the singleton instance of the EventManager. +	 */ +	static EventManager & get_instance(); + +	/** +	 * \brief Subscribe to a specific event type. +	 *  +	 * Registers a callback for a given event type and optional channel. Each callback +	 * is assigned a unique subscription ID that can be used for later unsubscription. +	 *  +	 * \tparam EventType The type of the event to subscribe to. +	 * \param callback The callback function to be invoked when the event is triggered. +	 * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels). +	 * \return A unique subscription ID associated with the registered callback. +	 */ +	template <typename EventType> +	subscription_t subscribe(const EventHandler<EventType> & callback, +							 event_channel_t channel = CHANNEL_ALL); + +	/** +	 * \brief Unsubscribe a previously registered callback. +	 *  +	 * Removes a callback from the subscription list based on its unique subscription ID. +	 *  +	 * \param event_id The unique subscription ID of the callback to remove. +	 */ +	void unsubscribe(subscription_t event_id); + +	/** +	 * \brief Trigger an event immediately. +	 *  +	 * Synchronously invokes all registered callbacks for the given event type on the specified channel. +	 *  +	 * \tparam EventType The type of the event to trigger. +	 * \param event The event instance to pass to the callbacks. +	 * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels). +	 */ +	template <typename EventType> +	void trigger_event(const EventType & event, event_channel_t channel = CHANNEL_ALL); + +	/** +	 * \brief Queue an event for later processing. +	 *  +	 * Adds an event to the event queue to be processed during the next call to `dispatch_events`. +	 *  +	 * \tparam EventType The type of the event to queue. +	 * \param event The event instance to queue. +	 * \param channel The channel to associate with the event (default is CHANNEL_ALL). +	 */ +	template <typename EventType> +	void queue_event(const EventType & event, event_channel_t channel = CHANNEL_ALL); + +	/** +	 * \brief Process all queued events. +	 *  +	 * Iterates through the event queue and triggers callbacks for each queued event. +	 * Events are removed from the queue once processed. +	 */ +	void dispatch_events(); + +	/** +	 * \brief Clear all subscriptions. +	 *  +	 * Removes all registered event handlers and clears the subscription list. +	 */ +	void clear(); + +private: +	/** +	 * \brief Default constructor for the EventManager. +	 *  +	 * Constructor is private to enforce the singleton pattern. +	 */ +	EventManager() = default; + +	/** +	 * \struct QueueEntry +	 * \brief Represents an entry in the event queue. +	 */ +	struct QueueEntry { +		std::unique_ptr<Event> event; ///< The event instance. +		event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event. +		std::type_index type; ///< The type of the event. +	}; + +	/** +	 * \brief Internal event handler +	 * +	 * This function processes a single event, and is used to process events both during +	 * EventManager::dispatch_events and inside EventManager::trigger_event +	 * +	 * \param type \c typeid of concrete Event class +	 * \param channel Event channel +	 * \param data Event data +	 */ +	void handle_event(std::type_index type, event_channel_t channel, const Event & data); + +	/** +	 * \struct CallbackEntry +	 * \brief Represents a registered event handler callback. +	 */ +	struct CallbackEntry { +		std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper. +		event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to. +		subscription_t id = -1; ///< Unique subscription ID. +	}; + +	//! The queue of events to be processed during dispatch. +	std::vector<QueueEntry> events_queue; + +	//! A map of event type to registered callbacks. +	std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers; + +	//! Counter to generate unique subscription IDs. +	subscription_t subscription_counter = 0; +}; + +} // namespace crepe + +#include "EventManager.hpp" diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp new file mode 100644 index 0000000..a5f4556 --- /dev/null +++ b/src/crepe/api/EventManager.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "EventManager.h" + +namespace crepe { + +template <typename EventType> +subscription_t EventManager::subscribe(const EventHandler<EventType> & callback, +									   event_channel_t channel) { +	subscription_counter++; +	std::type_index event_type = typeid(EventType); +	std::unique_ptr<EventHandlerWrapper<EventType>> handler +		= std::make_unique<EventHandlerWrapper<EventType>>(callback); +	std::vector<CallbackEntry> & handlers = this->subscribers[event_type]; +	handlers.emplace_back(CallbackEntry{ +		.callback = std::move(handler), .channel = channel, .id = subscription_counter}); +	return subscription_counter; +} + +template <typename EventType> +void EventManager::queue_event(const EventType & event, event_channel_t channel) { +	static_assert(std::is_base_of<Event, EventType>::value, +				  "EventType must derive from Event"); +	this->events_queue.push_back(QueueEntry{ +		.event = std::make_unique<EventType>(event), +		.channel = channel, +		.type = typeid(EventType), +	}); +} + +template <typename EventType> +void EventManager::trigger_event(const EventType & event, event_channel_t channel) { +	this->handle_event(typeid(EventType), channel, event); +} + +} // namespace crepe diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index 287e81d..4874426 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -23,12 +23,10 @@ void GameObject::set_parent(const GameObject & parent) {  	ComponentManager & mgr = this->component_manager;  	// Set parent on own Metadata component -	vector<reference_wrapper<Metadata>> this_metadata -		= mgr.get_components_by_id<Metadata>(this->id); +	RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id);  	this_metadata.at(0).get().parent = parent.id;  	// Add own id to children list of parent's Metadata component -	vector<reference_wrapper<Metadata>> parent_metadata -		= mgr.get_components_by_id<Metadata>(parent.id); +	RefVector<Metadata> parent_metadata = mgr.get_components_by_id<Metadata>(parent.id);  	parent_metadata.at(0).get().children.push_back(this->id);  } diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp new file mode 100644 index 0000000..8642655 --- /dev/null +++ b/src/crepe/api/IKeyListener.cpp @@ -0,0 +1,19 @@ +#include "IKeyListener.h" + +using namespace crepe; + +// Constructor with specified channel +IKeyListener::IKeyListener(event_channel_t channel) +	: event_manager(EventManager::get_instance()) { +	this->press_id = event_manager.subscribe<KeyPressEvent>( +		[this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); +	this->release_id = event_manager.subscribe<KeyReleaseEvent>( +		[this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, +		channel); +} + +// Destructor, unsubscribe events +IKeyListener::~IKeyListener() { +	event_manager.unsubscribe(this->press_id); +	event_manager.unsubscribe(this->release_id); +} diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h new file mode 100644 index 0000000..328a4c2 --- /dev/null +++ b/src/crepe/api/IKeyListener.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Event.h" +#include "EventHandler.h" +#include "EventManager.h" + +namespace crepe { + +/** + * \class IKeyListener + * \brief Interface for keyboard event handling in the application. + */ +class IKeyListener { +public: +	/** +     * \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; +	IKeyListener & operator=(const IKeyListener &) = delete; +	IKeyListener & operator=(IKeyListener &&) = delete; +	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. +     */ +	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. +     */ +	virtual bool on_key_released(const KeyReleaseEvent & event) = 0; + +private: +	//! Key press event id +	subscription_t press_id = -1; +	//! Key release event id +	subscription_t release_id = -1; +	//! EventManager reference +	EventManager & event_manager; +}; + +} // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp new file mode 100644 index 0000000..989aeb3 --- /dev/null +++ b/src/crepe/api/IMouseListener.cpp @@ -0,0 +1,29 @@ +#include "IMouseListener.h" + +using namespace crepe; + +IMouseListener::IMouseListener(event_channel_t channel) +	: event_manager(EventManager::get_instance()) { +	this->click_id = event_manager.subscribe<MouseClickEvent>( +		[this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, +		channel); + +	this->press_id = event_manager.subscribe<MousePressEvent>( +		[this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, +		channel); + +	this->release_id = event_manager.subscribe<MouseReleaseEvent>( +		[this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, +		channel); + +	this->move_id = event_manager.subscribe<MouseMoveEvent>( +		[this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel); +} + +IMouseListener::~IMouseListener() { +	// Unsubscribe event handlers +	event_manager.unsubscribe(this->click_id); +	event_manager.unsubscribe(this->press_id); +	event_manager.unsubscribe(this->release_id); +	event_manager.unsubscribe(this->move_id); +} diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h new file mode 100644 index 0000000..15e1619 --- /dev/null +++ b/src/crepe/api/IMouseListener.h @@ -0,0 +1,72 @@ +#pragma once + +#include "Event.h" +#include "EventHandler.h" +#include "EventManager.h" + +namespace crepe { + +/** + * \class IMouseListener + * \brief Interface for mouse event handling in the application. + */ +class IMouseListener { +public: +	/** +     * \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; +	IMouseListener(const IMouseListener &) = delete; +	IMouseListener & operator=(const IMouseListener &&) = delete; +	IMouseListener(IMouseListener &&) = delete; + +	/** +     * \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. +     */ +	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. +     */ +	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. +     */ +	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. +     */ +	virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; + +private: +	//! Mouse click event id +	subscription_t click_id = -1; +	//! Mouse press event id +	subscription_t press_id = -1; +	//! Mouse release event id +	subscription_t release_id = -1; +	//! Mouse move event id +	subscription_t move_id = -1; +	//! EventManager reference +	EventManager & event_manager; +}; + +} //namespace crepe diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h new file mode 100644 index 0000000..9e173e0 --- /dev/null +++ b/src/crepe/api/KeyCodes.h @@ -0,0 +1,153 @@ +#pragma once + +//! Enumeration for mouse button inputs, including standard and extended buttons. +enum class MouseButton { +	NONE = 0, //!< No mouse button input. +	LEFT_MOUSE = 1, //!< Left mouse button. +	RIGHT_MOUSE = 2, //!< Right mouse button. +	MIDDLE_MOUSE = 3, //!< Middle mouse button (scroll wheel press). +	X1_MOUSE = 4, //!< First extended mouse button. +	X2_MOUSE = 5, //!< Second extended mouse button. +	SCROLL_UP = 6, //!< Scroll wheel upward movement. +	SCROLL_DOWN = 7, //!< Scroll wheel downward movement. +}; + +//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. +enum class Keycode { +	NONE = 0, //!< No key input. +	SPACE = 32, //!< Spacebar. +	APOSTROPHE = 39, //!< Apostrophe ('). +	COMMA = 44, //!< Comma (,). +	MINUS = 45, //!< Minus (-). +	PERIOD = 46, //!< Period (.). +	SLASH = 47, //!< Slash (/). +	D0 = 48, //!< Digit 0. +	D1 = 49, //!< Digit 1. +	D2 = 50, //!< Digit 2. +	D3 = 51, //!< Digit 3. +	D4 = 52, //!< Digit 4. +	D5 = 53, //!< Digit 5. +	D6 = 54, //!< Digit 6. +	D7 = 55, //!< Digit 7. +	D8 = 56, //!< Digit 8. +	D9 = 57, //!< Digit 9. +	SEMICOLON = 59, //!< Semicolon (;). +	EQUAL = 61, //!< Equal sign (=). +	A = 65, //!< Key 'A'. +	B = 66, //!< Key 'B'. +	C = 67, //!< Key 'C'. +	D = 68, //!< Key 'D'. +	E = 69, //!< Key 'E'. +	F = 70, //!< Key 'F'. +	G = 71, //!< Key 'G'. +	H = 72, //!< Key 'H'. +	I = 73, //!< Key 'I'. +	J = 74, //!< Key 'J'. +	K = 75, //!< Key 'K'. +	L = 76, //!< Key 'L'. +	M = 77, //!< Key 'M'. +	N = 78, //!< Key 'N'. +	O = 79, //!< Key 'O'. +	P = 80, //!< Key 'P'. +	Q = 81, //!< Key 'Q'. +	R = 82, //!< Key 'R'. +	S = 83, //!< Key 'S'. +	T = 84, //!< Key 'T'. +	U = 85, //!< Key 'U'. +	V = 86, //!< Key 'V'. +	W = 87, //!< Key 'W'. +	X = 88, //!< Key 'X'. +	Y = 89, //!< Key 'Y'. +	Z = 90, //!< Key 'Z'. +	LEFT_BRACKET = 91, //!< Left bracket ([). +	BACKSLASH = 92, //!< Backslash (\). +	RIGHT_BRACKET = 93, //!< Right bracket (]). +	GRAVE_ACCENT = 96, //!< Grave accent (`). +	WORLD1 = 161, //!< Non-US key #1. +	WORLD2 = 162, //!< Non-US key #2. +	ESCAPE = 256, //!< Escape key. +	ENTER = 257, //!< Enter key. +	TAB = 258, //!< Tab key. +	BACKSPACE = 259, //!< Backspace key. +	INSERT = 260, //!< Insert key. +	DELETE = 261, //!< Delete key. +	RIGHT = 262, //!< Right arrow key. +	LEFT = 263, //!< Left arrow key. +	DOWN = 264, //!< Down arrow key. +	UP = 265, //!< Up arrow key. +	PAGE_UP = 266, //!< Page Up key. +	PAGE_DOWN = 267, //!< Page Down key. +	HOME = 268, //!< Home key. +	END = 269, //!< End key. +	CAPS_LOCK = 280, //!< Caps Lock key. +	SCROLL_LOCK = 281, //!< Scroll Lock key. +	NUM_LOCK = 282, //!< Num Lock key. +	PRINT_SCREEN = 283, //!< Print Screen key. +	PAUSE = 284, //!< Pause key. +	/** +	 * \name Function keys (F1-F25). +	 * \{ +	 */ +	F1 = 290, +	F2 = 291, +	F3 = 292, +	F4 = 293, +	F5 = 294, +	F6 = 295, +	F7 = 296, +	F8 = 297, +	F9 = 298, +	F10 = 299, +	F11 = 300, +	F12 = 301, +	F13 = 302, +	F14 = 303, +	F15 = 304, +	F16 = 305, +	F17 = 306, +	F18 = 307, +	F19 = 308, +	F20 = 309, +	F21 = 310, +	F22 = 311, +	F23 = 312, +	F24 = 313, +	F25 = 314, +	/// \} +	/** +	 * \name Keypad digits and operators. +	 * \{ +	 */ +	KP0 = 320, +	KP1 = 321, +	KP2 = 322, +	KP3 = 323, +	KP4 = 324, +	KP5 = 325, +	KP6 = 326, +	KP7 = 327, +	KP8 = 328, +	KP9 = 329, +	KP_DECIMAL = 330, +	KP_DIVIDE = 331, +	KP_MULTIPLY = 332, +	KP_SUBTRACT = 333, +	KP_ADD = 334, +	KP_ENTER = 335, +	KP_EQUAL = 336, +	/// \} +	/** +	 * \name Modifier keys. +	 * \{ +	 */ +	LEFT_SHIFT = 340, +	LEFT_CONTROL = 341, +	LEFT_ALT = 342, +	LEFT_SUPER = 343, +	RIGHT_SHIFT = 344, +	RIGHT_CONTROL = 345, +	RIGHT_ALT = 346, +	RIGHT_SUPER = 347, +	/// \} +	MENU = 348, //!< Menu key. +}; diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index f6904be..13e6dac 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,14 +4,28 @@  #include "../ComponentManager.h"  #include "../system/System.h" +#include "api/SceneManager.h"  namespace crepe { +/** + * \brief Main game loop manager + * + * This class is responsible for managing the game loop, including initialization and updating. + */  class LoopManager {  public:  	void start();  	LoopManager(); +	/** +	 * \brief Add a new concrete scene to the scene manager +	 * +	 * \tparam T  Type of concrete scene +	 */ +	template <typename T> +	void add_scene(); +  private:  	/**  	 * \brief Setup function for one-time initialization. @@ -53,12 +67,14 @@ private:  	 * This function updates physics and game logic based on LoopTimer's fixed_delta_time.  	 */  	void fixed_update(); +  	/**  	 * \brief Set game running variable  	 *  	 * \param running running (false = game shutdown, true = game running)  	 */  	void set_running(bool running); +  	/**  	 * \brief Function for executing render-related systems.  	 * @@ -71,6 +87,8 @@ private:  private:  	//! Component manager instance  	ComponentManager component_manager{}; +	//! Scene manager instance +	SceneManager scene_manager{component_manager};  private:  	/** diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp index 0b14fdb..9cf470b 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/api/LoopManager.hpp @@ -11,6 +11,11 @@  namespace crepe {  template <class T> +void LoopManager::add_scene() { +	this->scene_manager.add_scene<T>(); +} + +template <class T>  T & LoopManager::get_system() {  	using namespace std;  	static_assert(is_base_of<System, T>::value, diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp index 88aa82d..849945e 100644 --- a/src/crepe/api/Scene.cpp +++ b/src/crepe/api/Scene.cpp @@ -2,6 +2,4 @@  using namespace crepe; -Scene::Scene(ComponentManager & mgr, const std::string & name) -	: component_manager(mgr), -	  name(name) {} +Scene::Scene(ComponentManager & mgr) : component_manager(mgr) {} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index 0e516b6..869bf6f 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -7,19 +7,36 @@ namespace crepe {  class SceneManager;  class ComponentManager; +/** + * \brief Represents a Scene + *  + * This class represents a Scene. The Scene class is only used as an interface for the game + * programmer. + */  class Scene {  protected: -	Scene(ComponentManager & mgr, const std::string & name); +	//TODO: Use Loek's custom reference class to set ComponentManger via SceneManager instead of via constructor +	/** +	 * \param mgr  Reference to the ComponentManager +	 */ +	Scene(ComponentManager & mgr); +	//! SceneManager instances Scene  	friend class SceneManager;  public:  	virtual ~Scene() = default;  public: +	//! Load the scene  	virtual void load_scene() = 0; -	const std::string name; +	/** +	 * \brief Get the scene's name +	 * \return The scene's name +	 */ +	virtual std::string get_name() const = 0;  protected: +	//! Reference to the ComponentManager  	ComponentManager & component_manager;  }; diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/api/SceneManager.cpp index 7fb5cb0..1f783ad 100644 --- a/src/crepe/api/SceneManager.cpp +++ b/src/crepe/api/SceneManager.cpp @@ -18,7 +18,7 @@ void SceneManager::load_next_scene() {  	auto it = find_if(this->scenes.begin(), this->scenes.end(),  					  [&next_scene = this->next_scene](unique_ptr<Scene> & scene) { -						  return scene->name == next_scene; +						  return scene.get()->get_name() == next_scene;  					  });  	// next scene not found diff --git a/src/crepe/api/SceneManager.h b/src/crepe/api/SceneManager.h index e854794..45ba668 100644 --- a/src/crepe/api/SceneManager.h +++ b/src/crepe/api/SceneManager.h @@ -1,7 +1,6 @@  #pragma once  #include <memory> -#include <queue>  #include <vector>  #include "Scene.h" @@ -10,8 +9,15 @@ namespace crepe {  class ComponentManager; +/** + * \brief Manages scenes + * + * This class manages scenes. It can add new scenes and load them. It also manages the current scene + * and the next scene. + */  class SceneManager {  public: +	//! \param mgr  Reference to the ComponentManager  	SceneManager(ComponentManager & mgr);  public: @@ -19,10 +25,9 @@ public:  	 * \brief Add a new concrete scene to the scene manager  	 *  	 * \tparam T  Type of concrete scene -	 * \param name  Name of new scene  	 */  	template <typename T> -	void add_scene(const std::string & name); +	void add_scene();  	/**  	 * \brief Set the next scene  	 * @@ -35,8 +40,11 @@ public:  	void load_next_scene();  private: +	//! Vector of concrete scenes (added by add_scene())  	std::vector<std::unique_ptr<Scene>> scenes; +	//! Next scene to load  	std::string next_scene; +	//! Reference to the ComponentManager  	ComponentManager & component_manager;  }; diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/api/SceneManager.hpp index 714f690..94e5946 100644 --- a/src/crepe/api/SceneManager.hpp +++ b/src/crepe/api/SceneManager.hpp @@ -5,16 +5,16 @@  namespace crepe {  template <typename T> -void SceneManager::add_scene(const std::string & name) { +void SceneManager::add_scene() {  	using namespace std;  	static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene"); -	Scene * scene = new T(this->component_manager, name); +	Scene * scene = new T(this->component_manager);  	this->scenes.emplace_back(unique_ptr<Scene>(scene));  	// The first scene added, is the one that will be loaded at the beginning  	if (next_scene.empty()) { -		next_scene = name; +		next_scene = scene->get_name();  	}  } diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 2b70379..839d937 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -62,7 +62,7 @@ protected:  	 * \returns List of component references  	 */  	template <typename T> -	std::vector<std::reference_wrapper<T>> get_components() const; +	RefVector<T> get_components() const;  protected:  	// NOTE: Script must have a constructor without arguments so the game programmer doesn't need diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index a064a90..a85d814 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -10,7 +10,7 @@ namespace crepe {  template <typename T>  T & Script::get_component() const {  	using namespace std; -	vector<reference_wrapper<T>> all_components = this->get_components<T>(); +	RefVector<T> all_components = this->get_components<T>();  	if (all_components.size() < 1)  		throw runtime_error(  			format("Script: no component found with type = {}", typeid(T).name())); @@ -19,9 +19,8 @@ T & Script::get_component() const {  }  template <typename T> -std::vector<std::reference_wrapper<T>> Script::get_components() const { +RefVector<T> Script::get_components() const {  	auto & mgr = *this->component_manager_ref; -  	return mgr.get_components_by_id<T>(this->game_object_id);  } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index 0192793..74a55d4 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -2,8 +2,9 @@  #include <memory> +#include "../Component.h" +  #include "Color.h" -#include "Component.h"  #include "Texture.h"  namespace crepe { diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index 734a5bb..9be9421 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -26,7 +26,7 @@ Texture::~Texture() {  void Texture::load(unique_ptr<Asset> res) {  	SDLContext & ctx = SDLContext::get_instance(); -	this->texture = std::move(ctx.texture_from_path(res->get_canonical())); +	this->texture = std::move(ctx.texture_from_path(res->get_path()));  }  int Texture::get_width() const { |