diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index be581e825e4..df0db9a1843 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -306,11 +306,17 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const { return closest_point; } -bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_path) { +bool AStar3D::_solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path) { last_closest_point = nullptr; pass++; - if (!end_point->enabled && !p_allow_partial_path) { + if (!p_begin_point->enabled) { + return false; + } + if (p_begin_point == p_end_point) { + return true; + } + if (!p_end_point->enabled && !p_allow_partial_path) { return false; } @@ -319,11 +325,11 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_ LocalVector open_list; SortArray sorter; - begin_point->g_score = 0; - begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); - begin_point->abs_g_score = 0; - begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id); - open_list.push_back(begin_point); + p_begin_point->g_score = 0; + p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id); + p_begin_point->abs_g_score = 0; + p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id); + open_list.push_back(p_begin_point); while (!open_list.is_empty()) { Point *p = open_list[0]; // The currently processed point. @@ -333,7 +339,7 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_ last_closest_point = p; } - if (p == end_point) { + if (p == p_end_point) { found_route = true; break; } @@ -370,7 +376,7 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_ e->prev_point = p; e->g_score = tentative_g_score; - e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); + e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id); e->abs_g_score = tentative_g_score; e->abs_f_score = e->f_score - e->g_score; @@ -428,12 +434,6 @@ Vector AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool ERR_FAIL_COND_V_MSG(!b_entry, Vector(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id)); Point *b = *b_entry; - if (a == b) { - Vector ret; - ret.push_back(a->pos); - return ret; - } - Point *begin_point = a; Point *end_point = b; @@ -482,16 +482,6 @@ Vector AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_ ERR_FAIL_COND_V_MSG(!b_entry, Vector(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id)); Point *b = *b_entry; - if (a == b) { - Vector ret; - ret.push_back(a->id); - return ret; - } - - if (!a->enabled) { - return Vector(); - } - Point *begin_point = a; Point *end_point = b; @@ -738,11 +728,6 @@ Vector AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool ERR_FAIL_COND_V_MSG(!b_entry, Vector(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id)); AStar3D::Point *b = *b_entry; - if (a == b) { - Vector ret = { Vector2(a->pos.x, a->pos.y) }; - return ret; - } - AStar3D::Point *begin_point = a; AStar3D::Point *end_point = b; @@ -791,16 +776,6 @@ Vector AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_ ERR_FAIL_COND_V_MSG(!to_entry, Vector(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id)); AStar3D::Point *b = *to_entry; - if (a == b) { - Vector ret; - ret.push_back(a->id); - return ret; - } - - if (!a->enabled) { - return Vector(); - } - AStar3D::Point *begin_point = a; AStar3D::Point *end_point = b; @@ -840,11 +815,17 @@ Vector AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_ return path; } -bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path) { +bool AStar2D::_solve(AStar3D::Point *p_begin_point, AStar3D::Point *p_end_point, bool p_allow_partial_path) { astar.last_closest_point = nullptr; astar.pass++; - if (!end_point->enabled && !p_allow_partial_path) { + if (!p_begin_point->enabled) { + return false; + } + if (p_begin_point == p_end_point) { + return true; + } + if (!p_end_point->enabled && !p_allow_partial_path) { return false; } @@ -853,11 +834,11 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo LocalVector open_list; SortArray sorter; - begin_point->g_score = 0; - begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); - begin_point->abs_g_score = 0; - begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id); - open_list.push_back(begin_point); + p_begin_point->g_score = 0; + p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id); + p_begin_point->abs_g_score = 0; + p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id); + open_list.push_back(p_begin_point); while (!open_list.is_empty()) { AStar3D::Point *p = open_list[0]; // The currently processed point. @@ -867,7 +848,7 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo astar.last_closest_point = p; } - if (p == end_point) { + if (p == p_end_point) { found_route = true; break; } @@ -904,7 +885,7 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo e->prev_point = p; e->g_score = tentative_g_score; - e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); + e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id); e->abs_g_score = tentative_g_score; e->abs_f_score = e->f_score - e->g_score; diff --git a/core/math/a_star.h b/core/math/a_star.h index 612dfe32a49..048ead784c3 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -113,7 +113,7 @@ class AStar3D : public RefCounted { Point *last_closest_point = nullptr; bool neighbor_filter_enabled = false; - bool _solve(Point *begin_point, Point *end_point, bool p_allow_partial_path); + bool _solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path); protected: static void _bind_methods(); @@ -172,7 +172,7 @@ class AStar2D : public RefCounted { GDCLASS(AStar2D, RefCounted); AStar3D astar; - bool _solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path); + bool _solve(AStar3D::Point *p_begin_point, AStar3D::Point *p_end_point, bool p_allow_partial_path); protected: static void _bind_methods(); diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 2548957f833..9a6e804a9a1 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -495,6 +495,12 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point, bool p_allow_ last_closest_point = nullptr; pass++; + if (_get_solid_unchecked(p_begin_point->id)) { + return false; + } + if (p_begin_point == p_end_point) { + return true; + } if (_get_solid_unchecked(p_end_point->id) && !p_allow_partial_path) { return false; } @@ -636,17 +642,8 @@ Vector AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region)); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region)); - Point *a = _get_point(p_from_id.x, p_from_id.y); - Point *b = _get_point(p_to_id.x, p_to_id.y); - - if (a == b) { - Vector ret; - ret.push_back(a->pos); - return ret; - } - - Point *begin_point = a; - Point *end_point = b; + Point *begin_point = _get_point(p_from_id.x, p_from_id.y); + Point *end_point = _get_point(p_to_id.x, p_to_id.y); bool found_route = _solve(begin_point, end_point, p_allow_partial_path); if (!found_route) { @@ -689,17 +686,8 @@ TypedArray AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region)); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region)); - Point *a = _get_point(p_from_id.x, p_from_id.y); - Point *b = _get_point(p_to_id.x, p_to_id.y); - - if (a == b) { - TypedArray ret; - ret.push_back(a->id); - return ret; - } - - Point *begin_point = a; - Point *end_point = b; + Point *begin_point = _get_point(p_from_id.x, p_from_id.y); + Point *end_point = _get_point(p_to_id.x, p_to_id.y); bool found_route = _solve(begin_point, end_point, p_allow_partial_path); if (!found_route) { diff --git a/doc/classes/AStar2D.xml b/doc/classes/AStar2D.xml index cbb8b6dcdeb..31df1c7bd9e 100644 --- a/doc/classes/AStar2D.xml +++ b/doc/classes/AStar2D.xml @@ -152,7 +152,8 @@ Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path. - If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. + If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]). + If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. [b]Note:[/b] When [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish. [codeblocks] [gdscript] @@ -244,7 +245,8 @@ Returns an array with the points that are in the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path. - If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. + If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]). + If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. [b]Note:[/b] This method is not thread-safe; it can only be used from a single [Thread] at a given time. Consider using [Mutex] to ensure exclusive access to one thread to avoid race conditions. Additionally, when [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish. diff --git a/doc/classes/AStar3D.xml b/doc/classes/AStar3D.xml index 61622ad6525..27a459a8f69 100644 --- a/doc/classes/AStar3D.xml +++ b/doc/classes/AStar3D.xml @@ -193,7 +193,8 @@ Returns an array with the IDs of the points that form the path found by AStar3D between the given points. The array is ordered from the starting point to the ending point of the path. - If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. + If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]). + If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. [b]Note:[/b] When [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish. [codeblocks] [gdscript] @@ -283,7 +284,8 @@ Returns an array with the points that are in the path found by AStar3D between the given points. The array is ordered from the starting point to the ending point of the path. - If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. + If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]). + If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. [b]Note:[/b] This method is not thread-safe; it can only be used from a single [Thread] at a given time. Consider using [Mutex] to ensure exclusive access to one thread to avoid race conditions. Additionally, when [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish. diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 335c1ae13cb..1a62c2b6137 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -79,7 +79,8 @@ Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path. - If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. + If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]). + If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. [b]Note:[/b] When [param allow_partial_path] is [code]true[/code] and [param to_id] is solid the search may take an unusually long time to finish. @@ -97,7 +98,8 @@ Returns an array with the points that are in the path found by [AStarGrid2D] between the given points. The array is ordered from the starting point to the ending point of the path. - If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. + If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]). + If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached. [b]Note:[/b] This method is not thread-safe; it can only be used from a single [Thread] at a given time. Consider using [Mutex] to ensure exclusive access to one thread to avoid race conditions. Additionally, when [param allow_partial_path] is [code]true[/code] and [param to_id] is solid the search may take an unusually long time to finish. diff --git a/tests/core/math/test_astar.h b/tests/core/math/test_astar.h index fd99cd5ac81..c0997c88374 100644 --- a/tests/core/math/test_astar.h +++ b/tests/core/math/test_astar.h @@ -209,6 +209,28 @@ TEST_CASE("[AStar3D] Add/Remove") { CHECK_FALSE(a.are_points_connected(0, j, true)); } } - // It's been great work, cheers. \(^ ^)/ +} + +TEST_CASE("[AStar3D] Path from disabled point is empty") { + AStar3D a; + Vector3 p1(0, 0, 0); + Vector3 p2(0, 1, 0); + a.add_point(1, p1); + a.add_point(2, p2); + a.connect_points(1, 2); + + CHECK_EQ(a.get_id_path(1, 1), Vector{ 1 }); + CHECK_EQ(a.get_id_path(1, 2), Vector{ 1, 2 }); + + CHECK_EQ(a.get_point_path(1, 1), Vector{ p1 }); + CHECK_EQ(a.get_point_path(1, 2), Vector{ p1, p2 }); + + a.set_point_disabled(1, true); + + CHECK(a.get_id_path(1, 1).is_empty()); + CHECK(a.get_id_path(1, 2).is_empty()); + + CHECK(a.get_point_path(1, 1).is_empty()); + CHECK(a.get_point_path(1, 2).is_empty()); } } // namespace TestAStar