#include "player_input.hpp" #include "godot_macros.h" #include #include #include #include #include namespace utils { void PlayerInput::_bind_methods() { #define CLASSNAME PlayerInput } gd::Vector2 PlayerInput::lastMouseMotion{0.f, 0.f}; bool PlayerInput::primaryExists{false}; PlayerInput::Listener::Listener(gd::String negative, gd::String positive, gd::Callable callable) : actionNegative{negative} , actionPositive{positive} , callable{callable} , isMouseEvent{positive.begins_with("_mouse_") || negative.begins_with("_mouse_")} {} PlayerInput::Listener::Listener(gd::String action, gd::Callable callable) : PlayerInput::Listener::Listener(gd::String(), action, callable) {} std::optional PlayerInput::Listener::evaluate_action(gd::String const &action) { gd::Input *input = gd::Input::get_singleton(); if(action.begins_with("_mouse_")) { gd::Vector2 vector = PlayerInput::get_last_mouse_motion(); if(action.ends_with("_up")) return vector.y > 0.f ? vector.y : 0.f; else if(action.ends_with("_down")) return vector.y < 0.f ? -vector.y : 0.f; else if(action.ends_with("_right")) return vector.x > 0.f ? vector.x : 0.f; else if(action.ends_with("_left")) return vector.x < 0.f ? -vector.x : 0.f; } if(action.is_empty()) { return 0.f; } else { return float(input->is_action_pressed(action)); } } bool PlayerInput::Listener::has_changed(gd::Ref const &event) { bool const mouse_changed{this->isMouseEvent && event->is_class("InputEventMouseMotion")}; bool const negative_changed{!this->actionNegative.is_empty() && event->is_action(this->actionNegative)}; bool const positive_changed{!this->actionPositive.is_empty() && event->is_action(this->actionPositive)}; return mouse_changed || negative_changed || positive_changed; } float PlayerInput::Listener::evaluate(gd::Ref const &event) { std::optional positive = PlayerInput::Listener::evaluate_action(this->actionPositive); std::optional negative = PlayerInput::Listener::evaluate_action(this->actionNegative); if(!positive.has_value() || !negative.has_value()) return 0.f; float newest = positive.value() - negative.value(); if(this->lastCached != newest || this->isMouseEvent) this->callable.call(event, newest); return (this->lastCached = newest); } bool PlayerInput::Listener::operator==(PlayerInput::Listener const& b) const { return this->callable == b.callable && this->actionNegative == b.actionNegative && this->actionPositive == b.actionPositive; } gd::Vector2 PlayerInput::get_last_mouse_motion() { return PlayerInput::lastMouseMotion; } void PlayerInput::_enter_tree() { GDGAMEONLY(); if(!PlayerInput::primaryExists) { this->isPrimary = true; PlayerInput::primaryExists = true; } } void PlayerInput::_exit_tree() { GDGAMEONLY(); if(this->isPrimary) { this->isPrimary = false; PlayerInput::primaryExists = false; } } void PlayerInput::_unhandled_input(gd::Ref const &event) { GDGAMEONLY(); if(this->isPrimary && event->is_class("InputEventMouseMotion")) PlayerInput::lastMouseMotion = gd::Object::cast_to(*event)->get_relative(); for(Listener& listener: this->listeners) { if(listener.has_changed(event)) { listener.evaluate(event); } } } void PlayerInput::_process(double deltaTime) { if(this->isPrimary) PlayerInput::lastMouseMotion = {0.f, 0.f}; } void PlayerInput::listen_to(Listener const& listener) { this->listeners.push_back(listener); } void PlayerInput::listen_to(gd::String action, gd::Callable callable) { this->listeners.push_back(Listener(action, callable)); } void PlayerInput::listen_to(gd::String negative, gd::String positive, gd::Callable callable) { this->listeners.push_back(Listener(negative, positive, callable)); } void PlayerInput::stop_listening(Node *node) { for(size_t i = 0; i < this->listeners.size(); ++i) { Listener l = this->listeners.get(i); if(l.callable.get_object() == node) { this->listeners.remove_at(i); i--; } } } void PlayerInput::stop_listening(Listener const& listener) { this->listeners.erase(listener); } void PlayerInput::clear_listeners() { this->listeners.clear(); } }