summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/RichTextLabel.xml55
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp2
-rw-r--r--drivers/vulkan/vulkan_context.cpp9
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs34
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs2
-rw-r--r--modules/mono/glue/runtime_interop.cpp10
-rw-r--r--scene/2d/navigation_agent_2d.cpp142
-rw-r--r--scene/2d/navigation_agent_2d.h1
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp19
-rw-r--r--scene/3d/navigation_agent_3d.cpp138
-rw-r--r--scene/3d/navigation_agent_3d.h13
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp19
-rw-r--r--scene/gui/rich_text_label.cpp46
-rw-r--r--scene/gui/rich_text_label.h5
16 files changed, 440 insertions, 79 deletions
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 1ecc8a1d4e..49bb65b64d 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -106,6 +106,45 @@
<return type="PopupMenu" />
<description>
Returns the [PopupMenu] of this [RichTextLabel]. By default, this menu is displayed when right-clicking on the [RichTextLabel].
+ You can add custom menu items or remove standard ones. Make sure your IDs don't conflict with the standard ones (see [enum MenuItems]). For example:
+ [codeblocks]
+ [gdscript]
+ func _ready():
+ var menu = get_menu()
+ # Remove "Select All" item.
+ menu.remove_item(MENU_SELECT_ALL)
+ # Add custom items.
+ menu.add_separator()
+ menu.add_item("Duplicate Text", MENU_MAX + 1)
+ # Connect callback.
+ menu.id_pressed.connect(_on_item_pressed)
+
+ func _on_item_pressed(id):
+ if id == MENU_MAX + 1:
+ add_text("\n" + get_parsed_text())
+ [/gdscript]
+ [csharp]
+ public override void _Ready()
+ {
+ var menu = GetMenu();
+ // Remove "Select All" item.
+ menu.RemoveItem(RichTextLabel.MenuItems.SelectAll);
+ // Add custom items.
+ menu.AddSeparator();
+ menu.AddItem("Duplicate Text", RichTextLabel.MenuItems.Max + 1);
+ // Add event handler.
+ menu.IdPressed += OnItemPressed;
+ }
+
+ public void OnItemPressed(int id)
+ {
+ if (id == TextEdit.MenuItems.Max + 1)
+ {
+ AddText("\n" + GetParsedText());
+ }
+ }
+ [/csharp]
+ [/codeblocks]
[b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member Window.visible] property.
</description>
</method>
@@ -193,6 +232,13 @@
If [member threaded] is enabled, returns [code]true[/code] if the background thread has finished text processing, otherwise always return [code]true[/code].
</description>
</method>
+ <method name="menu_option">
+ <return type="void" />
+ <param index="0" name="option" type="int" />
+ <description>
+ Executes a given action as defined in the [enum MenuItems] enum.
+ </description>
+ </method>
<method name="newline">
<return type="void" />
<description>
@@ -633,6 +679,15 @@
</constant>
<constant name="ITEM_CUSTOMFX" value="26" enum="ItemType">
</constant>
+ <constant name="MENU_COPY" value="0" enum="MenuItems">
+ Copies the selected text.
+ </constant>
+ <constant name="MENU_SELECT_ALL" value="1" enum="MenuItems">
+ Selects the whole [RichTextLabel] text.
+ </constant>
+ <constant name="MENU_MAX" value="2" enum="MenuItems">
+ Represents the size of the [enum MenuItems] enum.
+ </constant>
</constants>
<theme_items>
<theme_item name="default_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 672dcacd88..96278c3b92 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -179,7 +179,7 @@ typedef void (*DEBUGPROCARB)(GLenum source,
typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam);
void RasterizerGLES3::initialize() {
- print_line("OpenGL Renderer: " + RS::get_singleton()->get_video_adapter_name());
+ print_line(vformat("OpenGL API %s - Compatibility - Using Device: %s - %s", RS::get_singleton()->get_video_adapter_api_version(), RS::get_singleton()->get_video_adapter_vendor(), RS::get_singleton()->get_video_adapter_name()));
}
void RasterizerGLES3::finalize() {
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index d1391cb53e..864ad5931a 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -1299,8 +1299,15 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) {
// Get device version
device_api_version = gpu_props.apiVersion;
+ String rendering_method;
+ if (OS::get_singleton()->get_current_rendering_method() == "mobile") {
+ rendering_method = "Forward Mobile";
+ } else {
+ rendering_method = "Forward+";
+ }
+
// Output our device version
- print_line("Vulkan API " + get_device_api_version() + " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name);
+ print_line(vformat("Vulkan API %s - %s - Using Vulkan Device #%d: %s - %s", get_device_api_version(), rendering_method, device_index, device_vendor, device_name));
{
Error _err = _initialize_device_extensions();
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
index 344b76a202..02d0226e90 100644
--- a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
@@ -21,6 +21,26 @@ namespace GodotPlugins
_resolver = new AssemblyDependencyResolver(pluginPath);
_sharedAssemblies = sharedAssemblies;
_mainLoadContext = mainLoadContext;
+
+ if (string.IsNullOrEmpty(AppContext.BaseDirectory))
+ {
+ // See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs#L17-L35
+ // but Assembly.Location is unavailable, because we load assemblies from memory.
+ string? baseDirectory = Path.GetDirectoryName(pluginPath);
+ if (baseDirectory != null)
+ {
+ if (!Path.EndsInDirectorySeparator(baseDirectory))
+ baseDirectory += Path.PathSeparator;
+ // This SetData call effectively sets AppContext.BaseDirectory
+ // See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.cs#L21-L25
+ AppDomain.CurrentDomain.SetData("APP_CONTEXT_BASE_DIRECTORY", baseDirectory);
+ }
+ else
+ {
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine("Failed to set AppContext.BaseDirectory. Dynamic loading of libraries may fail.");
+ }
+ }
}
protected override Assembly? Load(AssemblyName assemblyName)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index 1e23689c95..8ac18c4314 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -459,6 +459,10 @@ namespace Godot.NativeInterop
public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self);
+ public static partial godot_bool godotsharp_node_path_equals(in godot_node_path p_self, in godot_node_path p_other);
+
+ public static partial int godotsharp_node_path_hash(in godot_node_path p_self);
+
// GD, etc
internal static partial void godotsharp_bytes_to_var(in godot_packed_byte_array p_bytes,
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index b02bd167a1..f216fb7ea3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -39,7 +39,7 @@ namespace Godot
/// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
/// </code>
/// </example>
- public sealed class NodePath : IDisposable
+ public sealed class NodePath : IDisposable, IEquatable<NodePath>
{
internal godot_node_path.movable NativeValue;
@@ -288,5 +288,37 @@ namespace Godot
/// </summary>
/// <returns>If the <see cref="NodePath"/> is empty.</returns>
public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
+
+ public static bool operator ==(NodePath left, NodePath right)
+ {
+ if (left is null)
+ return right is null;
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(NodePath left, NodePath right)
+ {
+ return !(left == right);
+ }
+
+ public bool Equals(NodePath other)
+ {
+ if (other is null)
+ return false;
+ var self = (godot_node_path)NativeValue;
+ var otherNative = (godot_node_path)other.NativeValue;
+ return NativeFuncs.godotsharp_node_path_equals(self, otherNative).ToBool();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || (obj is NodePath other && Equals(other));
+ }
+
+ public override int GetHashCode()
+ {
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_hash(self);
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index b9ee0bc278..97d28f9ee9 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -10,7 +10,7 @@ namespace Godot
/// Comparing them is much faster than with regular strings, because only the pointers are compared,
/// not the whole strings.
/// </summary>
- public sealed class StringName : IDisposable
+ public sealed class StringName : IDisposable, IEquatable<StringName>
{
internal godot_string_name.movable NativeValue;
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index d17fe3e75f..a90b07620c 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -1141,6 +1141,14 @@ bool godotsharp_node_path_is_absolute(const NodePath *p_self) {
return p_self->is_absolute();
}
+bool godotsharp_node_path_equals(const NodePath *p_self, const NodePath *p_other) {
+ return *p_self == *p_other;
+}
+
+int godotsharp_node_path_hash(const NodePath *p_self) {
+ return p_self->hash();
+}
+
void godotsharp_randomize() {
Math::randomize();
}
@@ -1477,6 +1485,8 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_node_path_get_subname,
(void *)godotsharp_node_path_get_subname_count,
(void *)godotsharp_node_path_is_absolute,
+ (void *)godotsharp_node_path_equals,
+ (void *)godotsharp_node_path_hash,
(void *)godotsharp_bytes_to_var,
(void *)godotsharp_convert,
(void *)godotsharp_hash,
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index ed6f3b8020..85f6840fde 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -168,6 +168,17 @@ void NavigationAgent2D::_notification(int p_what) {
set_physics_process_internal(false);
} break;
+ case NOTIFICATION_EXIT_TREE: {
+ set_agent_parent(nullptr);
+ set_physics_process_internal(false);
+
+#ifdef DEBUG_ENABLED
+ if (debug_path_instance.is_valid()) {
+ RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, false);
+ }
+#endif // DEBUG_ENABLED
+ } break;
+
case NOTIFICATION_PAUSED: {
if (agent_parent && !agent_parent->can_process()) {
map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid());
@@ -188,17 +199,6 @@ void NavigationAgent2D::_notification(int p_what) {
}
} break;
- case NOTIFICATION_EXIT_TREE: {
- agent_parent = nullptr;
- set_physics_process_internal(false);
-
-#ifdef DEBUG_ENABLED
- if (debug_path_instance.is_valid()) {
- RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, false);
- }
-#endif // DEBUG_ENABLED
- } break;
-
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent && target_position_submitted) {
if (avoidance_enabled) {
@@ -208,7 +208,6 @@ void NavigationAgent2D::_notification(int p_what) {
}
_check_distance_to_target();
}
-
#ifdef DEBUG_ENABLED
if (debug_path_dirty) {
_update_debug_path();
@@ -220,11 +219,11 @@ void NavigationAgent2D::_notification(int p_what) {
NavigationAgent2D::NavigationAgent2D() {
agent = NavigationServer2D::get_singleton()->agent_create();
- set_neighbor_distance(neighbor_distance);
- set_max_neighbors(max_neighbors);
- set_time_horizon(time_horizon);
- set_radius(radius);
- set_max_speed(max_speed);
+ NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
+ NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
+ NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+ NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
+ NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed);
// Preallocate query and result objects to improve performance.
navigation_query = Ref<NavigationPathQueryParameters2D>();
@@ -254,7 +253,12 @@ NavigationAgent2D::~NavigationAgent2D() {
}
void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) {
+ if (avoidance_enabled == p_enabled) {
+ return;
+ }
+
avoidance_enabled = p_enabled;
+
if (avoidance_enabled) {
NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
} else {
@@ -267,6 +271,10 @@ bool NavigationAgent2D::get_avoidance_enabled() const {
}
void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
+ if (agent_parent == p_agent_parent) {
+ return;
+ }
+
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable());
@@ -280,7 +288,9 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
}
// create new avoidance callback if enabled
- set_avoidance_enabled(avoidance_enabled);
+ if (avoidance_enabled) {
+ NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
+ }
} else {
agent_parent = nullptr;
NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID());
@@ -288,11 +298,13 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
}
void NavigationAgent2D::set_navigation_layers(uint32_t p_navigation_layers) {
- bool navigation_layers_changed = navigation_layers != p_navigation_layers;
- navigation_layers = p_navigation_layers;
- if (navigation_layers_changed) {
- _request_repath();
+ if (navigation_layers == p_navigation_layers) {
+ return;
}
+
+ navigation_layers = p_navigation_layers;
+
+ _request_repath();
}
uint32_t NavigationAgent2D::get_navigation_layers() const {
@@ -326,7 +338,12 @@ void NavigationAgent2D::set_path_metadata_flags(BitField<NavigationPathQueryPara
}
void NavigationAgent2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
map_override = p_navigation_map;
+
NavigationServer2D::get_singleton()->agent_set_map(agent, map_override);
_request_repath();
}
@@ -340,41 +357,78 @@ RID NavigationAgent2D::get_navigation_map() const {
return RID();
}
-void NavigationAgent2D::set_path_desired_distance(real_t p_dd) {
- path_desired_distance = p_dd;
+void NavigationAgent2D::set_path_desired_distance(real_t p_path_desired_distance) {
+ if (Math::is_equal_approx(path_desired_distance, p_path_desired_distance)) {
+ return;
+ }
+
+ path_desired_distance = p_path_desired_distance;
}
-void NavigationAgent2D::set_target_desired_distance(real_t p_dd) {
- target_desired_distance = p_dd;
+void NavigationAgent2D::set_target_desired_distance(real_t p_target_desired_distance) {
+ if (Math::is_equal_approx(target_desired_distance, p_target_desired_distance)) {
+ return;
+ }
+
+ target_desired_distance = p_target_desired_distance;
}
void NavigationAgent2D::set_radius(real_t p_radius) {
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
+
NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
}
void NavigationAgent2D::set_neighbor_distance(real_t p_distance) {
+ if (Math::is_equal_approx(neighbor_distance, p_distance)) {
+ return;
+ }
+
neighbor_distance = p_distance;
+
NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
}
void NavigationAgent2D::set_max_neighbors(int p_count) {
+ if (max_neighbors == p_count) {
+ return;
+ }
+
max_neighbors = p_count;
+
NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
void NavigationAgent2D::set_time_horizon(real_t p_time) {
+ if (Math::is_equal_approx(time_horizon, p_time)) {
+ return;
+ }
+
time_horizon = p_time;
+
NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
}
void NavigationAgent2D::set_max_speed(real_t p_max_speed) {
+ if (Math::is_equal_approx(max_speed, p_max_speed)) {
+ return;
+ }
+
max_speed = p_max_speed;
+
NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed);
}
-void NavigationAgent2D::set_path_max_distance(real_t p_pmd) {
- path_max_distance = p_pmd;
+void NavigationAgent2D::set_path_max_distance(real_t p_path_max_distance) {
+ if (Math::is_equal_approx(path_max_distance, p_path_max_distance)) {
+ return;
+ }
+
+ path_max_distance = p_path_max_distance;
}
real_t NavigationAgent2D::get_path_max_distance() {
@@ -382,8 +436,13 @@ real_t NavigationAgent2D::get_path_max_distance() {
}
void NavigationAgent2D::set_target_position(Vector2 p_position) {
+ if (target_position.is_equal_approx(p_position)) {
+ return;
+ }
+
target_position = p_position;
target_position_submitted = true;
+
_request_repath();
}
@@ -432,10 +491,15 @@ Vector2 NavigationAgent2D::get_final_position() {
}
void NavigationAgent2D::set_velocity(Vector2 p_velocity) {
+ if (target_velocity.is_equal_approx(p_velocity)) {
+ return;
+ }
+
target_velocity = p_velocity;
+ velocity_submitted = true;
+
NavigationServer2D::get_singleton()->agent_set_target_velocity(agent, target_velocity);
NavigationServer2D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
- velocity_submitted = true;
}
void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
@@ -608,6 +672,10 @@ void NavigationAgent2D::_check_distance_to_target() {
#ifdef DEBUG_ENABLED
void NavigationAgent2D::set_debug_enabled(bool p_enabled) {
+ if (debug_enabled == p_enabled) {
+ return;
+ }
+
debug_enabled = p_enabled;
debug_path_dirty = true;
}
@@ -617,6 +685,10 @@ bool NavigationAgent2D::get_debug_enabled() const {
}
void NavigationAgent2D::set_debug_use_custom(bool p_enabled) {
+ if (debug_use_custom == p_enabled) {
+ return;
+ }
+
debug_use_custom = p_enabled;
debug_path_dirty = true;
}
@@ -626,6 +698,10 @@ bool NavigationAgent2D::get_debug_use_custom() const {
}
void NavigationAgent2D::set_debug_path_custom_color(Color p_color) {
+ if (debug_path_custom_color == p_color) {
+ return;
+ }
+
debug_path_custom_color = p_color;
debug_path_dirty = true;
}
@@ -635,6 +711,10 @@ Color NavigationAgent2D::get_debug_path_custom_color() const {
}
void NavigationAgent2D::set_debug_path_custom_point_size(float p_point_size) {
+ if (Math::is_equal_approx(debug_path_custom_point_size, p_point_size)) {
+ return;
+ }
+
debug_path_custom_point_size = MAX(0.1, p_point_size);
debug_path_dirty = true;
}
@@ -644,6 +724,10 @@ float NavigationAgent2D::get_debug_path_custom_point_size() const {
}
void NavigationAgent2D::set_debug_path_custom_line_width(float p_line_width) {
+ if (Math::is_equal_approx(debug_path_custom_line_width, p_line_width)) {
+ return;
+ }
+
debug_path_custom_line_width = p_line_width;
debug_path_dirty = true;
}
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 8f4a373327..5278c81f66 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -57,7 +57,6 @@ class NavigationAgent2D : public Node {
int max_neighbors = 10;
real_t time_horizon = 1.0;
real_t max_speed = 100.0;
-
real_t path_max_distance = 100.0;
Vector2 target_position;
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 4bd170301a..d7ef77e25b 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -185,6 +185,10 @@ real_t NavigationObstacle2D::estimate_agent_radius() const {
}
void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) {
+ if (parent_node2d == p_agent_parent) {
+ return;
+ }
+
if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
parent_node2d = Object::cast_to<Node2D>(p_agent_parent);
if (map_override.is_valid()) {
@@ -200,7 +204,12 @@ void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) {
}
void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
map_override = p_navigation_map;
+
NavigationServer2D::get_singleton()->agent_set_map(agent, map_override);
}
@@ -214,13 +223,23 @@ RID NavigationObstacle2D::get_navigation_map() const {
}
void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
+ if (estimate_radius == p_estimate_radius) {
+ return;
+ }
+
estimate_radius = p_estimate_radius;
+
notify_property_list_changed();
reevaluate_agent_radius();
}
void NavigationObstacle2D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
+
reevaluate_agent_radius();
}
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 1ffabbb6da..524304425c 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -207,7 +207,7 @@ void NavigationAgent3D::_notification(int p_what) {
if (avoidance_enabled) {
// agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
// no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
- NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
}
_check_distance_to_target();
}
@@ -222,12 +222,12 @@ void NavigationAgent3D::_notification(int p_what) {
NavigationAgent3D::NavigationAgent3D() {
agent = NavigationServer3D::get_singleton()->agent_create();
- set_neighbor_distance(50.0);
- set_max_neighbors(10);
- set_time_horizon(5.0);
- set_radius(1.0);
- set_max_speed(10.0);
- set_ignore_y(true);
+ NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
+ NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+ NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
+ NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed);
+ NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y);
// Preallocate query and result objects to improve performance.
navigation_query = Ref<NavigationPathQueryParameters3D>();
@@ -260,7 +260,12 @@ NavigationAgent3D::~NavigationAgent3D() {
}
void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) {
+ if (avoidance_enabled == p_enabled) {
+ return;
+ }
+
avoidance_enabled = p_enabled;
+
if (avoidance_enabled) {
NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
} else {
@@ -273,6 +278,10 @@ bool NavigationAgent3D::get_avoidance_enabled() const {
}
void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
+ if (agent_parent == p_agent_parent) {
+ return;
+ }
+
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
@@ -286,7 +295,9 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
}
// create new avoidance callback if enabled
- set_avoidance_enabled(avoidance_enabled);
+ if (avoidance_enabled) {
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
+ }
} else {
agent_parent = nullptr;
NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
@@ -294,11 +305,13 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
}
void NavigationAgent3D::set_navigation_layers(uint32_t p_navigation_layers) {
- bool navigation_layers_changed = navigation_layers != p_navigation_layers;
- navigation_layers = p_navigation_layers;
- if (navigation_layers_changed) {
- _request_repath();
+ if (navigation_layers == p_navigation_layers) {
+ return;
}
+
+ navigation_layers = p_navigation_layers;
+
+ _request_repath();
}
uint32_t NavigationAgent3D::get_navigation_layers() const {
@@ -332,7 +345,12 @@ void NavigationAgent3D::set_path_metadata_flags(BitField<NavigationPathQueryPara
}
void NavigationAgent3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
map_override = p_navigation_map;
+
NavigationServer3D::get_singleton()->agent_set_map(agent, map_override);
_request_repath();
}
@@ -346,50 +364,96 @@ RID NavigationAgent3D::get_navigation_map() const {
return RID();
}
-void NavigationAgent3D::set_path_desired_distance(real_t p_dd) {
- path_desired_distance = p_dd;
+void NavigationAgent3D::set_path_desired_distance(real_t p_path_desired_distance) {
+ if (Math::is_equal_approx(path_desired_distance, p_path_desired_distance)) {
+ return;
+ }
+
+ path_desired_distance = p_path_desired_distance;
}
-void NavigationAgent3D::set_target_desired_distance(real_t p_dd) {
- target_desired_distance = p_dd;
+void NavigationAgent3D::set_target_desired_distance(real_t p_target_desired_distance) {
+ if (Math::is_equal_approx(target_desired_distance, p_target_desired_distance)) {
+ return;
+ }
+
+ target_desired_distance = p_target_desired_distance;
}
void NavigationAgent3D::set_radius(real_t p_radius) {
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
+
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
}
-void NavigationAgent3D::set_agent_height_offset(real_t p_hh) {
- navigation_height_offset = p_hh;
+void NavigationAgent3D::set_agent_height_offset(real_t p_agent_height_offset) {
+ if (Math::is_equal_approx(navigation_height_offset, p_agent_height_offset)) {
+ return;
+ }
+
+ navigation_height_offset = p_agent_height_offset;
}
void NavigationAgent3D::set_ignore_y(bool p_ignore_y) {
+ if (ignore_y == p_ignore_y) {
+ return;
+ }
+
ignore_y = p_ignore_y;
+
NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y);
}
void NavigationAgent3D::set_neighbor_distance(real_t p_distance) {
+ if (Math::is_equal_approx(neighbor_distance, p_distance)) {
+ return;
+ }
+
neighbor_distance = p_distance;
+
NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
}
void NavigationAgent3D::set_max_neighbors(int p_count) {
+ if (max_neighbors == p_count) {
+ return;
+ }
+
max_neighbors = p_count;
+
NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
void NavigationAgent3D::set_time_horizon(real_t p_time) {
+ if (Math::is_equal_approx(time_horizon, p_time)) {
+ return;
+ }
+
time_horizon = p_time;
+
NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
}
void NavigationAgent3D::set_max_speed(real_t p_max_speed) {
+ if (Math::is_equal_approx(max_speed, p_max_speed)) {
+ return;
+ }
+
max_speed = p_max_speed;
+
NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed);
}
-void NavigationAgent3D::set_path_max_distance(real_t p_pmd) {
- path_max_distance = p_pmd;
+void NavigationAgent3D::set_path_max_distance(real_t p_path_max_distance) {
+ if (Math::is_equal_approx(path_max_distance, p_path_max_distance)) {
+ return;
+ }
+
+ path_max_distance = p_path_max_distance;
}
real_t NavigationAgent3D::get_path_max_distance() {
@@ -397,8 +461,13 @@ real_t NavigationAgent3D::get_path_max_distance() {
}
void NavigationAgent3D::set_target_position(Vector3 p_position) {
+ if (target_position.is_equal_approx(p_position)) {
+ return;
+ }
+
target_position = p_position;
target_position_submitted = true;
+
_request_repath();
}
@@ -412,7 +481,7 @@ Vector3 NavigationAgent3D::get_next_path_position() {
const Vector<Vector3> &navigation_path = navigation_result->get_path();
if (navigation_path.size() == 0) {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
- return agent_parent->get_global_transform().origin;
+ return agent_parent->get_global_position();
} else {
return navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0);
}
@@ -420,7 +489,7 @@ Vector3 NavigationAgent3D::get_next_path_position() {
real_t NavigationAgent3D::distance_to_target() const {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent.");
- return agent_parent->get_global_transform().origin.distance_to(target_position);
+ return agent_parent->get_global_position().distance_to(target_position);
}
bool NavigationAgent3D::is_target_reached() const {
@@ -447,10 +516,15 @@ Vector3 NavigationAgent3D::get_final_position() {
}
void NavigationAgent3D::set_velocity(Vector3 p_velocity) {
+ if (target_velocity.is_equal_approx(p_velocity)) {
+ return;
+ }
+
target_velocity = p_velocity;
+ velocity_submitted = true;
+
NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, target_velocity);
NavigationServer3D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
- velocity_submitted = true;
}
void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) {
@@ -491,7 +565,7 @@ void NavigationAgent3D::update_navigation() {
update_frame_id = Engine::get_singleton()->get_physics_frames();
- Vector3 origin = agent_parent->get_global_transform().origin;
+ Vector3 origin = agent_parent->get_global_position();
bool reload_path = false;
@@ -624,6 +698,10 @@ void NavigationAgent3D::_check_distance_to_target() {
#ifdef DEBUG_ENABLED
void NavigationAgent3D::set_debug_enabled(bool p_enabled) {
+ if (debug_enabled == p_enabled) {
+ return;
+ }
+
debug_enabled = p_enabled;
debug_path_dirty = true;
}
@@ -633,6 +711,10 @@ bool NavigationAgent3D::get_debug_enabled() const {
}
void NavigationAgent3D::set_debug_use_custom(bool p_enabled) {
+ if (debug_use_custom == p_enabled) {
+ return;
+ }
+
debug_use_custom = p_enabled;
debug_path_dirty = true;
}
@@ -642,6 +724,10 @@ bool NavigationAgent3D::get_debug_use_custom() const {
}
void NavigationAgent3D::set_debug_path_custom_color(Color p_color) {
+ if (debug_path_custom_color == p_color) {
+ return;
+ }
+
debug_path_custom_color = p_color;
debug_path_dirty = true;
}
@@ -651,6 +737,10 @@ Color NavigationAgent3D::get_debug_path_custom_color() const {
}
void NavigationAgent3D::set_debug_path_custom_point_size(float p_point_size) {
+ if (Math::is_equal_approx(debug_path_custom_point_size, p_point_size)) {
+ return;
+ }
+
debug_path_custom_point_size = p_point_size;
debug_path_dirty = true;
}
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index 98bf395d7c..209b2a0989 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -52,14 +52,13 @@ class NavigationAgent3D : public Node {
real_t path_desired_distance = 1.0;
real_t target_desired_distance = 1.0;
- real_t radius = 0.0;
+ real_t radius = 1.0;
real_t navigation_height_offset = 0.0;
- bool ignore_y = false;
- real_t neighbor_distance = 0.0;
- int max_neighbors = 0;
- real_t time_horizon = 0.0;
- real_t max_speed = 0.0;
-
+ bool ignore_y = true;
+ real_t neighbor_distance = 50.0;
+ int max_neighbors = 10;
+ real_t time_horizon = 5.0;
+ real_t max_speed = 10.0;
real_t path_max_distance = 3.0;
Vector3 target_position;
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index c706f55566..85b3c164cc 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -192,6 +192,10 @@ real_t NavigationObstacle3D::estimate_agent_radius() const {
}
void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) {
+ if (parent_node3d == p_agent_parent) {
+ return;
+ }
+
if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
parent_node3d = Object::cast_to<Node3D>(p_agent_parent);
if (map_override.is_valid()) {
@@ -207,7 +211,12 @@ void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) {
}
void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
map_override = p_navigation_map;
+
NavigationServer3D::get_singleton()->agent_set_map(agent, map_override);
}
@@ -221,13 +230,23 @@ RID NavigationObstacle3D::get_navigation_map() const {
}
void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) {
+ if (estimate_radius == p_estimate_radius) {
+ return;
+ }
+
estimate_radius = p_estimate_radius;
+
notify_property_list_changed();
reevaluate_agent_radius();
}
void NavigationObstacle3D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
+
reevaluate_agent_radius();
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 3051502dd0..f9c9906efa 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2031,7 +2031,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
}
if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
- _generate_context_menu();
+ _update_context_menu();
menu->set_position(get_screen_position() + b->get_position());
menu->reset_size();
menu->popup();
@@ -2090,7 +2090,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
if (k->is_action("ui_menu", true)) {
if (context_menu_enabled) {
- _generate_context_menu();
+ _update_context_menu();
menu->set_position(get_screen_position());
menu->reset_size();
menu->popup();
@@ -4992,7 +4992,9 @@ bool RichTextLabel::is_shortcut_keys_enabled() const {
// Context menu.
PopupMenu *RichTextLabel::get_menu() const {
- const_cast<RichTextLabel *>(this)->_generate_context_menu();
+ if (!menu) {
+ const_cast<RichTextLabel *>(this)->_generate_context_menu();
+ }
return menu;
}
@@ -5466,6 +5468,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu);
ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible);
+ ClassDB::bind_method(D_METHOD("menu_option", "option"), &RichTextLabel::menu_option);
ClassDB::bind_method(D_METHOD("_thread_end"), &RichTextLabel::_thread_end);
@@ -5544,6 +5547,10 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_HINT);
BIND_ENUM_CONSTANT(ITEM_DROPCAP);
BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
+
+ BIND_ENUM_CONSTANT(MENU_COPY);
+ BIND_ENUM_CONSTANT(MENU_SELECT_ALL);
+ BIND_ENUM_CONSTANT(MENU_MAX);
}
TextServer::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const {
@@ -5675,19 +5682,32 @@ Size2 RichTextLabel::get_minimum_size() const {
// Context menu.
void RichTextLabel::_generate_context_menu() {
- if (!menu) {
- menu = memnew(PopupMenu);
- add_child(menu, false, INTERNAL_MODE_FRONT);
+ menu = memnew(PopupMenu);
+ add_child(menu, false, INTERNAL_MODE_FRONT);
+ menu->connect("id_pressed", callable_mp(this, &RichTextLabel::menu_option));
+
+ menu->add_item(RTR("Copy"), MENU_COPY);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL);
+}
- menu->connect("id_pressed", callable_mp(this, &RichTextLabel::_menu_option));
+void RichTextLabel::_update_context_menu() {
+ if (!menu) {
+ _generate_context_menu();
}
- // Reorganize context menu.
- menu->clear();
- if (selection.enabled) {
- menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE);
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
+ int idx = -1;
+
+#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \
+ idx = m_menu->get_item_index(m_id); \
+ if (idx >= 0) { \
+ m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \
+ m_menu->set_item_disabled(idx, m_disabled); \
}
+
+ MENU_ITEM_ACTION_DISABLED(menu, MENU_COPY, "ui_copy", !selection.enabled)
+ MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selection.enabled)
+
+#undef MENU_ITEM_ACTION_DISABLED
}
Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) {
@@ -5715,7 +5735,7 @@ Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) {
}
}
-void RichTextLabel::_menu_option(int p_option) {
+void RichTextLabel::menu_option(int p_option) {
switch (p_option) {
case MENU_COPY: {
selection_copy();
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index fcbb91f67e..b01fccf14c 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -81,6 +81,7 @@ public:
enum MenuItems {
MENU_COPY,
MENU_SELECT_ALL,
+ MENU_MAX
};
enum DefaultFont {
@@ -454,8 +455,8 @@ private:
// Context menu.
PopupMenu *menu = nullptr;
void _generate_context_menu();
+ void _update_context_menu();
Key _get_menu_action_accelerator(const String &p_action);
- void _menu_option(int p_option);
int visible_characters = -1;
float visible_ratio = 1.0;
@@ -688,6 +689,7 @@ public:
// Context menu.
PopupMenu *get_menu() const;
bool is_menu_visible() const;
+ void menu_option(int p_option);
void parse_bbcode(const String &p_bbcode);
void append_text(const String &p_bbcode);
@@ -739,5 +741,6 @@ public:
VARIANT_ENUM_CAST(RichTextLabel::ListType);
VARIANT_ENUM_CAST(RichTextLabel::ItemType);
+VARIANT_ENUM_CAST(RichTextLabel::MenuItems);
#endif // RICH_TEXT_LABEL_H