#ifndef UTILS_PLAYER_INPUT_HPP #define UTILS_PLAYER_INPUT_HPP #include #include #include #include #include #include namespace gd = godot; namespace utils { /*! An event-driven input observer. * * Listen for events with `listen_to`, registering godot input action names to callbacks. It's possible to register an "axis" by registering a listener with a positive and negative action. */ class PlayerInput : public gd::Node { GDCLASS(PlayerInput, gd::Node) static void _bind_methods(); public: /*! A PlayerInput action listener. * * A listener is a combination of a positive and negative action and a callable. * The expected callable signature is `void (godot::Ref event, float value)` * actions can also be "special" actions prefixed with `_`. * Special actions include `_mouse_up`, `_mouse_down`, `_mouse_left`, and `_mouse_right`. */ struct Listener { friend class PlayerInput; private: //! Negative action on axis, evaluates to -1. gd::String actionNegative{""}; //! Positive action on axis, evaluates to +1. gd::String actionPositive{""}; /*! The last cached action. * * If the newest result matches this, the event will be considered duplicate and ignored (not passed to listener) */ float lastCached{0.f}; //! The listening function. gd::Callable callable; //! If either actionNegative or actionPositive is a _mouse_ event this will be true bool isMouseEvent{false}; public: Listener() = default; Listener(gd::String negative, gd::String positive, gd::Callable callable); Listener(gd::String action, gd::Callable callable); // evaluate the current state of an action. static std::optional evaluate_action(gd::String const &action); /*! Check if this event has any chance to result in a trigger. * * Does not evaluate the event or poll current input state */ bool has_changed(gd::Ref const &event); //! evaluate the event for changes to either actionPositive or actionNegative float evaluate(gd::Ref const &event); bool operator==(PlayerInput::Listener const& b) const; }; public: //! Returns the last stored mouse delta. static gd::Vector2 get_last_mouse_motion(); virtual void _enter_tree() override; virtual void _exit_tree() override; virtual void _unhandled_input(gd::Ref const &event) override; virtual void _process(double deltaTime) override; //! Start listening for action. void listen_to(Listener const &listener); /*! Start listening for action. * * Shorthand for `listen_to(Listener(action, callable))`. */ void listen_to(gd::String action, gd::Callable callable); /*! Start listening for action. * * Shorthand for `listen_to(Listener(negative, positive, callable))`. */ void listen_to(gd::String negative, gd::String positive, gd::Callable callable); //! Remove any listeners related to node. void stop_listening(Node *node); //! Remove listeners exactly equal to listener. void stop_listening(Listener const &listener); //! Remove all listeners. void clear_listeners(); //! set the device observe events from. void set_device(int id); private: //! The last mouse motion, updated by the primary instance static gd::Vector2 lastMouseMotion; //! Does a primary instance exist static bool primaryExists; /*! Is this the primary instance. * * The primary instance is responsible for updating static variables like lastMouseMotion. */ bool isPrimary{false}; //! which device to observe events from. int device{-1}; //! current listeners for this instance gd::Vector listeners{}; }; } #endif // !UTILS_PLAYER_INPUT_HPP