From 3c5d56de6bc29583cfc31083545a6a15b8db88ae Mon Sep 17 00:00:00 2001 From: Nimac0 Date: Wed, 9 Apr 2025 21:06:34 +0200 Subject: [PATCH] feat/ref: Interaction and Event management --- include/InputManager.h | 16 ++-- include/InteractionComponent.h | 28 ++++++ include/InteractionEventdataStruct.h | 19 +++- src/EventManager.cpp | 10 +-- src/GameInternal.cpp | 1 + src/InputManager.cpp | 126 +++++---------------------- src/InteractionComponent.cpp | 22 +++++ src/InteractionEventdataStruct.cpp | 16 ++++ src/TransformComponent.cpp | 3 + 9 files changed, 120 insertions(+), 121 deletions(-) create mode 100644 include/InteractionComponent.h create mode 100644 src/InteractionComponent.cpp create mode 100644 src/InteractionEventdataStruct.cpp diff --git a/include/InputManager.h b/include/InputManager.h index ebc6272..8c3b144 100644 --- a/include/InputManager.h +++ b/include/InputManager.h @@ -102,7 +102,7 @@ public: struct InputAction { std::string name; std::vector bindings; - std::function callback; + std::function callback; }; InputManager(); @@ -110,28 +110,26 @@ public: void init(); // see if necessary void processEvents(); - void registerAction(const std::string& actionName, const std::vector& keys, std::function callback, const std::string& context); + void registerAction(const std::string& actionName, const std::vector& keys, std::function callback, const std::string& context); void setActiveContext(const std::string& context); std::string getActiveContext() const; - void rebindAction(const std::string& actionName, const std::vector& newBindings, const std::string& context); - void removeBindings(const std::string& actionName, const std::string& context); - std::vector getBindings(const std::string& actionName, const std::string& context) const; + //void rebindAction(const std::string& actionName, const std::vector& newBindings, const std::string& context); + //void removeBindings(const std::string& actionName, const std::string& context); + //std::vector getBindings(const std::string& actionName, const std::string& context) const; std::vector getActionsByKey(const Key key) const; SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event); + void initKeyMap(); private: // TODO: flesh this out to avoid loops in process actions // additionally to actionsByContext, not instead - std::map> actionsByContext; - std::map> actionsByKey; + std::map>> actionsByContextAndKey; std::map keyMap; std::string activeContext; - - void initKeyMap(); }; std::ostream& operator<<(std::ostream& os, InputManager::Key key); diff --git a/include/InteractionComponent.h b/include/InteractionComponent.h new file mode 100644 index 0000000..d016a6f --- /dev/null +++ b/include/InteractionComponent.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Component.h" +#include "InteractionListener.h" + +#include + +class InteractionComponent : public Component, public InteractionListener +{ +public: + /** + * @brief Constructor for the InteractionComponent. + * @param callback A function to be called when an interaction event is triggered. void* actor, void* data are passed to the callback function from InteractionEventdataStruct. + */ + InteractionComponent(std::function callback); + + /** + * @brief Internal function to be called when an interaction event is triggered. + */ + void interact(void* actor, void* data) override; + + /** + * @brief Internal function to use as reference for targeting. + */ + std::shared_ptr getPosition() override; +private: + std::function interactionCallback; +}; \ No newline at end of file diff --git a/include/InteractionEventdataStruct.h b/include/InteractionEventdataStruct.h index 036f03c..759bbb8 100644 --- a/include/InteractionEventdataStruct.h +++ b/include/InteractionEventdataStruct.h @@ -2,14 +2,27 @@ #include "Entity.h" #include "InteractionListener.h" +#include "InteractionManager.h" #include "Vector2D.h" #include #include +/** + * @brief Struct to hold data for interaction events. + * This struct is used to pass data to the interaction manager when an interaction event is triggered. + */ struct InteractionEventdataStruct { - void* actor; // suggestion, can also be used for other arbitrary data + /// Arbitray data to pass to the interaction listener. Can for example be an Entity ptr to represent the actor. + void* actor; + /// The data to pass to the interaction listener. Can be any type of pointer. void* data; + /// The target of the interaction, e.g. InteractionComponent of an Entity. Is required if strategy is set to 0 (none) std::weak_ptr target = std::weak_ptr(); - std::shared_ptr targetingReference; // required without explicit target - uint8_t strategy = 0; // required without explicit target, defaults to none + /// Coordinates from which to base targeting on. Is required if strategy is not set to 0 (none) + std::shared_ptr targetingReference = nullptr; + /// required without explicit target, defaults to none + /// @sa InteractionManager::TargetingStrategy + uint8_t strategy = 0; // int since enum would be impossibling user defined targetingStrategies + + void triggerEvent(); }; \ No newline at end of file diff --git a/src/EventManager.cpp b/src/EventManager.cpp index 049a8b0..717f072 100644 --- a/src/EventManager.cpp +++ b/src/EventManager.cpp @@ -12,11 +12,11 @@ Uint32 vego::VEGO_Event_Interaction; EventManager::EventManager() { - /// \TODO: from c++26 you (should be able to) can get the amount of name values in an enum vego::VEGO_Event_Interaction = SDL_RegisterEvents(1); // TODO: error handling } + void EventManager::registerListener(EventListener listener, std::initializer_list eventTypes) { std::ranges::for_each(eventTypes.begin(), eventTypes.end(), [this, &listener](const Uint32& eventType) { @@ -31,10 +31,10 @@ SDL_AppResult EventManager::handleEvent(SDL_Event* event) { SDL_EventType type = (SDL_EventType) event->type; if (this->eventListeners.contains(type)) { - auto results = this->eventListeners.at(type) | std::views::transform( - [&event](EventListener listener) { - return listener((SDL_EventType) event->type, event); - }); + std::vector results; + for (auto& listener : this->eventListeners.at(type)) { + results.emplace_back(listener((SDL_EventType) event->type, event)); + } if (std::ranges::contains(results, SDL_APP_FAILURE)) return SDL_APP_FAILURE; if (std::ranges::contains(results, SDL_APP_SUCCESS)) diff --git a/src/GameInternal.cpp b/src/GameInternal.cpp index d4aea1a..30ec531 100644 --- a/src/GameInternal.cpp +++ b/src/GameInternal.cpp @@ -50,6 +50,7 @@ SDL_AppResult GameInternal::init() GameInternal::soundManager = new SoundManager(); GameInternal::collisionHandler = new CollisionHandler(manager); // why does this use a referrence, but AssetManager a pointer? GameInternal::inputManager = new InputManager(); + GameInternal::inputManager->initKeyMap(); GameInternal::renderManager = new RenderManager(); GameInternal::eventManager = new EventManager(); diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 6542ec3..519df21 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -101,12 +101,8 @@ std::ostream& operator<<(std::ostream& os, InputManager::Key key) { std::ostream& operator<<(std::ostream& os, const InputManager::InputAction& action) { os << action.name << " with binding(s): "; - for (size_t i = 0; i < action.bindings.size(); ++i) { - os << action.bindings[i]; - - if (i < action.bindings.size() - 1) { - os << ", "; - } + for (auto& binding : action.bindings) { + os << binding << ", "; } return os; } @@ -168,13 +164,6 @@ InputManager::InputManager() : activeContext("Default") {} InputManager::~InputManager() {} -void InputManager::init() { - if (SDL_Init(SDL_INIT_EVENTS) != 0) { - std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl; - return; - } -} - void InputManager::initKeyMap() { keyMap = { {Key::UP, SDL_SCANCODE_UP}, @@ -259,81 +248,25 @@ void InputManager::initKeyMap() { }; } -void InputManager::registerAction(const std::string& actionName, const std::vector& keys, std::function callback, const std::string& context) { - actionsByContext[context].emplace_back(actionName, keys, callback); - InputAction& storedAction = actionsByContext[context].back(); +void InputManager::registerAction(const std::string& actionName, const std::vector& keys, std::function callback, const std::string& context) { + InputAction* storedAction = new InputAction{actionName, keys, callback}; for (const auto& key : keys) { - actionsByKey[key].push_back(&storedAction); + actionsByContextAndKey[context][key].emplace_back(storedAction); } std::cout << "Registered action: " << storedAction << " in context: " << context << std::endl; } -void InputManager::rebindAction(const std::string& actionName, const std::vector& newBindings, const std::string& context) { - auto it = actionsByContext.find(context); - if (it != actionsByContext.end()) { - for (auto& action : it->second) { - if (action.name == actionName) { - for (const auto& key : action.bindings) { - auto& keyActions = actionsByKey[key]; - keyActions.erase(std::remove(keyActions.begin(), keyActions.end(), &action), keyActions.end()); - if (keyActions.empty()) { - actionsByKey.erase(key); - } - } - - action.bindings = newBindings; - - for (const auto& key : newBindings) { - actionsByKey[key].push_back(&action); - } - - std::cout << "Rebound action: " << actionName << " in context: " << context << " to new bindings: "; - for (const auto& key : newBindings) { - std::cout << key << " "; - } - std::cout << std::endl; - return; - } - } - } - std::cout << "Action not found: " << actionName << " in context: " << context << std::endl; -} - -void InputManager::removeBindings(const std::string& actionName, const std::string& context) { - auto it = actionsByContext.find(context); - if (it != actionsByContext.end()) { - for (auto& action : it->second) { - if (action.name == actionName) { - action.bindings.clear(); - std::cout << "Removed bindings for action: " << actionName << " in context: " << context << std::endl; - return; - } - } - } - std::cout << "Action not found: " << actionName << " in context: " << context << std::endl; -} - -std::vector InputManager::getBindings(const std::string& actionName, const std::string& context) const { - auto it = actionsByContext.find(context); - if (it != actionsByContext.end()) { - for (const auto& action : it->second) { - if (action.name == actionName) { - return action.bindings; - } - } - } - std::cout << "Action not found: " << actionName << " in context: " << context << "\n"; - return {}; -} - std::vector InputManager::getActionsByKey(const Key key) const { - auto it = actionsByKey.find(key); - if (it != actionsByKey.end()) { - std::cout << "DEBUG: Found " << it->second.size() << " actions for key " << key << std::endl; - return it->second; + std::vector result; + + for (const auto& [context, keyMap] : actionsByContextAndKey) { + auto it = keyMap.find(key); + if (it != keyMap.end()) { + result.insert(result.end(), it->second.begin(), it->second.end()); + } } - std::cout << "DEBUG: No actions found for key " << key << std::endl; - return {}; + + return result; } void InputManager::setActiveContext(const std::string& context) { @@ -346,37 +279,22 @@ std::string InputManager::getActiveContext() const { } SDL_AppResult InputManager::handleEvent(SDL_EventType type, SDL_Event* const event) { - if (type != SDL_EVENT_KEY_DOWN) { - return SDL_APP_CONTINUE; - } - if (event->key.repeat) { return SDL_APP_CONTINUE; } - - auto keyIt = std::ranges::find_if(keyMap, - [&](const auto& pair) { return pair.second == event->key.scancode; }); + auto keyIt = std::ranges::find_if(keyMap, [&](const auto& pair) + { return pair.second == event->key.scancode; }); if (keyIt != keyMap.end()) { std::cout << "in != keymap.end" << std::endl; Key pressedKey = keyIt->first; - auto actionIt = actionsByKey.find(pressedKey); - if (actionIt != actionsByKey.end()) { - std::cout << "in != actionsByKey.end" << std::endl; - - for (auto* action : actionIt->second) { - std::cout << "before if(action)" << std::endl; - if (action) { - std::cout << "after if(action)" << std::endl; - - auto& activeActions = actionsByContext[activeContext]; - if (std::ranges::find_if(activeActions, - [&](const InputAction& act) { return &act == action; }) != activeActions.end()) { - std::cout << "Action triggered: " << action->name << " in context: " << activeContext << std::endl; - action->callback(); - } - } + auto keyActions = actionsByContextAndKey[activeContext]; + auto it = keyActions.find(pressedKey); + if (it != keyActions.end()) { + for (auto& action : it->second) { + std::cout << "Action triggered: " << action->name << " in context: " << activeContext << std::endl; + action->callback(type == SDL_EVENT_KEY_UP); } } } diff --git a/src/InteractionComponent.cpp b/src/InteractionComponent.cpp new file mode 100644 index 0000000..bd69ecb --- /dev/null +++ b/src/InteractionComponent.cpp @@ -0,0 +1,22 @@ +#include "InteractionComponent.h" + +#include "VEGO.h" + +InteractionComponent::InteractionComponent(std::function callback) : interactionCallback(callback) +{ + VEGO_Game().interactionManager->registerListener(this->entity->getComponentAsPointer()); +} + +void InteractionComponent::interact(void* actor, void* data) +{ + if (interactionCallback) { + interactionCallback(actor, data); + } +} +std::shared_ptr InteractionComponent::getPosition() // required for targeting strategy, return null to only allow explicit targeting +{ + if (entity->hasComponent()) { + return std::make_shared(entity->getComponent().position); + } + return nullptr; +} \ No newline at end of file diff --git a/src/InteractionEventdataStruct.cpp b/src/InteractionEventdataStruct.cpp new file mode 100644 index 0000000..bdbb81a --- /dev/null +++ b/src/InteractionEventdataStruct.cpp @@ -0,0 +1,16 @@ +#include "InteractionEventdataStruct.h" + +#include "VEGO.h" +#include "VEGO_Event.h" + +void InteractionEventdataStruct::triggerEvent() +{ + // TODO: if target is null && strategy is 0, error + + SDL_Event event; + SDL_zero(event); + event.type = vego::VEGO_Event_Interaction; + event.user.data1 = this; + + SDL_PushEvent(&event); +} \ No newline at end of file diff --git a/src/TransformComponent.cpp b/src/TransformComponent.cpp index 12e5ff2..a67dd1d 100644 --- a/src/TransformComponent.cpp +++ b/src/TransformComponent.cpp @@ -42,6 +42,9 @@ void TransformComponent::init() void TransformComponent::update(uint_fast16_t diffTime) { + direction.x = direction.x > 0 ? 1 : direction.x < 0 ? -1 : 0; + direction.y = direction.y > 0 ? 1 : direction.y < 0 ? -1 : 0; + float multiplier = direction.x != 0 && direction.y != 0 ? 0.707 : 1; // normalizes vector; only works if directions are in increments of 45° Vector2D positionChange( direction.x * this->getSpeed() * multiplier * diffTime * (1.f/1000),