diff --git a/core/input_map.cpp b/core/input_map.cpp index ee94657ed1f8..0f41c8ab9b25 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -51,7 +51,7 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); ClassDB::bind_method(D_METHOD("get_action_list", "action"), &InputMap::_get_action_list); - ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action); + ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false)); ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals); } @@ -125,7 +125,7 @@ List InputMap::get_actions() const { return actions; } -List>::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +List>::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List>::Element *E = p_action.inputs.front(); E; E = E->next()) { @@ -136,7 +136,9 @@ List>::Element *InputMap::_find_event(Action &p_action, const Re int device = e->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + if (p_exact_match && e->shortcut_match(p_event)) { + return E; + } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -165,8 +167,8 @@ void InputMap::action_add_event(const StringName &p_action, const Ref &p_event) { ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action)); - return (_find_event(input_map[p_action], p_event) != nullptr); + return (_find_event(input_map[p_action], p_event, true) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref &p_event) { ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); - List>::Element *E = _find_event(input_map[p_action], p_event); + List>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { input_map[p_action].inputs.erase(E); if (Input::get_singleton()->is_action_pressed(p_action)) { @@ -217,11 +219,11 @@ const List> *InputMap::get_action_list(const StringName &p_actio return &E->get().inputs; } -bool InputMap::event_is_action(const Ref &p_event, const StringName &p_action) const { - return event_get_action_status(p_event, p_action); +bool InputMap::event_is_action(const Ref &p_event, const StringName &p_action, bool p_exact_match) const { + return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { Map::Element *E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action)); @@ -239,7 +241,7 @@ bool InputMap::event_get_action_status(const Ref &p_event, const Str bool pressed; float strength; float raw_strength; - List>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength); + List>::Element *event = _find_event(E->get(), p_event, p_exact_match, &pressed, &strength, &raw_strength); if (event != nullptr) { if (p_pressed != nullptr) { *p_pressed = pressed; diff --git a/core/input_map.h b/core/input_map.h index c2aa131882f1..fa87dcafe5a5 100644 --- a/core/input_map.h +++ b/core/input_map.h @@ -54,7 +54,7 @@ class InputMap : public Object { mutable Map input_map; - List>::Element *_find_event(Action &p_action, const Ref &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + List>::Element *_find_event(Action &p_action, const Ref &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; Array _get_action_list(const StringName &p_action); Array _get_actions(); @@ -79,8 +79,8 @@ class InputMap : public Object { void action_erase_events(const StringName &p_action); const List> *get_action_list(const StringName &p_action); - bool event_is_action(const Ref &p_event, const StringName &p_action) const; - bool event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + bool event_is_action(const Ref &p_event, const StringName &p_action, bool p_exact_match = false) const; + bool event_get_action_status(const Ref &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; const Map &get_action_map() const; void load_from_globals(); diff --git a/core/os/input.cpp b/core/os/input.cpp index eb046ab645d3..42816200dcd5 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -57,11 +57,11 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_key_pressed", "scancode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); - ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action"), &Input::get_action_raw_strength); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact"), &Input::is_action_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact"), &Input::is_action_just_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact"), &Input::is_action_just_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact"), &Input::get_action_raw_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f)); ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); diff --git a/core/os/input.h b/core/os/input.h index 346732355c77..86791546f470 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -81,11 +81,11 @@ class Input : public Object { virtual bool is_key_pressed(int p_scancode) const = 0; virtual bool is_mouse_button_pressed(int p_button) const = 0; virtual bool is_joy_button_pressed(int p_device, int p_button) const = 0; - virtual bool is_action_pressed(const StringName &p_action) const = 0; - virtual bool is_action_just_pressed(const StringName &p_action) const = 0; - virtual bool is_action_just_released(const StringName &p_action) const = 0; - virtual float get_action_strength(const StringName &p_action) const = 0; - virtual float get_action_raw_strength(const StringName &p_action) const = 0; + virtual bool is_action_pressed(const StringName &p_action, bool p_exact = false) const = 0; + virtual bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const = 0; + virtual bool is_action_just_released(const StringName &p_action, bool p_exact = false) const = 0; + virtual float get_action_strength(const StringName &p_action, bool p_exact = false) const = 0; + virtual float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const = 0; float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const; Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index a47e7cc07537..3203194a548d 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -44,31 +44,31 @@ int InputEvent::get_device() const { return device; } -bool InputEvent::is_action(const StringName &p_action) const { - return InputMap::get_singleton()->event_is_action(Ref((InputEvent *)this), p_action); +bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const { + return InputMap::get_singleton()->event_is_action(Ref((InputEvent *)this), p_action, p_exact_match); } -bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const { +bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && pressed && (p_allow_echo || !is_echo()); } -bool InputEvent::is_action_released(const StringName &p_action) const { +bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && !pressed; } -float InputEvent::get_action_strength(const StringName &p_action) const { +float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const { float strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, nullptr, &strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, p_exact_match, nullptr, &strength, nullptr); return valid ? strength : 0.0f; } -float InputEvent::get_action_raw_strength(const StringName &p_action) const { +float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match) const { float raw_strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, p_exact_match, nullptr, nullptr, &raw_strength); return valid ? raw_strength : 0.0f; } @@ -104,10 +104,10 @@ void InputEvent::_bind_methods() { ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device); ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device); - ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo"), &InputEvent::is_action_pressed, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action", "action", "exact_match"), &InputEvent::is_action, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed); ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo); diff --git a/core/os/input_event.h b/core/os/input_event.h index 50f83d613a69..c599b62a0864 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -198,11 +198,11 @@ class InputEvent : public Resource { void set_device(int p_device); int get_device() const; - bool is_action(const StringName &p_action) const; - bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const; - bool is_action_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action(const StringName &p_action, bool p_exact_match = false) const; + bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const; + bool is_action_released(const StringName &p_action, bool p_exact_match = false) const; + float get_action_strength(const StringName &p_action, bool p_exact_match = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const; // To be removed someday, since they do not make sense for all events virtual bool is_pressed() const; diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index aca494d72b14..b12734736290 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -59,8 +59,11 @@ + + Returns a value between 0 and 1 representing the raw intensity of the given action, ignoring the action's deadzone. In most cases, you should use [method get_action_strength] instead. + If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -68,8 +71,11 @@ + + Returns a value between 0 and 1 representing the intensity of the given action. In a joypad, for example, the further away the axis (analog sticks or L2, R2 triggers) is from the dead zone, the closer the value will be to 1. If the action is mapped to a control that has no axis as the keyboard, the value returned will be 0 or 1. + If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -250,9 +256,12 @@ + + Returns [code]true[/code] when the user starts pressing the action event, meaning it's [code]true[/code] only on the frame that the user pressed down the button. This is useful for code that needs to run only once when an action is pressed, instead of every frame while it's pressed. + If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -260,8 +269,11 @@ + + Returns [code]true[/code] when the user stops pressing the action event, meaning it's [code]true[/code] only on the frame that the user released the button. + If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -269,8 +281,11 @@ + + Returns [code]true[/code] if you are pressing the action event. Note that if an action has multiple buttons assigned and more than one of them is pressed, releasing one button will release the action, even if some other button assigned to this action is still pressed. + If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index a23852ea288b..600bececd8ac 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -35,8 +35,11 @@ + + Returns a value between 0.0 and 1.0 depending on the given actions' state. Useful for getting the value of events of type [InputEventJoypadMotion]. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -44,8 +47,11 @@ + + Returns [code]true[/code] if this input event matches a pre-defined action of any type. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -55,8 +61,11 @@ + + Returns [code]true[/code] if the given action is being pressed (and is not an echo event for [InputEventKey] events, unless [code]allow_echo[/code] is [code]true[/code]). Not relevant for events of type [InputEventMouseMotion] or [InputEventScreenDrag]. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. @@ -64,8 +73,11 @@ + + Returns [code]true[/code] if the given action is released (i.e. not pressed). Not relevant for events of type [InputEventMouseMotion] or [InputEventScreenDrag]. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml index 55cc699db4a1..2692d3e70ff3 100644 --- a/doc/classes/InputMap.xml +++ b/doc/classes/InputMap.xml @@ -100,8 +100,11 @@ + + Returns [code]true[/code] if the given event is part of an existing action. This method ignores keyboard modifiers if the given [InputEvent] is not pressed (for proper release detection). See [method action_has_event] if you don't want this behavior. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. diff --git a/main/input_default.cpp b/main/input_default.cpp index e439bbdfbd79..72965f43b380 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -90,15 +90,15 @@ bool InputDefault::is_joy_button_pressed(int p_device, int p_button) const { return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } -bool InputDefault::is_action_pressed(const StringName &p_action) const { +bool InputDefault::is_action_pressed(const StringName &p_action, bool p_exact) const { #ifdef DEBUG_ENABLED bool has_action = InputMap::get_singleton()->has_action(p_action); ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); #endif - return action_state.has(p_action) && action_state[p_action].pressed; + return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true); } -bool InputDefault::is_action_just_pressed(const StringName &p_action) const { +bool InputDefault::is_action_just_pressed(const StringName &p_action, bool p_exact) const { #ifdef DEBUG_ENABLED bool has_action = InputMap::get_singleton()->has_action(p_action); ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); @@ -108,6 +108,10 @@ bool InputDefault::is_action_just_pressed(const StringName &p_action) const { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -115,7 +119,7 @@ bool InputDefault::is_action_just_pressed(const StringName &p_action) const { } } -bool InputDefault::is_action_just_released(const StringName &p_action) const { +bool InputDefault::is_action_just_released(const StringName &p_action, bool p_exact) const { #ifdef DEBUG_ENABLED bool has_action = InputMap::get_singleton()->has_action(p_action); ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); @@ -125,6 +129,10 @@ bool InputDefault::is_action_just_released(const StringName &p_action) const { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -132,7 +140,7 @@ bool InputDefault::is_action_just_released(const StringName &p_action) const { } } -float InputDefault::get_action_strength(const StringName &p_action) const { +float InputDefault::get_action_strength(const StringName &p_action, bool p_exact) const { #ifdef DEBUG_ENABLED bool has_action = InputMap::get_singleton()->has_action(p_action); ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); @@ -142,10 +150,14 @@ float InputDefault::get_action_strength(const StringName &p_action) const { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().strength; } -float InputDefault::get_action_raw_strength(const StringName &p_action) const { +float InputDefault::get_action_raw_strength(const StringName &p_action, bool p_exact) const { #ifdef DEBUG_ENABLED bool has_action = InputMap::get_singleton()->has_action(p_action); ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); @@ -155,6 +167,10 @@ float InputDefault::get_action_raw_strength(const StringName &p_action) const { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().raw_strength; } @@ -462,14 +478,15 @@ void InputDefault::_parse_input_event_impl(const Ref &p_event, bool for (const Map::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { - // Save the action's state - if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { + // If not echo and action pressed state has changed + if (!p_event->is_echo() && is_action_pressed(E->key(), false) != p_event->is_action_pressed(E->key())) { Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = p_event->is_action_pressed(E->key()); action.strength = 0.0f; action.raw_strength = 0.0f; + action.exact = InputMap::get_singleton()->event_is_action(p_event, E->key(), true); action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); diff --git a/main/input_default.h b/main/input_default.h index 973af6ef413a..dcafb4bb38c3 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -54,6 +54,7 @@ class InputDefault : public Input { uint64_t physics_frame; uint64_t idle_frame; bool pressed; + bool exact; float strength; float raw_strength; }; @@ -221,11 +222,11 @@ class InputDefault : public Input { virtual bool is_key_pressed(int p_scancode) const; virtual bool is_mouse_button_pressed(int p_button) const; virtual bool is_joy_button_pressed(int p_device, int p_button) const; - virtual bool is_action_pressed(const StringName &p_action) const; - virtual bool is_action_just_pressed(const StringName &p_action) const; - virtual bool is_action_just_released(const StringName &p_action) const; - virtual float get_action_strength(const StringName &p_action) const; - virtual float get_action_raw_strength(const StringName &p_action) const; + virtual bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; + virtual bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const; + virtual bool is_action_just_released(const StringName &p_action, bool p_exact = false) const; + virtual float get_action_strength(const StringName &p_action, bool p_exact = false) const; + virtual float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const; virtual float get_joy_axis(int p_device, int p_axis) const; String get_joy_name(int p_idx);