126 lines
		
	
	
		
			4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "player_input.hpp"
 | 
						|
#include "godot_macros.h"
 | 
						|
#include "godot_cpp/classes/input.hpp"
 | 
						|
#include "godot_cpp/classes/input_event.hpp"
 | 
						|
#include "godot_cpp/classes/input_event_mouse_motion.hpp"
 | 
						|
#include <algorithm>
 | 
						|
#include <optional>
 | 
						|
 | 
						|
namespace godot {
 | 
						|
void PlayerInput::_bind_methods() {}
 | 
						|
 | 
						|
Vector2 PlayerInput::lastMouseMotion{0.f, 0.f};
 | 
						|
bool PlayerInput::primaryExists{false};
 | 
						|
 | 
						|
PlayerInput::Listener::Listener(String positive, String negative, Node *object, String method)
 | 
						|
: actionNegative{negative}
 | 
						|
, actionPositive{positive}
 | 
						|
, methodName{method}
 | 
						|
, object{object}
 | 
						|
, isMouseEvent{positive.begins_with("_mouse_") || negative.begins_with("_mouse_")} {}
 | 
						|
 | 
						|
std::optional<float> PlayerInput::Listener::evaluate_action(String const &action) {
 | 
						|
    Input *input = Input::get_singleton();
 | 
						|
    if(action.begins_with("_mouse_")) {
 | 
						|
        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;
 | 
						|
        else
 | 
						|
            return std::nullopt;
 | 
						|
    } else if(action.is_empty()) {
 | 
						|
        return 0.f;
 | 
						|
    } else {
 | 
						|
        return float(input->is_action_pressed(action));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool PlayerInput::Listener::has_changed(Ref<InputEvent> const &event) {
 | 
						|
    return (
 | 
						|
        (!event->is_class("InputEventMouseMotion") ||
 | 
						|
            this->isMouseEvent) ||
 | 
						|
        event->is_action(this->actionNegative) ||
 | 
						|
        event->is_action(this->actionPositive)
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
float PlayerInput::Listener::evaluate(Ref<InputEvent> const &event) {
 | 
						|
    std::optional<float> positive = PlayerInput::Listener::evaluate_action(this->actionPositive);
 | 
						|
    std::optional<float> 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->object->call(this->methodName, event, newest);
 | 
						|
    return (this->lastCached = newest);
 | 
						|
}
 | 
						|
 | 
						|
bool PlayerInput::Listener::operator==(godot::PlayerInput::Listener const& b) {
 | 
						|
    return this->methodName == b.methodName
 | 
						|
        && this->object == b.object
 | 
						|
        && this->actionNegative == b.actionNegative
 | 
						|
        && this->actionPositive == b.actionPositive;
 | 
						|
}
 | 
						|
 | 
						|
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(Ref<InputEvent> const &event) {
 | 
						|
    GDGAMEONLY();
 | 
						|
    if(this->isPrimary && event->is_class("InputEventMouseMotion"))
 | 
						|
        PlayerInput::lastMouseMotion = Object::cast_to<InputEventMouseMotion>(*event)->get_relative();
 | 
						|
    for(Listener& listener: this->listeners) {
 | 
						|
        if(listener.has_changed(event)) {
 | 
						|
            listener.evaluate(event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void PlayerInput::_process(double deltaTime) {
 | 
						|
    PlayerInput::lastMouseMotion = {0.f, 0.f};
 | 
						|
}
 | 
						|
 | 
						|
void PlayerInput::listen_to(Listener const& listener) {
 | 
						|
    this->listeners.push_back(listener);
 | 
						|
}
 | 
						|
 | 
						|
void PlayerInput::stop_listening(Node *node) {
 | 
						|
    for(size_t i = 0; i < this->listeners.size(); ++i) {
 | 
						|
        Listener& l = this->listeners.at(i);
 | 
						|
        if(l.object == node) {
 | 
						|
            this->listeners.erase(this->listeners.begin() + i);
 | 
						|
            i--;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void PlayerInput::stop_listening(Listener const& listener) {
 | 
						|
    std::vector<Listener>::iterator itr = std::find(this->listeners.begin(), this->listeners.end(), listener);
 | 
						|
    if(itr != this->listeners.end())
 | 
						|
        this->listeners.erase(itr);
 | 
						|
}
 | 
						|
}
 | 
						|
 |