diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2ec0c742787..e13a606805a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -56,12 +56,11 @@ /editor/icons/ @godotengine/usability /editor/import/ @godotengine/import /editor/inspector/ @godotengine/docks -/editor/scene/ @godotengine/core /editor/scene/2d/ @godotengine/2d-editor /editor/scene/2d/physics @godotengine/2d-editor @godotengine/physics /editor/scene/3d/ @godotengine/3d-editor /editor/scene/3d/physics @godotengine/3d-editor @godotengine/physics -/editor/scene/gui/ @godotengine/usability @godotengine/gui-nodes +/editor/scene/gui/ @godotengine/gui-nodes /editor/themes/ @godotengine/usability @godotengine/gui-nodes /editor/translations/ @godotengine/usability diff --git a/core/input/input.cpp b/core/input/input.cpp index eee8ca18370..bd14ec0a4c3 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -125,6 +125,8 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact_match"), &Input::is_action_pressed, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact_match"), &Input::is_action_just_pressed, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact_match"), &Input::is_action_just_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_pressed_by_event", "action", "event", "exact_match"), &Input::is_action_just_pressed_by_event, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_released_by_event", "action", "event", "exact_match"), &Input::is_action_just_released_by_event, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_raw_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); @@ -413,6 +415,37 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con } } +bool Input::is_action_just_pressed_by_event(const StringName &p_action, const Ref &p_event, bool p_exact) const { + ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); + ERR_FAIL_COND_V(p_event.is_null(), false); + + if (disable_input) { + return false; + } + + HashMap::ConstIterator E = action_states.find(p_action); + if (!E) { + return false; + } + + if (p_exact && E->value.exact == false) { + return false; + } + + if (E->value.pressed_event_id != p_event->get_instance_id()) { + return false; + } + + // Backward compatibility for legacy behavior, only return true if currently pressed. + bool pressed_requirement = legacy_just_pressed_behavior ? E->value.cache.pressed : true; + + if (Engine::get_singleton()->is_in_physics_frame()) { + return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames(); + } else { + return pressed_requirement && E->value.pressed_process_frame == Engine::get_singleton()->get_process_frames(); + } +} + bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); @@ -439,6 +472,37 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co } } +bool Input::is_action_just_released_by_event(const StringName &p_action, const Ref &p_event, bool p_exact) const { + ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); + ERR_FAIL_COND_V(p_event.is_null(), false); + + if (disable_input) { + return false; + } + + HashMap::ConstIterator E = action_states.find(p_action); + if (!E) { + return false; + } + + if (p_exact && E->value.exact == false) { + return false; + } + + if (E->value.released_event_id != p_event->get_instance_id()) { + return false; + } + + // Backward compatibility for legacy behavior, only return true if currently released. + bool released_requirement = legacy_just_pressed_behavior ? !E->value.cache.pressed : true; + + if (Engine::get_singleton()->is_in_physics_frame()) { + return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames(); + } else { + return released_requirement && E->value.released_process_frame == Engine::get_singleton()->get_process_frames(); + } +} + float Input::get_action_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); @@ -897,10 +961,12 @@ void Input::_parse_input_event_impl(const Ref &p_event, bool p_is_em _update_action_cache(E.key, action_state); // As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick. if (action_state.cache.pressed && !was_pressed) { + action_state.pressed_event_id = p_event->get_instance_id(); action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames() + 1; action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames(); } if (!action_state.cache.pressed && was_pressed) { + action_state.released_event_id = p_event->get_instance_id(); action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames() + 1; action_state.released_process_frame = Engine::get_singleton()->get_process_frames(); } @@ -1029,6 +1095,7 @@ void Input::action_press(const StringName &p_action, float p_strength) { // As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick. if (!action_state.cache.pressed) { + action_state.pressed_event_id = ObjectID(); action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames() + 1; action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames(); } @@ -1047,6 +1114,7 @@ void Input::action_release(const StringName &p_action) { action_state.cache.strength = 0.0; action_state.cache.raw_strength = 0.0; // As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick. + action_state.released_event_id = ObjectID(); action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames() + 1; action_state.released_process_frame = Engine::get_singleton()->get_process_frames(); action_state.device_states.clear(); diff --git a/core/input/input.h b/core/input/input.h index d9a8c3823f1..2ed018b5c7d 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -111,6 +111,8 @@ class Input : public Object { uint64_t pressed_process_frame = UINT64_MAX; uint64_t released_physics_frame = UINT64_MAX; uint64_t released_process_frame = UINT64_MAX; + ObjectID pressed_event_id; + ObjectID released_event_id; bool exact = true; struct DeviceState { @@ -308,6 +310,8 @@ class Input : public Object { bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const; bool is_action_just_released(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_pressed_by_event(const StringName &p_action, const Ref &p_event, bool p_exact = false) const; + bool is_action_just_released_by_event(const StringName &p_action, const Ref &p_event, bool p_exact = false) const; float get_action_strength(const StringName &p_action, bool p_exact = false) const; float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const; diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 59a6e6a12e2..750ad812e9f 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -300,6 +300,12 @@ Variant Resource::_duplicate_recursive(const Variant &p_variant, const Duplicate DEV_ASSERT(false); } } + if (should_duplicate) { + Ref