summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--CONTRIBUTING.md16
-rw-r--r--SConstruct5
-rw-r--r--core/math/a_star.cpp14
-rw-r--r--doc/classes/AStar.xml3
-rw-r--r--doc/classes/AStar2D.xml3
-rw-r--r--doc/classes/Dictionary.xml1
-rw-r--r--doc/classes/File.xml35
-rw-r--r--editor/plugins/script_editor_plugin.cpp1
-rw-r--r--editor/scene_tree_dock.cpp4
-rw-r--r--methods.py11
-rw-r--r--scene/gui/tree.cpp21
-rw-r--r--scene/main/node.cpp61
13 files changed, 126 insertions, 52 deletions
diff --git a/.gitignore b/.gitignore
index dfb1490aa9..ca27e42016 100644
--- a/.gitignore
+++ b/.gitignore
@@ -340,6 +340,9 @@ platform/windows/godot_res.res
# Visual Studio Code workspace file
*.code-workspace
+# Scons construction environment dump
+.scons_env.json
+
# Scons progress indicator
.scons_node_count
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ba04008680..c28692c34f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -119,6 +119,22 @@ for an introduction to developing on Godot.
The [Contributing docs](https://docs.godotengine.org/en/latest/community/contributing/index.html)
also have important information on the PR workflow and the code style we use.
+### Document your changes
+
+If your pull request adds methods, properties or signals that are exposed to
+scripting APIs, you **must** update the class reference to document those.
+This is to ensure the documentation coverage doesn't decrease as contributions
+are merged.
+
+[Update the documentation template](https://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html#updating-the-documentation-template)
+using your compiled binary, then fill in the descriptions.
+Follow the style guide described in the
+[Docs writing guidelines](https://docs.godotengine.org/en/latest/community/contributing/docs_writing_guidelines.html).
+
+If your pull request modifies parts of the code in a non-obvious way, make sure
+to add comments in the code as well. This helps other people understand the
+change without having to look at `git blame`.
+
### Be nice to the Git history
Try to make simple PRs that handle one specific topic. Just like for reporting
diff --git a/SConstruct b/SConstruct
index b424363dc8..9496595a26 100644
--- a/SConstruct
+++ b/SConstruct
@@ -693,6 +693,9 @@ elif selected_platform != "":
else:
Exit(255)
-# The following only makes sense when the env is defined, and assumes it is
+# The following only makes sense when the 'env' is defined, and assumes it is.
if "env" in locals():
methods.show_progress(env)
+ # TODO: replace this with `env.Dump(format="json")`
+ # once we start requiring SCons 4.0 as min version.
+ methods.dump(env)
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index 580a7cf7bb..30f712b2c3 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -280,10 +280,16 @@ int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) co
continue; // Disabled points should not be considered.
}
+ // Keep the closest point's ID, and in case of multiple closest IDs,
+ // the smallest one (makes it deterministic).
real_t d = p_point.distance_squared_to((*it.value)->pos);
- if (closest_id < 0 || d < closest_dist) {
+ int id = *(it.key);
+ if (d <= closest_dist) {
+ if (d == closest_dist && id > closest_id) { // Keep lowest ID.
+ continue;
+ }
closest_dist = d;
- closest_id = *(it.key);
+ closest_id = id;
}
}
@@ -291,7 +297,6 @@ int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) co
}
Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
- bool found = false;
real_t closest_dist = 1e20;
Vector3 closest_point;
@@ -311,10 +316,9 @@ Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
Vector3 p = Geometry3D::get_closest_point_to_segment(p_point, segment);
real_t d = p_point.distance_squared_to(p);
- if (!found || d < closest_dist) {
+ if (d < closest_dist) {
closest_point = p;
closest_dist = d;
- found = true;
}
}
diff --git a/doc/classes/AStar.xml b/doc/classes/AStar.xml
index e930abba87..2695e86f47 100644
--- a/doc/classes/AStar.xml
+++ b/doc/classes/AStar.xml
@@ -131,7 +131,8 @@
<argument index="1" name="include_disabled" type="bool" default="false">
</argument>
<description>
- Returns the ID of the closest point to [code]to_position[/code], optionally taking disabled points into account. Returns -1 if there are no points in the points pool.
+ Returns the ID of the closest point to [code]to_position[/code], optionally taking disabled points into account. Returns [code]-1[/code] if there are no points in the points pool.
+ [b]Note:[/b] If several points are the closest to [code]to_position[/code], the one with the smallest ID will be returned, ensuring a deterministic result.
</description>
</method>
<method name="get_closest_position_in_segment" qualifiers="const">
diff --git a/doc/classes/AStar2D.xml b/doc/classes/AStar2D.xml
index 16fa05041e..622d336ef6 100644
--- a/doc/classes/AStar2D.xml
+++ b/doc/classes/AStar2D.xml
@@ -114,7 +114,8 @@
<argument index="1" name="include_disabled" type="bool" default="false">
</argument>
<description>
- Returns the ID of the closest point to [code]to_position[/code], optionally taking disabled points into account. Returns -1 if there are no points in the points pool.
+ Returns the ID of the closest point to [code]to_position[/code], optionally taking disabled points into account. Returns [code]-1[/code] if there are no points in the points pool.
+ [b]Note:[/b] If several points are the closest to [code]to_position[/code], the one with the smallest ID will be returned, ensuring a deterministic result.
</description>
</method>
<method name="get_closest_position_in_segment" qualifiers="const">
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index 385f4b7e59..4538e5ea4e 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -148,6 +148,7 @@
# The line below prints `true`, whereas it would have printed `false` if both variables were compared directly.
print(dict1.hash() == dict2.hash())
[/codeblock]
+ [b]Note:[/b] Dictionaries with the same keys/values but in a different order will have a different hash.
</description>
</method>
<method name="keys">
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index 17c65731ff..b90039e496 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -54,28 +54,28 @@
<return type="int">
</return>
<description>
- Returns the next 16 bits from the file as an integer.
+ Returns the next 16 bits from the file as an integer. See [method store_16] for details on what values can be stored and retrieved this way.
</description>
</method>
<method name="get_32" qualifiers="const">
<return type="int">
</return>
<description>
- Returns the next 32 bits from the file as an integer.
+ Returns the next 32 bits from the file as an integer. See [method store_32] for details on what values can be stored and retrieved this way.
</description>
</method>
<method name="get_64" qualifiers="const">
<return type="int">
</return>
<description>
- Returns the next 64 bits from the file as an integer.
+ Returns the next 64 bits from the file as an integer. See [method store_64] for details on what values can be stored and retrieved this way.
</description>
</method>
<method name="get_8" qualifiers="const">
<return type="int">
</return>
<description>
- Returns the next 8 bits from the file as an integer.
+ Returns the next 8 bits from the file as an integer. See [method store_8] for details on what values can be stored and retrieved this way.
</description>
</method>
<method name="get_as_text" qualifiers="const">
@@ -297,7 +297,26 @@
</argument>
<description>
Stores an integer as 16 bits in the file.
- [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 2^16 - 1][/code].
+ [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 2^16 - 1][/code]. Any other value will overflow and wrap around.
+ To store a signed integer, use [method store_64] or store a signed integer from the interval [code][-2^15, 2^15 - 1][/code] (i.e. keeping one bit for the signedness) and compute its sign manually when reading. For example:
+ [codeblock]
+ const MAX_15B = 1 &lt;&lt; 15
+ const MAX_16B = 1 &lt;&lt; 16
+
+ func unsigned16_to_signed(unsigned):
+ return (unsigned + MAX_15B) % MAX_16B - MAX_15B
+
+ func _ready():
+ var f = File.new()
+ f.open("user://file.dat", File.WRITE_READ)
+ f.store_16(-42) # This wraps around and stores 65494 (2^16 - 42).
+ f.store_16(121) # In bounds, will store 121.
+ f.seek(0) # Go back to start to read the stored value.
+ var read1 = f.get_16() # 65494
+ var read2 = f.get_16() # 121
+ var converted1 = unsigned16_to_signed(read1) # -42
+ var converted2 = unsigned16_to_signed(read2) # 121
+ [/codeblock]
</description>
</method>
<method name="store_32">
@@ -307,7 +326,8 @@
</argument>
<description>
Stores an integer as 32 bits in the file.
- [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 2^32 - 1][/code].
+ [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 2^32 - 1][/code]. Any other value will overflow and wrap around.
+ To store a signed integer, use [method store_64], or convert it manually (see [method store_16] for an example).
</description>
</method>
<method name="store_64">
@@ -327,7 +347,8 @@
</argument>
<description>
Stores an integer as 8 bits in the file.
- [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 255][/code].
+ [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 255][/code]. Any other value will overflow and wrap around.
+ To store a signed integer, use [method store_64], or convert it manually (see [method store_16] for an example).
</description>
</method>
<method name="store_buffer">
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 8d6dac3907..48a9febcf9 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -837,7 +837,6 @@ void ScriptEditor::_file_dialog_action(String p_file) {
Error err;
FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err);
if (err) {
- memdelete(file);
editor->show_warning(TTR("Error writing TextFile:") + "\n" + p_file, TTR("Error!"));
break;
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index c37d32b26b..5795d85e66 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2658,7 +2658,9 @@ void SceneTreeDock::_remote_tree_selected() {
}
void SceneTreeDock::_local_tree_selected() {
- scene_tree->show();
+ if (!bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) || get_tree()->get_edited_scene_root() != nullptr) {
+ scene_tree->show();
+ }
if (remote_tree) {
remote_tree->hide();
}
diff --git a/methods.py b/methods.py
index 756db19e9a..ca6756f95f 100644
--- a/methods.py
+++ b/methods.py
@@ -803,3 +803,14 @@ def show_progress(env):
progress_finish_command = Command("progress_finish", [], progress_finish)
AlwaysBuild(progress_finish_command)
+
+
+def dump(env):
+ # Dumps latest build information for debugging purposes and external tools.
+ from json import dump
+
+ def non_serializable(obj):
+ return "<<non-serializable: %s>>" % (type(obj).__qualname__)
+
+ with open(".scons_env.json", "w") as f:
+ dump(env.Dictionary(), f, indent=4, default=non_serializable)
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 45fcb448f8..7b9db7c081 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2364,7 +2364,6 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
if (pos.x < len) {
cache.hover_type = Cache::CLICK_TITLE;
cache.hover_index = i;
- update();
break;
}
}
@@ -2383,6 +2382,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
mpos.y += v_scroll->get_value();
}
+ TreeItem *old_it = cache.hover_item;
+ int old_col = cache.hover_cell;
+
int col, h, section;
TreeItem *it = _find_item_at_pos(root, mpos, col, h, section);
@@ -2397,18 +2399,21 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
}
- if (it != cache.hover_item) {
- cache.hover_item = it;
- update();
- }
+ cache.hover_item = it;
+ cache.hover_cell = col;
- if (it && col != cache.hover_cell) {
- cache.hover_cell = col;
- update();
+ if (it != old_it || col != old_col) {
+ // Only need to update if mouse enters/exits a button
+ bool was_over_button = old_it && old_it->cells[old_col].custom_button;
+ bool is_over_button = it && it->cells[col].custom_button;
+ if (was_over_button || is_over_button) {
+ update();
+ }
}
}
}
+ // Update if mouse enters/exits columns
if (cache.hover_type != old_hover || cache.hover_index != old_index) {
update();
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index c9d430c656..1bf828a03b 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2238,46 +2238,53 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p
// because re-targeting of connections from some descendant to another is not possible
// if the emitter node comes later in tree order than the receiver
void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
- if (this != p_original && (get_owner() != p_original && get_owner() != p_original->get_owner())) {
+ if ((this != p_original) && !(p_original->is_a_parent_of(this))) {
return;
}
- List<Connection> conns;
- get_all_signal_connections(&conns);
+ List<const Node *> process_list;
+ process_list.push_back(this);
+ while (!process_list.empty()) {
+ const Node *n = process_list.front()->get();
+ process_list.pop_front();
- for (List<Connection>::Element *E = conns.front(); E; E = E->next()) {
- if (E->get().flags & CONNECT_PERSIST) {
- //user connected
- NodePath p = p_original->get_path_to(this);
- Node *copy = p_copy->get_node(p);
+ List<Connection> conns;
+ n->get_all_signal_connections(&conns);
- Node *target = Object::cast_to<Node>(E->get().callable.get_object());
- if (!target) {
- continue;
- }
- NodePath ptarget = p_original->get_path_to(target);
+ for (List<Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().flags & CONNECT_PERSIST) {
+ //user connected
+ NodePath p = p_original->get_path_to(n);
+ Node *copy = p_copy->get_node(p);
- Node *copytarget = target;
+ Node *target = Object::cast_to<Node>(E->get().callable.get_object());
+ if (!target) {
+ continue;
+ }
+ NodePath ptarget = p_original->get_path_to(target);
- // Attempt to find a path to the duplicate target, if it seems it's not part
- // of the duplicated and not yet parented hierarchy then at least try to connect
- // to the same target as the original
+ Node *copytarget = target;
- if (p_copy->has_node(ptarget)) {
- copytarget = p_copy->get_node(ptarget);
- }
+ // Attempt to find a path to the duplicate target, if it seems it's not part
+ // of the duplicated and not yet parented hierarchy then at least try to connect
+ // to the same target as the original
- if (copy && copytarget) {
- const Callable copy_callable = Callable(copytarget, E->get().callable.get_method());
- if (!copy->is_connected(E->get().signal.get_name(), copy_callable)) {
- copy->connect(E->get().signal.get_name(), copy_callable, E->get().binds, E->get().flags);
+ if (p_copy->has_node(ptarget)) {
+ copytarget = p_copy->get_node(ptarget);
+ }
+
+ if (copy && copytarget) {
+ const Callable copy_callable = Callable(copytarget, E->get().callable.get_method());
+ if (!copy->is_connected(E->get().signal.get_name(), copy_callable)) {
+ copy->connect(E->get().signal.get_name(), copy_callable, E->get().binds, E->get().flags);
+ }
}
}
}
- }
- for (int i = 0; i < get_child_count(); i++) {
- get_child(i)->_duplicate_signals(p_original, p_copy);
+ for (int i = 0; i < n->get_child_count(); i++) {
+ process_list.push_back(n->get_child(i));
+ }
}
}