From 71297497e02d55e7eee4bd6d5213c4a0a0d537a0 Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:30:21 +1000 Subject: [PATCH 1/5] Initial attempt at merging code from #70274. See pull request for more details. --- src/a_star.h | 276 ++++++ src/activity_actor.cpp | 6 +- src/activity_handlers.cpp | 6 +- src/activity_item_handling.cpp | 10 +- src/character.h | 4 +- src/coordinates.h | 7 + src/creature.h | 5 - src/debug_menu.cpp | 3 +- src/do_turn.cpp | 40 +- src/game.cpp | 66 +- src/game.h | 6 +- src/handle_action.cpp | 3 +- src/line.cpp | 13 + src/line.h | 3 +- src/magic_spell_effect.cpp | 2 +- src/map.cpp | 162 +--- src/map.h | 76 +- src/mapgen.cpp | 1 + src/mattack_actors.cpp | 11 +- src/monattack.cpp | 2 +- src/monexamine.cpp | 2 +- src/monmove.cpp | 345 +++---- src/monster.cpp | 36 +- src/monster.h | 26 +- src/npcmove.cpp | 11 +- src/npctrade_utils.cpp | 3 +- src/pathfinding.cpp | 1664 ++++++++++++++++++++++++++------ src/pathfinding.h | 739 +++++++++++++- src/point.cpp | 24 +- src/point.h | 52 + 30 files changed, 2756 insertions(+), 848 deletions(-) create mode 100644 src/a_star.h diff --git a/src/a_star.h b/src/a_star.h new file mode 100644 index 0000000000000..1990678c455cc --- /dev/null +++ b/src/a_star.h @@ -0,0 +1,276 @@ +#pragma once +#ifndef CATA_SRC_A_STAR_H +#define CATA_SRC_A_STAR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template , typename BestPathMap = std::unordered_map>> +class AStarState +{ +public: + void reset(const Node& start, Cost cost); + + std::optional get_next(Cost max); + + template + void generate_neighbors(const Node& current, StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, + HeuristicFn&& heuristic_fn, NeighborsFn&& neighbors_fn); + + bool has_path_to(const Node& end) const; + + Cost path_cost(const Node& end) const; + + std::vector path_to(const Node& end) const; + +private: + struct FirstElementGreaterThan { + template + bool operator()(const std::tuple& lhs, const std::tuple& rhs) const { + return std::get<0>(lhs) > std::get<0>(rhs); + } + }; + + using FrontierNode = std::tuple; + using FrontierQueue = + std::priority_queue, FirstElementGreaterThan>; + + VisitedSet visited_; + BestPathMap best_paths_; + FrontierQueue frontier_; +}; + +template , typename BestStateMap = std::unordered_map>> +class AStarPathfinder +{ +public: + template + std::vector find_path(Cost max, const Node& from, const Node& to, + StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, HeuristicFn&& heuristic_fn, + NeighborsFn&& neighbors_fn); + +private: + AStarState state_; +}; + +template , typename BestStateMap = std::unordered_map>> +class BidirectionalAStarPathfinder +{ +public: + template + std::vector find_path(Cost max, const Node& from, const Node& to, + StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, HeuristicFn&& heuristic_fn, + NeighborsFn&& neighbors_fn); + +private: + AStarState forward_; + AStarState backward_; +}; + +// Implementation Details + +template +void AStarState::reset(const Node& start, Cost cost) +{ + visited_.clear(); + best_paths_.clear(); + + // priority_queue doesn't have a clear method, so we cannot reuse it, and it may + // get quite large, so we explicitly create the underlying container and reserve + // space beforehand. + std::vector queue_data; + queue_data.reserve(400); + frontier_ = FrontierQueue(FirstElementGreaterThan(), std::move(queue_data)); + + best_paths_.try_emplace(start, 0, start); + frontier_.emplace(cost, start); +} + +template +bool AStarState::has_path_to(const Node& end) const +{ + return best_paths_.count(end); +} + +template +Cost AStarState::path_cost(const Node& end) const +{ + return best_paths_.at(end).first; +} + +template +std::vector AStarState::path_to(const Node& end) const +{ + std::vector result; + if (has_path_to(end)) { + Node current = end; + while (best_paths_.at(current).first != 0) { + result.push_back(current); + current = best_paths_.at(current).second; + } + std::reverse(result.begin(), result.end()); + } + return result; +} + +template +std::optional AStarState::get_next(Cost max) +{ + while (!frontier_.empty()) { + auto [cost, current] = frontier_.top(); + frontier_.pop(); + + if (cost >= max) { + return std::nullopt; + } + + if (const auto& [_, inserted] = visited_.emplace(current); !inserted) { + continue; + } + return current; + } + return std::nullopt; +} + +template +template +void AStarState::generate_neighbors( + const Node& current, StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, + HeuristicFn&& heuristic_fn, + NeighborsFn&& neighbors_fn) +{ + // Can't use structured bindings here due to a defect in Clang 10. + const std::pair& best_path = best_paths_[current]; + const Cost current_cost = best_path.first; + const Node& current_parent = best_path.second; + neighbors_fn(current_parent, current, [this, &s_cost_fn, &t_cost_fn, &heuristic_fn, ¤t, + current_cost](const Node& neighbour) { + if (visited_.count(neighbour)) { + return; + } + if (const std::optional s_cost = s_cost_fn(neighbour)) { + if (const std::optional t_cost = t_cost_fn(current, neighbour)) { + const auto& [iter, _] = best_paths_.try_emplace(neighbour, std::numeric_limits::max(), + Node()); + auto& [best_cost, parent] = *iter; + const Cost new_cost = current_cost + *s_cost + *t_cost; + if (new_cost < best_cost) { + best_cost = new_cost; + parent = current; + const Cost estimated_cost = new_cost + heuristic_fn(neighbour); + frontier_.emplace(estimated_cost, neighbour); + } + } + } + else { + visited_.emplace(neighbour); + } + }); +} + +template +template +std::vector AStarPathfinder::find_path( + Cost max_cost, const Node& from, const Node& to, StateCostFn&& s_cost_fn, + TransitionCostFn&& t_cost_fn, + HeuristicFn&& heuristic_fn, NeighborsFn&& neighbors_fn) +{ + if (!s_cost_fn(from) || !s_cost_fn(to)) { + return {}; + } + + state_.reset(from, heuristic_fn(from, to)); + while (const std::optional current = state_.get_next(max_cost)) { + if (*current == to) { + return state_.path_to(to); + } + state_.generate_neighbors(*current, s_cost_fn, t_cost_fn, [&heuristic_fn, + to](const Node& from) { + return heuristic_fn(from, to); + }, neighbors_fn); + } + return {}; +} + + +template +template +std::vector BidirectionalAStarPathfinder::find_path( + Cost max_cost, const Node& from, const Node& to, StateCostFn&& s_cost_fn, + TransitionCostFn&& t_cost_fn, + HeuristicFn&& heuristic_fn, NeighborsFn&& neighbors_fn) +{ + if (!s_cost_fn(from) || !s_cost_fn(to)) { + return {}; + } + + // The full cost is not used since that would result in paths that are up to 2x longer than + // intended. Half the cost cannot be used, since there is no guarantee that both searches + // proceed at the same pace. 2/3rds the cost is a fine balance between the two, and has the + // effect of the worst case still visiting less states than normal A*. + const Cost partial_max_cost = 2 * max_cost / 3; + forward_.reset(from, heuristic_fn(from, to)); + backward_.reset(to, heuristic_fn(to, from)); + for (;; ) { + const std::optional f_current_state = forward_.get_next(partial_max_cost); + if (!f_current_state) { + break; + } + const std::optional b_current_state = backward_.get_next(partial_max_cost); + if (!b_current_state) { + break; + } + + bool f_links = backward_.has_path_to(*f_current_state); + bool b_links = forward_.has_path_to(*b_current_state); + + if (f_links && b_links) { + const Cost f_cost = forward_.path_cost(*f_current_state) + backward_.path_cost( + *f_current_state); + const Cost b_cost = forward_.path_cost(*b_current_state) + backward_.path_cost( + *b_current_state); + if (b_cost < f_cost) { + f_links = false; + } + else { + b_links = false; + } + } + + if (f_links || b_links) { + const Node& midpoint = f_links ? *f_current_state : *b_current_state; + std::vector forward_path = forward_.path_to(midpoint); + std::vector backward_path = backward_.path_to(midpoint); + if (backward_path.empty()) { + return forward_path; + } + backward_path.pop_back(); + std::for_each(backward_path.rbegin(), backward_path.rend(), [&forward_path](const Node& node) { + forward_path.push_back(node); + }); + forward_path.push_back(to); + return forward_path; + } + + forward_.generate_neighbors(*f_current_state, s_cost_fn, t_cost_fn, [&heuristic_fn, + &to](const Node& from) { + return heuristic_fn(from, to); + }, neighbors_fn); + backward_.generate_neighbors(*b_current_state, s_cost_fn, [&t_cost_fn](const Node& from, + const Node& to) { + return t_cost_fn(to, from); + }, [&heuristic_fn, &from](const Node& to) { + return heuristic_fn(to, from); + }, neighbors_fn); + } + return {}; +} + +#endif // CATA_SRC_A_STAR_H diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index e1627bff18df5..702f4cf073124 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -7188,8 +7188,7 @@ void unload_loot_activity_actor::do_turn( player_activity &act, Character &you ) return; } std::vector route; - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings(), - you.get_path_avoid() ); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); if( route.empty() ) { // can't get there, can't do anything, skip it continue; @@ -7225,8 +7224,7 @@ void unload_loot_activity_actor::do_turn( player_activity &act, Character &you ) // get either direct route or route to nearest adjacent tile if // source tile is impassable if( here.passable( src_loc ) ) { - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings(), - you.get_path_avoid() ); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); } else { // impassable source tile (locker etc.), // get route to nearest adjacent tile instead diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 47c8d578ae9e3..96d320b6b30bb 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -2911,8 +2911,7 @@ void activity_handlers::travel_do_turn( player_activity *act, Character *you ) } } const std::vector route_to = - here.route( you->pos_bub(), centre_sub, you->get_pathfinding_settings(), - you->get_path_avoid() ); + here.route( you->pos_bub(), centre_sub, you->get_pathfinding_settings()); if( !route_to.empty() ) { const activity_id act_travel = ACT_TRAVELLING; you->set_destination( route_to, player_activity( act_travel ) ); @@ -3598,8 +3597,7 @@ static void perform_zone_activity_turn( const tripoint_bub_ms &tile_loc = here.bub_from_abs( tile ); std::vector route = - here.route( you->pos_bub(), tile_loc, you->get_pathfinding_settings(), - you->get_path_avoid() ); + here.route( you->pos_bub(), tile_loc, you->get_pathfinding_settings()); if( route.size() > 1 ) { route.pop_back(); diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index 5fb76e5cfc4da..aada313cd77bd 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -674,7 +674,7 @@ std::vector route_adjacent( const Character &you, const tripoin const auto &avoid = you.get_path_avoid(); for( const tripoint_bub_ms &tp : sorted ) { std::vector route = - here.route( you.pos_bub(), tp, you.get_pathfinding_settings(), avoid ); + here.route( you.pos_bub(), tp, you.get_pathfinding_settings() ); if( !route.empty() ) { return route; @@ -752,7 +752,7 @@ static std::vector route_best_workbench( } for( const tripoint_bub_ms &tp : sorted ) { std::vector route = - here.route( you.pos_bub(), tp, you.get_pathfinding_settings(), avoid ); + here.route( you.pos_bub(), tp, you.get_pathfinding_settings() ); if( !route.empty() ) { return route; @@ -2055,8 +2055,7 @@ void activity_on_turn_move_loot( player_activity &act, Character &you ) return; } std::vector route; - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings(), - you.get_path_avoid() ); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); if( route.empty() ) { // can't get there, can't do anything, skip it continue; @@ -2112,8 +2111,7 @@ void activity_on_turn_move_loot( player_activity &act, Character &you ) // get either direct route or route to nearest adjacent tile if // source tile is impassable if( here.passable( src_loc ) ) { - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings(), - you.get_path_avoid() ); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); } else { // impassable source tile (locker etc.), // get route to nearest adjacent tile instead diff --git a/src/character.h b/src/character.h index 86a2c133c1e87..ed516bb5d6c4b 100644 --- a/src/character.h +++ b/src/character.h @@ -3264,8 +3264,8 @@ class Character : public Creature, public visitable /** Returns the player's modified base movement cost */ int run_cost( int base_cost, bool diag = false ) const; - const pathfinding_settings &get_pathfinding_settings() const override; - std::function get_path_avoid() const override; + virtual const pathfinding_settings &get_pathfinding_settings() const; + virtual std::function get_path_avoid() const; /** * Get all hostile creatures currently visible to this player. */ diff --git a/src/coordinates.h b/src/coordinates.h index 1499ebf5c34ac..eaeb0adde6ddc 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -710,6 +710,13 @@ inline int octile_dist( const coords::coord_point +inline float octile_dist_exact(const coords::coord_point& loc1, + const coords::coord_point& loc2) +{ + return octile_dist_exact(loc1.raw(), loc2.raw()); +} + template direction direction_from( const coords::coord_point &loc1, const coords::coord_point &loc2 ) diff --git a/src/creature.h b/src/creature.h index f19152b67202a..e2a9608f1d7d5 100644 --- a/src/creature.h +++ b/src/creature.h @@ -945,11 +945,6 @@ class Creature : public viewer virtual units::mass weight_capacity() const; - /** Returns settings for pathfinding. */ - virtual const pathfinding_settings &get_pathfinding_settings() const = 0; - /** Returns a set of points we do not want to path through. */ - virtual std::function get_path_avoid() const = 0; - bool underwater; void draw( const catacurses::window &w, const point_bub_ms &origin, bool inverted ) const; // TODO: Get rid of the untyped overload diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index d9e04a6cda62b..be3a166418ba5 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -3427,8 +3427,7 @@ static void set_automove() // TODO: fix point types auto rt = get_map().route( player_character.pos_bub(), tripoint_bub_ms( *dest ), - player_character.get_pathfinding_settings(), - player_character.get_path_avoid() ); + player_character.get_pathfinding_settings()); if( !rt.empty() ) { player_character.set_destination( rt ); } else { diff --git a/src/do_turn.cpp b/src/do_turn.cpp index faabb59d44833..c0583da040ba0 100644 --- a/src/do_turn.cpp +++ b/src/do_turn.cpp @@ -268,25 +268,29 @@ void monmove() for( monster &critter : g->all_monsters() ) { // Critters in impassable tiles get pushed away, unless it's not impassable for them - if( !critter.is_dead() && m.impassable( critter.pos() ) && !critter.can_move_to( critter.pos() ) ) { - dbg( D_ERROR ) << "game:monmove: " << critter.name() - << " can't move to its location! (" << critter.posx() - << ":" << critter.posy() << ":" << critter.posz() << "), " - << m.tername( critter.pos() ); - add_msg_debug( debugmode::DF_MONSTER, "%s can't move to its location! (%d,%d,%d), %s", - critter.name(), - critter.posx(), critter.posy(), critter.posz(), m.tername( critter.pos() ) ); - bool okay = false; - for( const tripoint &dest : m.points_in_radius( critter.pos(), 3 ) ) { - if( critter.can_move_to( dest ) && g->is_empty( dest ) ) { - critter.setpos( dest ); - okay = true; - break; + + if (!critter.is_dead() && m.impassable(critter.pos())) { + const PathfindingSettings settings = critter.get_pathfinding_settings(); + if (!critter.can_move_to(critter.pos(), settings)) { + dbg(D_ERROR) << "game:monmove: " << critter.name() + << " can't move to its location! (" << critter.posx() + << ":" << critter.posy() << ":" << critter.posz() << "), " + << m.tername(critter.pos()); + add_msg_debug(debugmode::DF_MONSTER, "%s can't move to its location! (%d,%d,%d), %s", + critter.name(), + critter.posx(), critter.posy(), critter.posz(), m.tername(critter.pos())); + bool okay = false; + for (const tripoint& dest : m.points_in_radius(critter.pos(), 3)) { + if (critter.can_move_to(dest, settings) && g->is_empty(dest)) { + critter.setpos(dest); + okay = true; + break; + } + } + if (!okay) { + // die of "natural" cause (overpopulation is natural) + critter.die(nullptr); } - } - if( !okay ) { - // die of "natural" cause (overpopulation is natural) - critter.die( nullptr ); } } diff --git a/src/game.cpp b/src/game.cpp index d120ecc6894e7..5f4f9556cd954 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5009,7 +5009,7 @@ static bool can_place_monster( const monster &mon, const tripoint &p ) if( creatures.creature_at( p ) ) { return false; } - return mon.will_move_to( p ) && mon.know_danger_at( p ); + return mon.can_move_to(p); } static bool can_place_npc( const tripoint &p ) @@ -7456,10 +7456,9 @@ std::optional> game::safe_route_to( Character &who, if( is_dangerous_tile( p.raw() ) ) { continue; } + // TODO: Check the new pathfinding settings returns an equivalent result. const route_t route = here.route( who.pos_bub(), p, - who.get_pathfinding_settings(), [this]( const tripoint & p ) { - return is_dangerous_tile( p ); - } ); + who.get_pathfinding_settings()); if( route.empty() ) { continue; // no route } @@ -12324,43 +12323,50 @@ void game::start_hauling( const tripoint &pos ) u.assign_activity( actor ); } -std::optional game::find_or_make_stairs( map &mp, const int z_after, bool &rope_ladder, - bool peeking, const tripoint &pos ) +std::optional game::find_stairs(const map& mp, const int z_after, const tripoint& pos) { - const bool is_avatar = u.pos() == pos; - const int omtilesz = SEEX * 2; - real_coords rc( mp.getabs( pos.xy() ) ); - tripoint omtile_align_start( mp.getlocal( rc.begin_om_pos() ), z_after ); - tripoint omtile_align_end( omtile_align_start + point( -1 + omtilesz, -1 + omtilesz ) ); - // Try to find the stairs. - std::optional stairs; - int best = INT_MAX; const int movez = z_after - pos.z; const bool going_down_1 = movez == -1; - const bool going_up_1 = movez == 1; // If there are stairs on the same x and y as we currently are, use those - if( going_down_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_UP, pos + tripoint_below ) ) { - stairs.emplace( pos + tripoint_below ); + if (going_down_1 && mp.has_flag(ter_furn_flag::TFLAG_GOES_UP, pos + tripoint_below)) { + return pos + tripoint_below; } - if( going_up_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, pos + tripoint_above ) ) { - stairs.emplace( pos + tripoint_above ); + const bool going_up_1 = movez == 1; + if (going_up_1 && mp.has_flag(ter_furn_flag::TFLAG_GOES_DOWN, pos + tripoint_above)) { + return pos + tripoint_above; } + // We did not find stairs directly above or below, so search the map for them - // If there's empty space right below us, we can just go down that way. - if( !stairs.has_value() && get_map().tr_at( u.pos() ) != tr_ledge ) { - for( const tripoint &dest : mp.points_in_rectangle( omtile_align_start, omtile_align_end ) ) { - if( rl_dist( u.pos(), dest ) <= best && - ( ( going_down_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_UP, dest ) ) || - ( going_up_1 && ( mp.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, dest ) || - mp.ter( dest ) == ter_t_manhole_cover ) ) || - ( ( movez == 2 || movez == -2 ) && mp.ter( dest ) == ter_t_elevator ) ) ) { - stairs.emplace( dest ); - best = rl_dist( u.pos(), dest ); - } + int best = INT_MAX; + std::optional stairs; + const int omtilesz = SEEX * 2 - 1; + real_coords rc(mp.getabs(pos.xy())); + tripoint omtile_align_start(mp.getlocal(rc.begin_om_pos()), z_after); + tripoint omtile_align_end(omtile_align_start + point(omtilesz, omtilesz)); + for (const tripoint& dest : mp.points_in_rectangle(omtile_align_start, omtile_align_end)) { + if (rl_dist(pos, dest) <= best && + ((going_down_1 && mp.has_flag(ter_furn_flag::TFLAG_GOES_UP, dest)) || + (going_up_1 && (mp.has_flag(ter_furn_flag::TFLAG_GOES_DOWN, dest) || + mp.ter(dest) == ter_t_manhole_cover)) || + ((movez == 2 || movez == -2) && mp.ter(dest) == ter_t_elevator))) { + stairs.emplace(dest); + best = rl_dist(pos, dest); } } + return stairs; +} + +std::optional game::find_or_make_stairs( const map &mp, const int z_after, bool &rope_ladder, + bool peeking, const tripoint &pos ) +{ + const bool is_avatar = u.pos() == pos; + const int movez = z_after - pos.z; + + // Try to find the stairs. + std::optional stairs = find_stairs(mp, z_after, pos); + creature_tracker &creatures = get_creature_tracker(); if( stairs.has_value() ) { if( !is_avatar ) { diff --git a/src/game.h b/src/game.h index c48120acf4017..2f15cff0f0fa2 100644 --- a/src/game.h +++ b/src/game.h @@ -289,8 +289,10 @@ class game /** Returns the other end of the stairs (if any). May query, affect u etc. * @param pos Disable queries and msgs if not the same position as player. */ - std::optional find_or_make_stairs( map &mp, int z_after, bool &rope_ladder, - bool peeking, const tripoint &pos ); + std::optional find_stairs(const map& mp, int z_after, const tripoint& pos); + std::optional find_or_make_stairs(const map& mp, int z_after, bool& rope_ladder, + bool peeking, const tripoint& pos); + /* * Prompt player on direction they want to climb up or down. */ diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 3fa187fc9836a..81f85a5b5885e 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -2243,8 +2243,7 @@ bool game::do_regular_action( action_id &act, avatar &player_character, player_character.pos_bub() + dest_delta * ( SEEX - i ); destination_preview = m.route( player_character.pos_bub(), auto_travel_destination, - player_character.get_pathfinding_settings(), - player_character.get_path_avoid() ); + player_character.get_pathfinding_settings()); if( !destination_preview.empty() ) { destination_preview.erase( destination_preview.begin() + 1, destination_preview.end() ); diff --git a/src/line.cpp b/src/line.cpp index d9c8c4697bfcd..f6485a3123ddd 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -285,6 +285,19 @@ float octile_dist_exact( const point &loc1, const point &loc2 ) return d.x + d.y - 2 * mind + mind * M_SQRT2; } +float octile_dist_exact(const tripoint& from, const tripoint& to) +{ + const tripoint d = (from - to).abs(); + const int min = std::min(d.x, std::min(d.y, d.z)); + const int max = std::max(d.x, std::max(d.y, d.z)); + const int mid = d.x + d.y + d.z - min - max; + + constexpr int one_axis = 1; + constexpr float two_axis = M_SQRT2; + constexpr float three_axis = 1.73205f; + return (three_axis - two_axis) * min + (two_axis - one_axis) * mid + one_axis * max; +} + units::angle atan2( const point &p ) { return units::atan2( p.y, p.x ); diff --git a/src/line.h b/src/line.h index c9025dcb602be..52803cb8026d5 100644 --- a/src/line.h +++ b/src/line.h @@ -251,9 +251,10 @@ float rl_dist_exact( const tripoint &loc1, const tripoint &loc2 ); int manhattan_dist( const point &loc1, const point &loc2 ); // Travel distance between 2 points on a square grid, assuming diagonal moves -// cost sqrt(2) and cardinal moves cost 1. +// cost sqrt(2) or sqrt(3) and cardinal moves cost 1. int octile_dist( const point &loc1, const point &loc2, int multiplier = 1 ); float octile_dist_exact( const point &loc1, const point &loc2 ); +float octile_dist_exact(const tripoint& from, const tripoint& to); // get angle of direction represented by point units::angle atan2( const point & ); diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index a8fa94bba92cd..b5d961472b048 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -1838,7 +1838,7 @@ void spell_effect::slime_split_on_death( const spell &sp, Creature &caster, shared_ptr_fast mon = make_shared_fast( slime_id ); mon->ammo = mon->type->starting_ammo; - if( mon->will_move_to( dest ) && mon->know_danger_at( dest ) ) { + if( mon->can_move_to( dest ) ) { if( monster *const blob = g->place_critter_around( mon, dest, 0 ) ) { sp.make_sound( dest, caster ); if( !permanent ) { diff --git a/src/map.cpp b/src/map.cpp index d5e263c12b333..b2794155e1d4d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -257,10 +257,6 @@ map::map( int mapsize, bool zlev ) : my_MAPSIZE( mapsize ), my_HALF_MAPSIZE( map grid.resize( static_cast( my_MAPSIZE ) * my_MAPSIZE, nullptr ); } - for( auto &ptr : pathfinding_caches ) { - ptr = std::make_unique(); - } - dbg( D_INFO ) << "map::map(): my_MAPSIZE: " << my_MAPSIZE << " z-levels enabled:" << zlevels; traplocs.resize( trap::count() ); } @@ -1864,7 +1860,7 @@ bool map::furn_set( const tripoint_bub_ms &p, const furn_id &new_furniture, cons get_creature_tracker().invalidate_reachability_cache(); } // TODO: Limit to changes that affect move cost, traps and stairs - set_pathfinding_cache_dirty( p.raw() ); + set_pathfinding_cache_dirty( tripoint_bub_ms( p ) ); // Make sure the furniture falls if it needs to support_dirty( p.raw() ); @@ -2332,7 +2328,7 @@ bool map::ter_set( const tripoint &p, const ter_id &new_terrain, bool avoid_crea get_creature_tracker().invalidate_reachability_cache(); } // TODO: Limit to changes that affect move cost, traps and stairs - set_pathfinding_cache_dirty( p ); + set_pathfinding_cache_dirty(tripoint_bub_ms(p)); tripoint above( p.xy(), p.z + 1 ); // Make sure that if we supported something and no longer do so, it falls down @@ -3355,6 +3351,42 @@ int map::bash_rating( const int str, const tripoint &p, const bool allow_floor ) return bash_rating_internal( str, furniture, terrain, allow_floor, veh, part ); } +std::optional> map::bash_range(const tripoint& p, + const bool allow_floor) const +{ + if (!inbounds(p)) { + DebugLog(D_WARNING, D_MAP) << "Looking for out-of-bounds is_bashable at " + << p.x << ", " << p.y << ", " << p.z; + return std::nullopt; + } + + if (const optional_vpart_position vp = veh_at(p)) { + if (const auto vpobst = vp->obstacle_at_part()) { + const int bash_part = vpobst->part_index(); + // Car obstacle that isn't a door + // TODO: Account for armor + const int hp = vp->vehicle().part(bash_part).hp(); + const int bash_min = hp / 20 + 1; + // Large max to discourage bashing. + const int bash_max = bash_min + 100; + return std::make_pair(bash_min, bash_max); + } + } + + const furn_t& furniture = furn(p).obj(); + ///\EFFECT_STR determines what furniture can be smashed + if (furniture.id && furniture.bash.str_max != -1) { + return std::make_pair(furniture.bash.str_min, furniture.bash.str_max); + } + + const ter_t& terrain = ter(p).obj(); + if (terrain.bash.str_max != -1 && (!terrain.bash.bash_below || allow_floor)) { + return std::make_pair(terrain.bash.str_min, terrain.bash.str_max); + } + + return std::nullopt; +} + // End of 3D bashable void map::make_rubble( const tripoint_bub_ms &p, const furn_id &rubble_type, const bool items, @@ -6738,7 +6770,7 @@ void map::on_field_modified( const tripoint &p, const field_type &fd_type ) } if( fd_type.is_dangerous() ) { - set_pathfinding_cache_dirty( p ); + set_pathfinding_cache_dirty( tripoint_bub_ms( p ) ); } // Ensure blood type fields don't hang in the air @@ -9003,10 +9035,11 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool // Find horde's target submap for( monster &tmp : group.monsters ) { + const PathfindingSettings settings = tmp.get_pathfinding_settings(); for( int tries = 0; tries < 10 && !locations.empty(); tries++ ) { const tripoint local_pos = random_entry_removed( locations ); const tripoint_abs_ms abs_pos = get_map().getglobal( local_pos ); - if( !tmp.can_move_to( local_pos ) ) { + if( !tmp.can_move_to( local_pos, settings ) ) { continue; // target can not contain the monster } if( group.horde ) { @@ -10450,27 +10483,17 @@ const level_cache &map::access_cache( int zlev ) const return nullcache; } -pathfinding_cache::pathfinding_cache() -{ - dirty = true; -} - -pathfinding_cache &map::get_pathfinding_cache( int zlev ) const -{ - return *pathfinding_caches[zlev + OVERMAP_DEPTH]; -} - void map::set_pathfinding_cache_dirty( const int zlev ) { - if( inbounds_z( zlev ) ) { - get_pathfinding_cache( zlev ).dirty = true; + if (pathfinding_cache_ && inbounds_z(zlev)) { + pathfinding_cache_->invalidate(zlev); } } -void map::set_pathfinding_cache_dirty( const tripoint &p ) +void map::set_pathfinding_cache_dirty( const tripoint_bub_ms &p ) { - if( inbounds( p ) ) { - get_pathfinding_cache( p.z ).dirty_points.insert( p.xy() ); + if (pathfinding_cache_ && inbounds(p)) { + pathfinding_cache_->invalidate(p); } } @@ -10491,98 +10514,6 @@ void map::main_cleanup_override( bool over ) _main_cleanup_override = over; } -const pathfinding_cache &map::get_pathfinding_cache_ref( int zlev ) const -{ - if( !inbounds_z( zlev ) ) { - debugmsg( "Tried to get pathfinding cache for out of bounds z-level %d", zlev ); - return *pathfinding_caches[ OVERMAP_DEPTH ]; - } - pathfinding_cache &cache = get_pathfinding_cache( zlev ); - if( cache.dirty || !cache.dirty_points.empty() ) { - update_pathfinding_cache( zlev ); - } - - return cache; -} - -void map::update_pathfinding_cache( const tripoint &p ) const -{ - if( !inbounds( p ) ) { - return; - } - pathfinding_cache &cache = get_pathfinding_cache( p.z ); - pf_special cur_value = PF_NORMAL; - - const_maptile tile = maptile_at_internal( p ); - - const ter_t &terrain = tile.get_ter_t(); - const furn_t &furniture = tile.get_furn_t(); - const field &field = tile.get_field(); - const map &here = get_map(); - int part; - const vehicle *veh = veh_at_internal( p, part ); - - const int cost = move_cost_internal( furniture, terrain, field, veh, part ); - - if( cost > 2 ) { - cur_value |= PF_SLOW; - } else if( cost <= 0 ) { - cur_value |= PF_WALL; - if( terrain.has_flag( ter_furn_flag::TFLAG_CLIMBABLE ) ) { - cur_value |= PF_CLIMBABLE; - } - } - - if( veh != nullptr ) { - cur_value |= PF_VEHICLE; - } - - for( const auto &fld : tile.get_field() ) { - const field_entry &cur = fld.second; - if( cur.is_dangerous() ) { - cur_value |= PF_FIELD; - } - } - - if( ( !tile.get_trap_t().is_benign() || !terrain.trap.obj().is_benign() ) && - !here.has_vehicle_floor( p ) ) { - cur_value |= PF_TRAP; - } - - if( terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) || - terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) || - terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) || terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) || - terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) ) { - cur_value |= PF_UPDOWN; - } - - if( terrain.has_flag( ter_furn_flag::TFLAG_SHARP ) && !here.has_vehicle_floor( p ) ) { - cur_value |= PF_SHARP; - } - - cache.special[p.x][p.y] = cur_value; -} - -void map::update_pathfinding_cache( int zlev ) const -{ - pathfinding_cache &cache = get_pathfinding_cache( zlev ); - - if( cache.dirty ) { - const int size = getmapsize(); - for( int x = 0; x < size * SEEX; ++x ) { - for( int y = 0; y < size * SEEX; ++y ) { - update_pathfinding_cache( { x, y, zlev } ); - } - } - cache.dirty = false; - } else { - for( const point &p : cache.dirty_points ) { - update_pathfinding_cache( { p, zlev } ); - } - } - cache.dirty_points.clear(); -} - void map::clip_to_bounds( tripoint &p ) const { clip_to_bounds( p.x, p.y, p.z ); @@ -10688,6 +10619,7 @@ void map::invalidate_max_populated_zlev( int zlev ) } } +// PERF: 40.66% (Part of npc::regen_ai_cache) bool map::has_potential_los( const tripoint &from, const tripoint &to ) const { const point key = sees_cache_key( from, to ); diff --git a/src/map.h b/src/map.h index def2f349e1172..ffa4d708d269d 100644 --- a/src/map.h +++ b/src/map.h @@ -40,6 +40,7 @@ #include "map_selector.h" #include "mapdata.h" #include "maptile_fwd.h" +#include "pathfinding.h" #include "point.h" #include "rng.h" #include "type_id.h" @@ -96,8 +97,6 @@ using VehicleList = std::vector; class map; enum class ter_furn_flag : int; -struct pathfinding_cache; -struct pathfinding_settings; template struct weighted_int_list; struct field_proc_data; @@ -416,11 +415,25 @@ class map void set_outside_cache_dirty( int zlev ); void set_floor_cache_dirty( int zlev ); void set_pathfinding_cache_dirty( int zlev ); - void set_pathfinding_cache_dirty( const tripoint &p ); + void set_pathfinding_cache_dirty( const tripoint_bub_ms &p ); /*@}*/ void invalidate_map_cache( int zlev ); + RealityBubblePathfindingCache* pathfinding_cache() const { + if (!pathfinding_cache_) { + pathfinding_cache_ = std::make_unique(); + } + return pathfinding_cache_.get(); + } + + RealityBubblePathfinder* pathfinder() const { + if (!pathfinder_) { + pathfinder_ = std::make_unique(pathfinding_cache()); + } + return pathfinder_.get(); + } + // @returns true if map memory decoration should be re/memorized bool memory_cache_dec_is_dirty( const tripoint &p ) const; // @returns true if map memory terrain should be re/memorized @@ -730,16 +743,34 @@ class map * @param pre_closed Never path through those points. They can still be the source or the destination. */ // TODO: fix point types (remove the first overload) - std::vector route( const tripoint &f, const tripoint &t, - const pathfinding_settings &settings, - const std::function &avoid = []( const tripoint & ) { - return false; - } ) const; - std::vector route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, - const pathfinding_settings &settings, - const std::function &avoid = []( const tripoint & ) { - return false; - } ) const; + //std::vector route( const tripoint &f, const tripoint &t, + // const pathfinding_settings &settings, + //const std::function &avoid = []( const tripoint & ) { + // return false; + //} ) const; + //std::vector route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + // const pathfinding_settings &settings, + //const std::function &avoid = []( const tripoint & ) { + // return false; + //} ) const; + + bool can_teleport(const tripoint_bub_ms& t, const PathfindingSettings& settings) const; + bool can_move(const tripoint_bub_ms& f, const tripoint_bub_ms& t, + const PathfindingSettings& settings) const; + std::optional move_cost(const tripoint_bub_ms& f, const tripoint_bub_ms& t, + const PathfindingSettings& settings) const; + std::vector straight_route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, + const PathfindingSettings& settings) const; + std::vector route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, + const PathfindingSettings& settings) const; + + // TODO: Update callers to the new PathfindingSettings so that this overload is not necessary. + std::vector route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, + const pathfinding_settings& settings) const; + + // TODO: Replace this with a typesafe variant. + std::vector route(const tripoint& f, const tripoint& t, + const pathfinding_settings& settings); // Get a straight route from f to t, only along non-rough terrain. Returns an empty vector // if that is not possible. @@ -747,14 +778,17 @@ class map private: // Pathfinding cost helper that computes the cost of moving into |p| from |cur|. // Includes climbing, bashing and opening doors. + // TODO: Combine this logic with the new pathfinding. int cost_to_pass( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, pf_special p_special ) const; // Pathfinding cost helper that computes the cost of moving into |p| // from |cur| based on perceived danger. // Includes moving through traps. + // TODO: Combine this logic with the new pathfinding. int cost_to_avoid( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, pf_special p_special ) const; // Sum of cost_to_pass and cost_to_avoid. + // TODO: Combine this logic with the new pathfinding. int extra_cost( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, pf_special p_special ) const; public: @@ -1149,6 +1183,10 @@ class map return bash_rating( str, tripoint( p, abs_sub.z() ) ); } + // The range of minimum and maximum bash strength needed to bash something on the given tile. + // Returns std::nullopt if there is nothing bashable. + std::optional> bash_range(const tripoint& p, bool allow_floor = false) const; + // Rubble /** Generates rubble at the given location, if overwrite is true it just writes on top of what currently exists * floor_type is only used if there is a non-bashable wall at the location or with overwrite = true */ @@ -2420,7 +2458,10 @@ class map */ mutable std::array< std::unique_ptr, OVERMAP_LAYERS > caches; - mutable std::array< std::unique_ptr, OVERMAP_LAYERS > pathfinding_caches; + // Pathfinding caches. Lazily initialized, only use via accessors. + mutable std::unique_ptr pathfinding_cache_; + mutable std::unique_ptr pathfinder_; + /** * Set of submaps that contain active items in absolute coordinates. */ @@ -2447,8 +2488,6 @@ class map return caches[zlev + OVERMAP_DEPTH].get(); } - pathfinding_cache &get_pathfinding_cache( int zlev ) const; - visibility_variables visibility_variables_cache; // caches the highest zlevel above which all zlevels are uniform @@ -2470,11 +2509,6 @@ class map return get_cache( zlev ); } - const pathfinding_cache &get_pathfinding_cache_ref( int zlev ) const; - - void update_pathfinding_cache( const tripoint &p ) const; - void update_pathfinding_cache( int zlev ) const; - void update_visibility_cache( int zlev ); void invalidate_visibility_cache(); const visibility_variables &get_visibility_variables_cache() const; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 904c46f48c958..8a9246735b2e7 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -6892,6 +6892,7 @@ vehicle *map::add_vehicle( const vproto_id &type, const tripoint &p, const units add_vehicle_to_cache( placed_vehicle ); rebuild_vehicle_level_caches(); + // TODO: Only invalidate points the vehicle is at. set_pathfinding_cache_dirty( p.z ); placed_vehicle->place_zones( *this ); } diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 9dad99373b36c..921577c5dae59 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -144,6 +144,7 @@ bool leap_actor::call( monster &z ) const target.x, target.y, target.z ); std::multimap candidates; + const PathfindingSettings settings = z.get_pathfinding_settings(); for( const tripoint &candidate : here.points_in_radius( z.pos(), max_range ) ) { if( candidate == z.pos() ) { add_msg_debug( debugmode::DF_MATTACK, "Monster at coordinates %d,%d,%d", @@ -166,16 +167,11 @@ bool leap_actor::call( monster &z ) const "Candidate farther from target than optimal path, discarded" ); continue; } - if( !ignore_dest_terrain && !z.will_move_to( candidate ) ) { + if( !ignore_dest_terrain && !z.can_move_to( candidate, settings ) ) { add_msg_debug( debugmode::DF_MATTACK, "Candidate place it can't enter, discarded" ); continue; } - if( !ignore_dest_danger && !z.know_danger_at( candidate ) ) { - add_msg_debug( debugmode::DF_MATTACK, - "Candidate with dangerous conditions, discarded" ); - continue; - } candidates.emplace( candidate_dist, candidate ); } for( const auto &candidate : candidates ) { @@ -590,6 +586,7 @@ int melee_actor::do_grab( monster &z, Creature *target, bodypart_id bp_id ) cons // Drag stuff if( grab_data.drag_distance > 0 ) { int distance = grab_data.drag_distance; + const PathfindingSettings settings = z.get_pathfinding_settings(); while( distance > 0 ) { // Start with the opposite square tripoint opposite_square = z.pos() - ( target->pos() - z.pos() ); @@ -615,7 +612,7 @@ int melee_actor::do_grab( monster &z, Creature *target, bodypart_id bp_id ) cons std::set::iterator intersect_iter = intersect.begin(); std::advance( intersect_iter, rng( 0, intersect.size() - 1 ) ); tripoint target_square = random_entry>( intersect ); - if( z.can_move_to( target_square ) ) { + if( z.can_move_to( target_square, settings ) ) { monster *zz = target->as_monster(); tripoint zpt = z.pos(); z.move_to( target_square, false, false, grab_data.drag_movecost_mod ); diff --git a/src/monattack.cpp b/src/monattack.cpp index b7583bf55823d..603b657364896 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -2524,7 +2524,7 @@ bool mattack::formblob( monster *z ) // If we're big enough, spawn a baby blob. shared_ptr_fast mon = make_shared_fast( mon_blob_small ); mon->ammo = mon->type->starting_ammo; - if( mon->will_move_to( dest ) && mon->know_danger_at( dest ) ) { + if( mon->can_move_to( dest ) ) { didit = true; z->set_speed_base( z->get_speed_base() - mon_blob_small->speed ); if( monster *const blob = g->place_critter_around( mon, dest, 0 ) ) { diff --git a/src/monexamine.cpp b/src/monexamine.cpp index eb388327ae594..027307a8c8f50 100644 --- a/src/monexamine.cpp +++ b/src/monexamine.cpp @@ -563,7 +563,7 @@ void insert_battery( monster &z ) bool Character::can_mount( const monster &critter ) const { const auto &avoid = get_path_avoid(); - auto route = get_map().route( pos(), critter.pos(), get_pathfinding_settings(), avoid ); + auto route = get_map().route( pos(), critter.pos(), get_pathfinding_settings() ); if( route.empty() ) { return false; diff --git a/src/monmove.cpp b/src/monmove.cpp index adf1193bbf126..db715e252745b 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -128,162 +128,121 @@ bool monster::is_immune_field( const field_type_id &fid ) const return Creature::is_immune_field( fid ); } -static bool z_is_valid( int z ) -{ - return z >= -OVERMAP_DEPTH && z <= OVERMAP_HEIGHT; -} -bool monster::will_move_to( const tripoint &p ) const +PathfindingSettings monster::get_pathfinding_settings(bool avoid_bashing) const { - map &here = get_map(); - if( here.impassable( p ) ) { - if( digging() ) { - if( !here.has_flag( ter_furn_flag::TFLAG_BURROWABLE, p ) ) { - return false; - } - } else if( !( can_climb() && here.has_flag( ter_furn_flag::TFLAG_CLIMBABLE, p ) ) ) { - return false; - } - } + PathfindingSettings settings = type->path_settings.to_new_pathfinding_settings(); - if( !here.has_vehicle_floor( p ) ) { - if( !can_submerge() && !flies() && here.has_flag( ter_furn_flag::TFLAG_DEEP_WATER, p ) ) { - return false; - } - } + settings.set_size_restriction(get_size()); - if( digs() && !here.has_flag( ter_furn_flag::TFLAG_DIGGABLE, p ) && - !here.has_flag( ter_furn_flag::TFLAG_BURROWABLE, p ) ) { - return false; - } + settings.set_avoid_bashing(avoid_bashing); - if( has_flag( mon_flag_AQUATIC ) && ( - !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, p ) || - // AQUATIC (confined to water) monster avoid vehicles, unless they are already underneath one - ( here.veh_at( p ) && !here.veh_at( pos() ) ) - ) ) { - return false; - } + settings.set_is_digging(digging()); - if( has_flag( mon_flag_SUNDEATH ) && g->is_in_sunlight( p ) ) { - return false; - } + settings.set_avoid_climbing(!can_climb()); - if( get_size() > creature_size::medium && - here.has_flag_ter( ter_furn_flag::TFLAG_SMALL_PASSAGE, p ) ) { - return false; // if a large critter, can't move through tight passages - } + const bool can_fly = flies(); + settings.set_avoid_deep_water(!can_submerge() && !can_fly); - return true; -} + settings.set_avoid_hard_ground(digs()); -bool monster::know_danger_at( const tripoint &p ) const -{ - map &here = get_map(); + const bool is_aquatic = has_flag(mon_flag_AQUATIC); + settings.set_avoid_ground(is_aquatic); + // AQUATIC (confined to water) monster avoid vehicles, unless they are already underneath one + settings.set_avoid_vehicle(is_aquatic && !get_map().veh_at(pos())); - // Various avoiding behaviors. + // If we hate the sun, stay inside when it is out. + settings.set_avoid_unsheltered(has_flag(mon_flag_SUNDEATH) && + incident_sun_irradiance(get_weather().weather_id, calendar::turn) > irradiance::minimal); - bool avoid_simple = has_flag( mon_flag_PATH_AVOID_DANGER ); + // Various avoiding behaviors. + bool avoid_fire = has_flag(mon_flag_PATH_AVOID_FIRE); + bool avoid_fall = has_flag(mon_flag_PATH_AVOID_FALL); + bool avoid_simple = has_flag(mon_flag_PATH_AVOID_DANGER); + bool avoid_sharp = type->path_settings.avoid_sharp; + bool avoid_traps = type->path_settings.avoid_traps; + bool avoid_dangerous_fields = type->path_settings.avoid_dangerous_fields; + + // avoid_simple implies avoid fire, fall, and sharp. + if (avoid_simple) { + avoid_fire = true; + avoid_fall = true; + avoid_sharp = true; + } - bool avoid_fire = avoid_simple || has_flag( mon_flag_PATH_AVOID_FIRE ); - bool avoid_fall = avoid_simple || has_flag( mon_flag_PATH_AVOID_FALL ); - bool avoid_sharp = avoid_simple || get_pathfinding_settings().avoid_sharp; + // Don't enter lava if we have any concept of heat being bad + settings.set_avoid_lava(avoid_fire); - bool avoid_dangerous_fields = get_pathfinding_settings().avoid_dangerous_fields; - bool avoid_traps = get_pathfinding_settings().avoid_traps; + settings.set_is_flying(can_fly); + if (can_fly) { + avoid_fall = false; + } - // technically this will shortcut in evaluation from fire or fall - // before hitting simple or complex but this is more explicit - if( avoid_fire || avoid_fall || avoid_simple || - avoid_traps || avoid_dangerous_fields || avoid_sharp ) { - const ter_id target = here.ter( p ); - if( !here.has_vehicle_floor( p ) ) { - // Don't enter lava if we have any concept of heat being bad - if( avoid_fire && target == ter_t_lava ) { - return false; - } + // Don't throw ourselves off cliffs if we have a concept of falling + settings.set_avoid_falling(avoid_fall); - if( avoid_fall ) { - // Don't throw ourselves off cliffs if we have a concept of falling - if( !here.has_floor_or_water( p ) && !flies() ) { - return false; - } + // Don't enter open pits ever unless tiny, can fly or climb well + settings.set_avoid_pits(avoid_fall && type->size != creature_size::tiny && !can_climb()); - // Don't enter open pits ever unless tiny, can fly or climb well - if( !( type->size == creature_size::tiny || can_climb() ) && - ( target == ter_t_pit || target == ter_t_pit_spiked || target == ter_t_pit_glass ) ) { - return false; - } - } + // Some things are only avoided if we're not attacking the player + if (get_player_character().get_location() != get_dest() || + attitude(&get_player_character()) != MATT_ATTACK) { + // Sharp terrain is ignored while attacking + settings.set_avoid_sharp(avoid_sharp && !(type->size == creature_size::tiny || can_fly || + get_armor_type(damage_cut, bodypart_id("torso")) >= 10)); + } + else { + // yolo, get that bread, etc + settings.set_avoid_sharp(false); + } - // Some things are only avoided if we're not attacking - if( get_player_character().get_location() != get_dest() || - attitude( &get_player_character() ) != MATT_ATTACK ) { - // Sharp terrain is ignored while attacking - if( avoid_sharp && here.has_flag( ter_furn_flag::TFLAG_SHARP, p ) && - !( type->size == creature_size::tiny || flies() || - get_armor_type( damage_cut, bodypart_id( "torso" ) ) >= 10 ) ) { - return false; - } - } + // Don't step on any traps (if we can see) + settings.set_avoid_dangerous_traps(avoid_traps && has_flag(mon_flag_SEES)); - // Don't step on any traps (if we can see) - const trap &target_trap = here.tr_at( p ); - if( avoid_traps && has_flag( mon_flag_SEES ) && - !target_trap.is_benign() && here.has_floor_or_water( p ) ) { - return false; - } + if (avoid_dangerous_fields) { + // Don't enter any dangerous fields + settings.set_maybe_avoid_dangerous_fields_fn([this](const field_type_id& field_id) { + return !is_immune_field(field_id); + }); + } + else { + // Without avoid_complex, only fire and electricity are checked for field avoidance. + const bool should_avoid_fire = avoid_fire && !is_immune_field(fd_fire); + const bool should_avoid_simple = avoid_simple && !is_immune_field(fd_electricity); + if (should_avoid_fire && should_avoid_simple) { + settings.set_maybe_avoid_dangerous_fields_fn([](const field_type_id& field_id) { + return field_id == fd_fire || field_id == fd_electricity; + }); } - - const field &target_field = here.field_at( p ); - // Higher awareness is needed for identifying these as threats. - if( avoid_dangerous_fields && is_dangerous_fields( target_field ) ) { - return false; + else if (should_avoid_fire) { + settings.set_maybe_avoid_dangerous_fields_fn([](const field_type_id& field_id) { + return field_id == fd_fire; + }); } - - // Without avoid_complex, only fire and electricity are checked for field avoidance. - if( avoid_fire && target_field.find_field( fd_fire ) && !is_immune_field( fd_fire ) ) { - return false; + else if (should_avoid_simple) { + settings.set_maybe_avoid_dangerous_fields_fn([](const field_type_id& field_id) { + return field_id == fd_electricity; + }); } - if( avoid_simple && target_field.find_field( fd_electricity ) && - !is_immune_field( fd_electricity ) ) { - return false; + else { + settings.set_maybe_avoid_dangerous_fields_fn(); } } - - return true; + return settings; } -bool monster::can_reach_to( const tripoint &p ) const +bool monster::can_move_to(const tripoint& p, const PathfindingSettings& settings) const { - map &here = get_map(); - if( p.z > pos().z && z_is_valid( pos().z ) ) { - if( here.has_flag( ter_furn_flag::TFLAG_RAMP_UP, tripoint( p.xy(), p.z - 1 ) ) ) { - return true; - } - if( !here.has_flag( ter_furn_flag::TFLAG_GOES_UP, pos() ) && here.has_floor( p ) ) { - // can't go through the roof - return false; - } - } else if( p.z < pos().z && z_is_valid( pos().z ) ) { - const tripoint above( p.xy(), p.z + 1 ); - if( here.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN, above ) ) { - return true; - } - if( !here.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, pos() ) && - ( here.has_floor( above ) || ( !flies() && !here.has_floor_or_water( above ) ) ) ) { - // can't go through the floor - // Check floors for flying monsters movement - return false; - } + const map& here = get_map(); + const tripoint_bub_ms& from = pos_bub(); + if (here.inbounds(from)) { + return here.can_move(from, tripoint_bub_ms(p), settings); + } + else { + return here.can_teleport(tripoint_bub_ms(p), settings); } - return true; } -bool monster::can_move_to( const tripoint &p ) const -{ - return can_reach_to( p ) && will_move_to( p ); -} float monster::rate_target( Creature &c, float best, bool smart ) const { @@ -961,11 +920,12 @@ void monster::move() bool moved = false; tripoint destination; + PathfindingSettings settings = get_pathfinding_settings(); bool try_to_move = false; creature_tracker &creatures = get_creature_tracker(); for( const tripoint &dest : here.points_in_radius( pos(), 1 ) ) { if( dest != pos() ) { - if( can_move_to( dest ) && + if( can_move_to( dest, settings ) && creatures.creature_at( dest, true ) == nullptr ) { try_to_move = true; break; @@ -1004,26 +964,30 @@ void monster::move() path.erase( path.begin() ); } - const pathfinding_settings &pf_settings = get_pathfinding_settings(); - if( pf_settings.max_dist >= rl_dist( get_location(), get_dest() ) && + if (settings.max_distance() >= rl_dist(get_location(), get_dest()) && ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != local_dest ) ) { // We need a new path + path.clear(); + // Temporarily allow bashing to find a path through bashable terrain. + settings.set_avoid_bashing(false); + if( can_pathfind() ) { - path = here.route( pos(), local_dest, pf_settings, get_path_avoid() ); - if( path.empty() ) { + for (const tripoint_bub_ms& p : here.route(pos_bub(), tripoint_bub_ms(local_dest), settings)) { + path.push_back(p.raw()); + } + if (path.empty()) { increment_pathfinding_cd(); } } else { - path = here.straight_route( pos(), local_dest ); - if( !path.empty() ) { - if( std::any_of( path.begin(), path.end(), get_path_avoid() ) ) { - path.clear(); - } + for (const tripoint_bub_ms& p : here.straight_route(pos_bub(), tripoint_bub_ms(local_dest), + settings)) { + path.push_back(p.raw()); } } if( !path.empty() ) { reset_pathfinding_cd(); } + settings.set_avoid_bashing(true); } // Try to respect old paths, even if we can't pathfind at the moment @@ -1077,6 +1041,7 @@ void monster::move() } tripoint_abs_ms next_step; + const tripoint starting_pos = pos(); const bool can_open_doors = has_flag( mon_flag_CAN_OPEN_DOORS ) && !is_hallucination(); const bool staggers = has_flag( mon_flag_STUMBLES ); if( moved ) { @@ -1086,8 +1051,8 @@ void monster::move() const bool can_bash = bash_skill() > 0; // This is a float and using trig_dist() because that Does the Right Thing(tm) // in both circular and roguelike distance modes. - const float distance_to_target = trig_dist( pos(), destination ); - for( tripoint &candidate : squares_closer_to( pos(), destination ) ) { + const float distance_to_target = trig_dist(starting_pos, destination); + for (tripoint& candidate : squares_closer_to(starting_pos, destination)) { // rare scenario when monster is on the border of the map and it's goal is outside of the map if( !here.inbounds( candidate ) ) { continue; @@ -1106,19 +1071,19 @@ void monster::move() } const tripoint_abs_ms candidate_abs = get_map().getglobal( candidate ); - if( candidate.z != posz() ) { + if( candidate.z != starting_pos.z ) { bool can_z_move = true; - if( !here.valid_move( pos(), candidate, false, true, via_ramp ) ) { + if( !here.valid_move( starting_pos, candidate, false, true, via_ramp ) ) { // Can't phase through floor can_z_move = false; } // If we're trying to go up but can't fly, check if we can climb. If we can't, then don't // This prevents non-climb/fly enemies running up walls - if( candidate.z > posz() && !( via_ramp || flies() ) ) { + if( candidate.z > starting_pos.z && !( via_ramp || flies() ) ) { if( !can_climb() || !here.has_floor_or_support( candidate ) ) { - if( ( !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, pos() ) || - !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, candidate ) ) ) { + if( !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, starting_pos ) || + !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, candidate ) ) { // Can't "jump" up a whole z-level can_z_move = false; } @@ -1130,8 +1095,8 @@ void monster::move() if( !can_z_move && posx() / ( SEEX * 2 ) == candidate.x / ( SEEX * 2 ) && posy() / ( SEEY * 2 ) == candidate.y / ( SEEY * 2 ) ) { - const tripoint upper = candidate.z > posz() ? candidate : pos(); - const tripoint lower = candidate.z > posz() ? pos() : candidate; + const tripoint upper = candidate.z > starting_pos.z ? candidate : starting_pos; + const tripoint lower = candidate.z > starting_pos.z ? starting_pos : candidate; if( here.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, upper ) && here.has_flag( ter_furn_flag::TFLAG_GOES_UP, lower ) ) { can_z_move = true; @@ -1175,7 +1140,7 @@ void monster::move() // is there an openable door? if( can_open_doors && - here.open_door( *this, candidate, !here.is_outside( pos() ), true ) ) { + here.open_door( *this, candidate, !here.is_outside( starting_pos ), true ) ) { moved = true; next_step = candidate_abs; continue; @@ -1184,7 +1149,7 @@ void monster::move() // Try to shove vehicle out of the way shove_vehicle( destination, candidate ); // Bail out if we can't move there and we can't bash. - if( !pathed && !can_move_to( candidate ) ) { + if( !pathed && !can_move_to( candidate, settings ) ) { if( !can_bash ) { continue; } @@ -1244,6 +1209,14 @@ void monster::move() } else if( drag_to != get_location() && creatures.creature_at( drag_to ) == nullptr ) { dragged_foe->move_to( drag_to ); } + + // If we ended up in a place we didn't plan to, maybe we stumbled or fell, either way the path is + // invalid now. + const tripoint new_pos = pos(); + if (!path.empty() && path.front() != new_pos && new_pos != starting_pos) { + path.clear(); + } + } } else { moves = 0; @@ -1486,11 +1459,12 @@ tripoint monster::scent_move() } } + const PathfindingSettings settings = get_pathfinding_settings(); for( const tripoint &direction : sdirection ) { // Add some randomness to make creatures zigzag towards the source for( const tripoint &dest : here.points_in_radius( direction, 1 ) ) { if( here.valid_move( pos(), dest, can_bash, true ) && - ( can_move_to( dest ) || ( dest == player_character.pos() ) || + ( can_move_to( dest, settings ) || ( dest == player_character.pos() ) || ( can_bash && here.bash_rating( bash_estimate(), dest ) > 0 ) ) ) { smoves.push_back( dest ); } @@ -2192,10 +2166,11 @@ void monster::stumble() valid_stumbles.push_back( below ); } + const PathfindingSettings settings = get_pathfinding_settings(); creature_tracker &creatures = get_creature_tracker(); while( !valid_stumbles.empty() && !is_dead() ) { const tripoint dest = random_entry_removed( valid_stumbles ); - if( can_move_to( dest ) && + if( can_move_to( dest, settings ) && //Stop zombies and other non-breathing monsters wandering INTO water //(Unless they can swim/are aquatic) //But let them wander OUT of water if they are there. @@ -2285,76 +2260,6 @@ void monster::knock_back_to( const tripoint &to ) check_dead_state(); } -/* will_reach() is used for determining whether we'll get to stairs (and - * potentially other locations of interest). It is generally permissive. - * TODO: Pathfinding; - Make sure that non-smashing monsters won't "teleport" through windows - Injure monsters if they're gonna be walking through pits or whatever - */ -bool monster::will_reach( const point &p ) -{ - monster_attitude att = attitude( &get_player_character() ); - if( att != MATT_FOLLOW && att != MATT_ATTACK && att != MATT_FRIEND ) { - return false; - } - - if( digs() || has_flag( mon_flag_AQUATIC ) ) { - return false; - } - - if( ( has_flag( mon_flag_IMMOBILE ) || has_flag( mon_flag_RIDEABLE_MECH ) ) && - ( pos().xy() != p ) ) { - return false; - } - - auto path = get_map().route( pos(), tripoint( p, posz() ), get_pathfinding_settings() ); - if( path.empty() ) { - return false; - } - - if( has_flag( mon_flag_SMELLS ) && get_scent().get( pos() ) > 0 && - get_scent().get( { p, posz() } ) > get_scent().get( pos() ) ) { - return true; - } - - if( can_hear() && wandf > 0 && rl_dist( get_map().getlocal( wander_pos ).xy(), p ) <= 2 && - rl_dist( get_location().xy(), wander_pos.xy() ) <= wandf ) { - return true; - } - - if( can_see() && sees( tripoint( p, posz() ) ) ) { - return true; - } - - return false; -} - -int monster::turns_to_reach( const point &p ) -{ - map &here = get_map(); - // HACK: This function is a(n old) temporary hack that should soon be removed - auto path = here.route( pos(), tripoint( p, posz() ), get_pathfinding_settings() ); - if( path.empty() ) { - return 999; - } - - double turns = 0.; - for( size_t i = 0; i < path.size(); i++ ) { - const tripoint &next = path[i]; - if( here.impassable( next ) ) { - // No bashing through, it looks stupid when you go back and find - // the doors intact. - return 999; - } else if( i == 0 ) { - turns += static_cast( calc_movecost( pos(), next ) ) / get_speed(); - } else { - turns += static_cast( calc_movecost( path[i - 1], next ) ) / get_speed(); - } - } - - return static_cast( turns + .9 ); // Halve (to get turns) and round up -} - void monster::shove_vehicle( const tripoint &remote_destination, const tripoint &nearby_destination ) { diff --git a/src/monster.cpp b/src/monster.cpp index 9b43213341c88..9c65ecd650e5a 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1343,8 +1343,8 @@ bool monster::has_intelligence() const has_flag( mon_flag_PATH_AVOID_FIRE ) || has_flag( mon_flag_PATH_AVOID_DANGER ) || has_flag( mon_flag_PRIORITIZE_TARGETS ) || - get_pathfinding_settings().avoid_sharp || - get_pathfinding_settings().avoid_traps; + type->path_settings.avoid_sharp || + type->path_settings.avoid_traps; } std::vector monster::get_absorb_material() const @@ -1384,7 +1384,7 @@ tripoint_abs_ms monster::get_dest() const void monster::set_dest( const tripoint_abs_ms &p ) { - if( !goal || rl_dist( p, *goal ) > 2 ) { + if (!goal || rl_dist(p, *goal) > 4) { reset_pathfinding_cd(); } goal = p; @@ -3943,36 +3943,6 @@ void monster::on_load() name(), to_turns( dt ), healed, healed_speed ); } -const pathfinding_settings &monster::get_pathfinding_settings() const -{ - return type->path_settings; -} - -std::function monster::get_path_avoid() const -{ - return [this]( const tripoint & p ) { - map &here = get_map(); - // If we can't move there and can't bash it, don't path through it. - if( !can_move_to( p ) && ( bash_skill() <= 0 || !here.is_bashable( p ) ) ) { - return true; - } - - // Avoid nearby creatures if we have the flag. - int radius; - if( has_flag( mon_flag_PRIORITIZE_TARGETS ) ) { - radius = 2; - } else if( has_flag( mon_flag_PATH_AVOID_DANGER ) ) { - radius = 1; - } else { - return false; - } - if( rl_dist( p, pos() ) <= radius && get_creature_tracker().creature_at( p ) ) { - return true; - } - return false; - }; -} - double monster::calculate_by_enchantment( double modify, enchant_vals::mod value, bool round_output ) const { diff --git a/src/monster.h b/src/monster.h index fe096e1577c02..6db678c1bf296 100644 --- a/src/monster.h +++ b/src/monster.h @@ -15,12 +15,14 @@ #include #include "calendar.h" +#include "cata_utility.h" #include "character_id.h" #include "color.h" #include "compatibility.h" #include "creature.h" #include "damage.h" #include "enums.h" +#include "pathfinding.h" #include "point.h" #include "type_id.h" #include "units_fwd.h" @@ -39,7 +41,6 @@ namespace catacurses class window; } // namespace catacurses struct dealt_projectile_attack; -struct pathfinding_settings; struct trap; enum class mon_trigger : int; @@ -204,18 +205,19 @@ class monster : public Creature * * This is used in pathfinding and ONLY checks the terrain. It ignores players * and monsters, which might only block this tile temporarily. - * will_move_to() checks for impassable terrain etc - * can_reach_to() checks for z-level difference. - * can_move_to() is a wrapper for both of them. - * know_danger_at() checks for fire, trap etc. (flag PATH_AVOID_) + * + * If called multiple times in a loop, prefer getting the settings separately and + * reusing them. */ - bool can_move_to( const tripoint &p ) const; - bool can_reach_to( const tripoint &p ) const; - bool will_move_to( const tripoint &p ) const; - bool know_danger_at( const tripoint &p ) const; + bool can_move_to(const tripoint& p) const { + return can_move_to(p, get_pathfinding_settings()); + } + bool can_move_to(const tripoint& p, const PathfindingSettings& settings) const; + + // Get the pathfinding settings for this monster. + PathfindingSettings get_pathfinding_settings(bool avoid_bashing = true) const; - bool will_reach( const point &p ); // Do we have plans to get to (x, y)? - int turns_to_reach( const point &p ); // How long will it take? + bool monster_move_in_vehicle(const tripoint& p) const; // Returns true if the monster has a current goal bool has_dest() const; @@ -603,8 +605,6 @@ class monster : public Creature */ void on_load(); - const pathfinding_settings &get_pathfinding_settings() const override; - std::function get_path_avoid() const override; double calculate_by_enchantment( double modify, enchant_vals::mod value, bool round_output = false ) const; private: diff --git a/src/npcmove.cpp b/src/npcmove.cpp index eb00d020961d7..833fd946f90c1 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -705,11 +705,13 @@ std::optional npc_short_term_cache::closest_enemy_to_friendly_distance() co return distance; } +// PERF: 52.61% void npc::assess_danger() { float highest_priority = 1.0f; int hostile_count = 0; // for tallying nearby threatening enemies int friendly_count = 1; // count yourself as a friendly + // PERF: 2.20% int def_radius = rules.has_flag( ally_rule::follow_close ) ? follow_distance() : 6; bool npc_ranged = get_wielded_item() && get_wielded_item()->is_gun(); @@ -726,10 +728,12 @@ void npc::assess_danger() preferred_close_range = std::min( preferred_close_range, preferred_medium_range / 2 ); Character &player_character = get_player_character(); + // PERF: 1.19% bool sees_player = sees( player_character.pos() ); const bool self_defense_only = rules.engagement == combat_engagement::NO_MOVE || rules.engagement == combat_engagement::NONE; const bool no_fighting = rules.has_flag( ally_rule::forbid_engage ); + // PERF: 1.25% const bool must_retreat = rules.has_flag( ally_rule::follow_close ) && !too_close( pos(), player_character.pos(), follow_distance() ) && !is_guarding(); @@ -801,6 +805,7 @@ void npc::assess_danger() if( &guy == this ) { continue; } + // PERF: 31.03% (has_potential_los()) if( !clairvoyant && !here.has_potential_los( pos(), guy.pos() ) ) { continue; } @@ -819,6 +824,7 @@ void npc::assess_danger() } for( const monster &critter : g->all_monsters() ) { + // PERF: 10.27% (has_potential_los()) if( !clairvoyant && !here.has_potential_los( pos(), critter.pos() ) ) { continue; } @@ -2828,8 +2834,7 @@ bool npc::update_path( const tripoint &p, const bool no_bashing, bool force ) } } - std::vector new_path = get_map().route( pos(), p, get_pathfinding_settings( no_bashing ), - get_path_avoid() ); + std::vector new_path = get_map().route( pos(), p, get_pathfinding_settings( no_bashing )); if( new_path.empty() ) { if( !ai_cache.sound_alerts.empty() ) { ai_cache.sound_alerts.erase( ai_cache.sound_alerts.begin() ); @@ -4993,7 +4998,7 @@ void npc::go_to_omt_destination() } } } - path = here.route( pos(), centre_sub, get_pathfinding_settings(), get_path_avoid() ); + path = here.route( pos(), centre_sub, get_pathfinding_settings()); add_msg_debug( debugmode::DF_NPC, "%s going %s->%s", get_name(), omt_pos.to_string_writable(), goal.to_string_writable() ); diff --git a/src/npctrade_utils.cpp b/src/npctrade_utils.cpp index 98eb063fea8c0..c406cf529ae22 100644 --- a/src/npctrade_utils.cpp +++ b/src/npctrade_utils.cpp @@ -94,8 +94,7 @@ void add_fallback_zone( npc &guy ) ( here.furn( t_here )->max_volume > ter_t_floor->max_volume || here.furn( t_here )->has_flag( ter_furn_flag::TFLAG_CONTAINER ) ) && here.can_put_items_ter_furn( t_here ) && - !here.route( guy.pos_bub(), t_here, guy.get_pathfinding_settings(), - guy.get_path_avoid() ) + !here.route( guy.pos_bub(), t_here, guy.get_pathfinding_settings()) .empty() ) { points.emplace_back( t ); } diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 72dfc31d3c4ec..7dafd49cd0b19 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -27,6 +27,1056 @@ #include "vehicle.h" #include "vpart_position.h" +static const ter_id ter_t_pit = ter_id("t_pit"); +static const ter_id ter_t_pit_spiked = ter_id("t_pit_spiked"); +static const ter_id ter_t_pit_glass = ter_id("t_pit_glass"); +static const ter_id ter_t_lava = ter_id("t_lava"); + +RealityBubblePathfindingCache::RealityBubblePathfindingCache() +{ + for (int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; ++z) { + dirty_z_levels_.emplace(z); + } +} + +// Modifies `t` to point to a tile with `flag` in a 1-submap radius of `t`'s original value, searching +// nearest points first (starting with `t` itself). +// Returns false if it could not find a suitable point +bool RealityBubblePathfindingCache::vertical_move_destination(const map& here, ter_furn_flag flag, + tripoint& t) const +{ + const int z = t.z; + if (const std::optional p = find_point_closest_first(t.xy(), 0, SEEX, [&here, flag, + z](const point& p) { + if (p.x >= 0 && p.x < MAPSIZE_X && p.y >= 0 && p.y < MAPSIZE_Y) { + const tripoint t2(p, z); + return here.has_flag(flag, t2); + } + return false; + })) { + t = tripoint(*p, z); + return true; + } + return false; +} + +void RealityBubblePathfindingCache::update(const map& here, int min_z, int max_z) +{ + if (dirty_z_levels_.empty() && dirty_positions_.empty()) { + return; + } + + cata_assert(min_z <= max_z); + + const int size = here.getmapsize(); + for (const int z : dirty_z_levels_) { + if (z < min_z || z > max_z || !here.inbounds_z(z)) { + continue; + } + for (int y = 0; y < size * SEEY; ++y) { + for (int x = 0; x < size * SEEX; ++x) { + const tripoint_bub_ms p(x, y, z); + invalidate_dependants(p); + update(here, p); + } + } + } + for (const int z : dirty_z_levels_) { + if (min_z <= z && z <= max_z) { + dirty_positions_.erase(z); + } + } + + for (const auto& [z, dirty_points] : dirty_positions_) { + if (z < min_z || z > max_z || !here.inbounds_z(z)) { + continue; + } + for (const point_bub_ms& p : dirty_points) { + tripoint_bub_ms t(p, z); + if (here.inbounds(t)) { + update(here, t); + } + } + } + + for (int i = min_z; i <= max_z; ++i) { + dirty_z_levels_.erase(i); + dirty_positions_.erase(i); + } +} + +void RealityBubblePathfindingCache::update(const map& here, const tripoint_bub_ms& p) +{ + PathfindingFlags flags; + + const const_maptile& tile = here.maptile_at(p); + const ter_t& terrain = tile.get_ter_t(); + const ter_id terrain_id = tile.get_ter(); + const furn_t& furniture = tile.get_furn_t(); + const optional_vpart_position veh = here.veh_at(p); + + const int orig_cost = here.move_cost(p); + const int cost = orig_cost < 0 ? 0 : orig_cost; + + if (cost > 2) { + flags |= PathfindingFlag::Slow; + } + + if (!terrain.has_flag(ter_furn_flag::TFLAG_BURROWABLE) && + !terrain.has_flag(ter_furn_flag::TFLAG_DIGGABLE)) { + flags |= PathfindingFlag::HardGround; + } + + if (veh) { + flags |= PathfindingFlag::Vehicle; + } + + bool try_to_bash = false; + bool impassable = flags.is_set(PathfindingFlag::HardGround); + if (cost == 0) { + flags |= PathfindingFlag::Obstacle; + try_to_bash = true; + if (terrain.open || furniture.open) { + impassable = false; + flags |= PathfindingFlag::Door; + + if (terrain.has_flag(ter_furn_flag::TFLAG_OPENCLOSE_INSIDE) || + furniture.has_flag(ter_furn_flag::TFLAG_OPENCLOSE_INSIDE)) { + flags |= PathfindingFlag::InsideDoor; + } + } + + if (veh) { + if (const auto vpobst = veh->obstacle_at_part()) { + const int vpobst_i = vpobst->part_index(); + const vehicle& v = veh->vehicle(); + const int open_inside = v.next_part_to_open(vpobst_i, false); + if (open_inside != -1) { + impassable = false; + flags |= PathfindingFlag::Door; + + const int open_outside = v.next_part_to_open(vpobst_i, true); + if (open_inside != open_outside) { + flags |= PathfindingFlag::InsideDoor; + } + const int lock = v.next_part_to_unlock(vpobst_i, false); + if (lock != -1) { + flags |= PathfindingFlag::LockedDoor; + } + } + } + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_CLIMBABLE)) { + impassable = false; + flags |= PathfindingFlag::Climbable; + } + } + else { + impassable = false; + // Size restrictions only apply to otherwise passable tiles. + if (terrain.has_flag(ter_furn_flag::TFLAG_SMALL_PASSAGE)) { + flags |= PathfindingFlag::RestrictLarge | PathfindingFlag::RestrictHuge; + } + if (veh) { + if (auto cargo = veh->cargo(); cargo && !cargo->has_feature("CARGO_PASSABLE")) { + const units::volume free_volume = cargo->items().free_volume(); + if (free_volume < 11719_ml) { + flags |= PathfindingFlag::RestrictTiny; + } + if (free_volume < 23438_ml) { + flags |= PathfindingFlag::RestrictSmall; + } + if (free_volume < 46875_ml) { + flags |= PathfindingFlag::RestrictMedium; + } + if (free_volume < 93750_ml) { + flags |= PathfindingFlag::RestrictLarge; + } + if (free_volume < 187500_ml) { + flags |= PathfindingFlag::RestrictHuge; + } + } + } + + if (flags & PathfindingSettings::AnySizeRestriction) { + try_to_bash = true; + } + } + + if (try_to_bash && here.is_bashable(p.raw())) { + if (const auto bash_range = here.bash_range(p.raw())) { + flags |= PathfindingFlag::Bashable; + impassable = false; + bash_range_ref(p) = *bash_range; + } + } + + if (impassable) { + flags |= PathfindingFlag::Impassable; + } + + if (cost > std::numeric_limits::max()) { + debugmsg("Tile move cost too large for cache: %s, %d", terrain_id.id().str(), cost); + } + else { + move_cost_ref(p) = cost < 2 ? 2 : cost; + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_NO_FLOOR)) { + flags |= PathfindingFlag::Air; + } + else if (terrain.has_flag(ter_furn_flag::TFLAG_SWIMMABLE)) { + flags |= PathfindingFlag::Swimmable; + } + else { + flags |= PathfindingFlag::Ground; + } + + const bool has_vehicle_floor = here.has_vehicle_floor(p); + if (!has_vehicle_floor) { + if (terrain_id == ter_t_pit || terrain_id == ter_t_pit_spiked || terrain_id == ter_t_pit_glass) { + flags |= PathfindingFlag::Pit; + } + + if (terrain_id == ter_t_lava) { + flags |= PathfindingFlag::Lava; + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_DEEP_WATER)) { + flags |= PathfindingFlag::DeepWater; + } + + if (!tile.get_trap_t().is_benign()) { + flags |= PathfindingFlag::DangerousTrap; + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_SHARP)) { + flags |= PathfindingFlag::Sharp; + } + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_BURROWABLE)) { + flags |= PathfindingFlag::Burrowable; + } + + if (!g->is_sheltered(p.raw())) { + flags |= PathfindingFlag::Unsheltered; + } + + for (const auto& fld : tile.get_field()) { + const field_entry& cur = fld.second; + if (cur.is_dangerous()) { + flags |= PathfindingFlag::DangerousField; + break; + } + } + + if (p.z() < OVERMAP_HEIGHT) { + up_destinations_.erase(p); + const tripoint_bub_ms up(p.xy(), p.z() + 1); + if (terrain.has_flag(ter_furn_flag::TFLAG_GOES_UP)) { + if (std::optional dest = g->find_stairs(here, up.z(), p.raw())) { + if (vertical_move_destination(here, ter_furn_flag::TFLAG_GOES_DOWN, *dest)) { + tripoint_bub_ms d(*dest); + flags |= PathfindingFlag::GoesUp; + up_destinations_.emplace(p, d); + dependants_by_position_[d].push_back(p); + } + } + else { + dependants_by_position_[up].push_back(p); + } + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_RAMP) || + terrain.has_flag(ter_furn_flag::TFLAG_RAMP_UP)) { + dependants_by_position_[up].push_back(p); + if (here.valid_move(p.raw(), up.raw(), false, true, true)) { + flags |= PathfindingFlag::RampUp; + } + } + } + + if (p.z() > -OVERMAP_DEPTH) { + down_destinations_.erase(p); + const tripoint_bub_ms down(p.xy(), p.z() - 1); + if (terrain.has_flag(ter_furn_flag::TFLAG_GOES_DOWN)) { + if (std::optional dest = g->find_stairs(here, down.z(), p.raw())) { + if (vertical_move_destination(here, ter_furn_flag::TFLAG_GOES_UP, *dest)) { + tripoint_bub_ms d(*dest); + flags |= PathfindingFlag::GoesDown; + down_destinations_.emplace(p, d); + dependants_by_position_[d].push_back(p); + } + } + else { + dependants_by_position_[down].push_back(p); + } + } + + if (terrain.has_flag(ter_furn_flag::TFLAG_RAMP_DOWN)) { + dependants_by_position_[down].push_back(p); + if (here.valid_move(p.raw(), down.raw(), false, true, true)) { + flags |= PathfindingFlag::RampDown; + } + } + } + + flags_ref(p) = flags; +} + +void PathfindingSettings::set_size_restriction(std::optional size_restriction) +{ + size_restriction_mask_.clear(); + if (size_restriction) { + switch (*size_restriction) { + case creature_size::tiny: + size_restriction_mask_ = PathfindingFlag::RestrictTiny; + break; + case creature_size::small: + size_restriction_mask_ = PathfindingFlag::RestrictSmall; + break; + case creature_size::medium: + size_restriction_mask_ = PathfindingFlag::RestrictMedium; + break; + case creature_size::large: + size_restriction_mask_ = PathfindingFlag::RestrictLarge; + break; + case creature_size::huge: + size_restriction_mask_ = PathfindingFlag::RestrictHuge; + break; + default: + break; + } + } + size_restriction_ = size_restriction; +} + +int PathfindingSettings::bash_rating_from_range(int min, int max) const +{ + if (avoid_bashing_) { + return 0; + } + // TODO: Move all the bash stuff to map so this logic isn't duplicated. + ///\EFFECT_STR increases smashing damage + if (bash_strength_ < min) { + return 0; + } + else if (bash_strength_ >= max) { + return 10; + } + const double ret = (10.0 * (bash_strength_ - min)) / (max - min); + // Round up to 1, so that desperate NPCs can try to bash down walls + return std::max(ret, 1.0); +} + +std::pair +RealityBubblePathfinder::FastTripointSet::emplace(const tripoint_bub_ms& p) +{ + const int z = p.z() + OVERMAP_DEPTH; + dirty_[z] = true; + // Note that this is a reference into the bitset, despite not being a reference. + // NOLINTNEXTLINE(cata-almost-never-auto) + auto ref = set_[z][p.y() * MAPSIZE_X + p.x()]; + const bool missing = !ref; + ref = true; + return std::make_pair(NotIterator(), missing); +} + +void RealityBubblePathfinder::FastTripointSet::clear() +{ + for (int z = 0; z < OVERMAP_LAYERS; ++z) { + if (!dirty_[z]) { + continue; + } + dirty_[z] = false; + set_[z].reset(); + } +} + +std::pair*, bool> +RealityBubblePathfinder::FastBestPathMap::try_emplace(const tripoint_bub_ms& child, int cost, + const tripoint_bub_ms& parent) +{ + std::pair& result = best_states_[child.z() + + OVERMAP_DEPTH][child.y()][child.x()]; + if (const auto [_, inserted] = in_.emplace(child); inserted) { + result.first = cost; + result.second = parent; + return std::make_pair(&result, true); + } + return std::make_pair(&result, false); +} + +namespace +{ + + constexpr int one_axis = 50; + constexpr int two_axis = 71; + constexpr int three_axis = 87; + + int adjacent_octile_distance(const tripoint_bub_ms& from, const tripoint_bub_ms& to) + { + switch (std::abs(from.x() - to.x()) + std::abs(from.y() - to.y()) + std::abs( + from.z() - to.z())) { + case 1: + return one_axis; + case 2: + return two_axis; + case 3: + return three_axis; + default: + return 0; + } + } + + int octile_distance(const tripoint_bub_ms& from, const tripoint_bub_ms& to) + { + const tripoint d(std::abs(from.x() - to.x()), std::abs(from.y() - to.y()), + std::abs(from.z() - to.z())); + const int min = std::min(d.x, std::min(d.y, d.z)); + const int max = std::max(d.x, std::max(d.y, d.z)); + const int mid = d.x + d.y + d.z - min - max; + return (three_axis - two_axis) * min + (two_axis - one_axis) * mid + one_axis * max; + } + + int adjacent_distance_metric(const tripoint_bub_ms& from, const tripoint_bub_ms& to) + { + return trigdist ? adjacent_octile_distance(from, to) : 50 * square_dist(from, to); + } + + int distance_metric(const tripoint_bub_ms& from, const tripoint_bub_ms& to) + { + return trigdist ? octile_distance(from, to) : 50 * square_dist(from, to); + } + + std::optional position_cost(const map& here, const tripoint_bub_ms& p, + const PathfindingSettings& settings, const RealityBubblePathfindingCache& cache) + { + const PathfindingFlags flags = cache.flags(p); + if (flags & settings.avoid_mask()) { + return std::nullopt; + } + + std::optional cost; + if (flags & (PathfindingFlag::Obstacle | PathfindingSettings::AnySizeRestriction)) { + if (flags.is_set(PathfindingFlag::Obstacle)) { + if (settings.is_digging()) { + if (!flags.is_set(PathfindingFlag::Burrowable)) { + return std::nullopt; + } + cost = 0; + } + else { + if (flags.is_set(PathfindingFlag::Door) && !settings.avoid_opening_doors() && + (!flags.is_set(PathfindingFlag::LockedDoor) || !settings.avoid_unlocking_doors())) { + const int this_cost = flags.is_set(PathfindingFlag::LockedDoor) ? 4 : 2; + cost = std::min(this_cost, cost.value_or(this_cost)); + } + if (flags.is_set(PathfindingFlag::Climbable) && !settings.avoid_climbing() && + settings.climb_cost() > 0) { + // Climbing fences + const int this_cost = settings.climb_cost(); + cost = std::min(this_cost, cost.value_or(this_cost)); + } + } + } + + if (flags.is_set(PathfindingFlag::Bashable)) { + const auto [bash_min, bash_max] = cache.bash_range(p); + const int bash_rating = settings.bash_rating_from_range(bash_min, bash_max); + if (bash_rating >= 1) { + // Expected number of turns to bash it down + const int this_cost = 20 / bash_rating; + cost = std::min(this_cost, cost.value_or(this_cost)); + } + } + + // Don't check size restrictions if we can bash it down, open it, or climb over it. + if (!cost && (flags & PathfindingSettings::AnySizeRestriction)) { + // Any tile with a size restriction is passable otherwise. + if (flags & settings.size_restriction_mask()) { + return std::nullopt; + } + cost = 0; + } + + if (!cost) { + // Can't enter the tile at all. + return std::nullopt; + } + } + else { + cost = 0; + } + + const auto& maybe_avoid_dangerous_fields_fn = settings.maybe_avoid_dangerous_fields_fn(); + if (flags.is_set(PathfindingFlag::DangerousField) && maybe_avoid_dangerous_fields_fn) { + const field& target_field = here.field_at(p.raw()); + for (const auto& dfield : target_field) { + if (dfield.second.is_dangerous() && maybe_avoid_dangerous_fields_fn(dfield.first)) { + return std::nullopt; + } + } + } + + const auto& maybe_avoid_fn = settings.maybe_avoid_fn(); + if (maybe_avoid_fn && maybe_avoid_fn(p)) { + return std::nullopt; + } + + return *cost * 50; + } + + std::optional transition_cost(const map& here, const tripoint_bub_ms& from, + const tripoint_bub_ms& to, const PathfindingSettings& settings, + const RealityBubblePathfindingCache& cache) + { + const PathfindingFlags from_flags = cache.flags(from); + const bool is_falling = from_flags.is_set(PathfindingFlag::Air) && !settings.is_flying(); + if (is_falling) { + // Can only fall straight down. + if (from.z() < to.z() || from.xy() != to.xy()) { + return std::nullopt; + } + } + + const bool is_vertical_movement = from.z() != to.z(); + if (!is_falling && is_vertical_movement) { + const tripoint_bub_ms& upper = from.z() > to.z() ? from : to; + const tripoint_bub_ms& lower = from.z() < to.z() ? from : to; + if (cache.flags(lower).is_set(PathfindingFlag::GoesUp) && + cache.flags(upper).is_set(PathfindingFlag::GoesDown)) { + if (settings.avoid_climb_stairway()) { + return std::nullopt; + } + // Stairs can teleport us, so we need to use non-adjacent calc. + return 2 * distance_metric(from, to); + } + else if (settings.is_flying()) { + const tripoint_bub_ms above_lower(lower.xy(), lower.z() + 1); + if (!(cache.flags(upper).is_set(PathfindingFlag::Air) || + cache.flags(above_lower).is_set(PathfindingFlag::Air))) { + return std::nullopt; + } + } + else if (to.z() > from.z()) { + if (!cache.flags(to).is_set(PathfindingFlag::RampDown)) { + return std::nullopt; + } + } + else if (to.z() < from.z()) { + if (!cache.flags(to).is_set(PathfindingFlag::RampUp)) { + return std::nullopt; + } + } + } + + const PathfindingFlags to_flags = cache.flags(to); + if (to_flags.is_set(PathfindingFlag::Obstacle)) { + // Can't interact with obstacles across z-levels. + // TODO: allow this + if (is_vertical_movement) { + return std::nullopt; + } + if (!settings.is_digging()) { + if (to_flags.is_set(PathfindingFlag::Door) && !settings.avoid_opening_doors() && + (!to_flags.is_set(PathfindingFlag::LockedDoor) || !settings.avoid_unlocking_doors())) { + const bool is_inside_door = to_flags.is_set(PathfindingFlag::InsideDoor); + if (is_inside_door) { + int dummy; + const bool is_vehicle = to_flags.is_set(PathfindingFlag::Vehicle); + const bool is_outside = is_vehicle ? here.veh_at_internal(from.raw(), + dummy) != here.veh_at_internal(to.raw(), dummy) : here.is_outside(from.raw()); + if (is_outside) { + return std::nullopt; + } + } + } + } + } + else if (to_flags.is_set(PathfindingFlag::Air)) { + // This is checking horizontal movement only. + if (settings.avoid_falling() && !settings.is_flying()) { + return std::nullopt; + } + } + + // TODO: Move the move cost cache into map so this logic isn't duplicated. + const int mult = adjacent_distance_metric(from, to); + + // Flying monsters aren't slowed down by terrain. + if (settings.is_flying()) { + return 2 * mult; + } + + const int cost = cache.move_cost(from) + cache.move_cost(to); + return static_cast(mult * cost) / 2; + } + +} // namespace + + +bool map::can_teleport(const tripoint_bub_ms& to, const PathfindingSettings& settings) const +{ + if (!inbounds(to)) { + return false; + } + pathfinding_cache()->update(*this, to.z(), to.z()); + return position_cost(*this, to, settings, *pathfinding_cache()).has_value(); +} + +bool map::can_move(const tripoint_bub_ms& from, const tripoint_bub_ms& to, + const PathfindingSettings& settings) const +{ + if (!inbounds(from) || !inbounds(to)) { + return false; + } + if (from == to) { + return true; + } + pathfinding_cache()->update(*this, std::min(from.z(), to.z()), std::max(from.z(), to.z())); + if (position_cost(*this, to, settings, *pathfinding_cache()).has_value()) { + return transition_cost(*this, from, to, settings, *pathfinding_cache()).has_value(); + } + return false; +} + + +std::optional map::move_cost(const tripoint_bub_ms& from, const tripoint_bub_ms& to, + const PathfindingSettings& settings) const +{ + if (!inbounds(from) || !inbounds(to)) { + return std::nullopt; + } + if (from == to) { + return 0; + } + pathfinding_cache()->update(*this, std::min(from.z(), to.z()), std::max(from.z(), to.z())); + if (const std::optional p_cost = position_cost(*this, to, settings, *pathfinding_cache())) { + if (const std::optional t_cost = transition_cost(*this, from, to, settings, + *pathfinding_cache())) { + return *p_cost + *t_cost; + } + } + return std::nullopt; +} + +// TODO: Find the old straight_route() implementations and merge with the below. +//std::vector map::straight_route(const tripoint& f, const tripoint& t) const +//{ +// const std::vector temp = map::straight_route(tripoint_bub_ms(f), +// tripoint_bub_ms(t)); +// std::vector result; +// result.reserve(temp.size()); +// +// for (const tripoint_bub_ms pt : temp) { +// result.push_back(pt.raw()); +// } +// +// return result; +//} + +std::vector map::straight_route(const tripoint_bub_ms& from, + const tripoint_bub_ms& to, + const PathfindingSettings& settings) const +{ + + if (from == to || !inbounds(from) || !inbounds(to)) { + return {}; + } + + RealityBubblePathfindingCache& cache = *pathfinding_cache(); + const int pad = settings.rb_settings().padding().z(); + cache.update(*this, std::min(from.z(), to.z()) - pad, std::max(from.z(), to.z()) + pad); + + std::vector line_path = line_to(from, to); + const PathfindingFlags avoid = settings.avoid_mask() | PathfindingSettings::RoughTerrain; + // Check all points for all fast avoidance. + if (!std::any_of(line_path.begin(), line_path.end(), [&cache, avoid](const tripoint_bub_ms& p) { + return cache.flags(p) & avoid; + })) { + // Now do the slow check. Check if all the positions are valid. + if (std::all_of(line_path.begin(), line_path.end(), [this, &cache, + &settings](const tripoint_bub_ms& p) { + return position_cost(*this, p, settings, cache); + })) { + // Now check that all the transitions between each position are valid. + const tripoint_bub_ms* prev = &from; + if (std::find_if_not(line_path.begin(), line_path.end(), [this, &prev, &cache, + &settings](const tripoint_bub_ms& p) { + return transition_cost(*this, *std::exchange(prev, &p), p, settings, cache).has_value(); + }) == line_path.end()) { + return line_path; + } + } + } + return {}; +} + +std::vector map::route(const tripoint_bub_ms& from, const tripoint_bub_ms& to, + const PathfindingSettings& settings) const +{ + if (from == to || !inbounds(from) || !inbounds(to)) { + return {}; + } + + RealityBubblePathfindingCache& cache = *pathfinding_cache(); + const int pad = settings.rb_settings().padding().z(); + cache.update(*this, std::min(from.z(), to.z()) - pad, std::max(from.z(), to.z()) + pad); + + // First, check for a simple straight line on flat ground. + if (from.z() == to.z()) { + std::vector line_path = straight_route(from, to, settings); + if (!line_path.empty()) { + return line_path; + } + } + + // If expected path length is greater than max distance, allow only line path, like above + if (rl_dist(from, to) > settings.max_distance()) { + return {}; + } + + return pathfinder()->find_path(settings.rb_settings(), from, to, + [this, &settings, &cache](const tripoint_bub_ms& p) { + return position_cost(*this, p, settings, cache); + }, + [this, &settings, &cache](const tripoint_bub_ms& from, const tripoint_bub_ms& to) { + return transition_cost(*this, from, to, settings, cache); + }, + [](const tripoint_bub_ms& from, const tripoint_bub_ms& to) { + return 2 * distance_metric(from, to); + }); +} + +std::vector map::route(const tripoint& f, const tripoint& t, + const pathfinding_settings& settings) +{ + const PathfindingSettings pf_settings = settings.to_new_pathfinding_settings(); + // TODO: Get rid of this. + const tripoint_bub_ms from = tripoint_bub_ms::make_unchecked(f); + const tripoint_bub_ms to = tripoint_bub_ms::make_unchecked(t); + std::vector bub_route = route(from, to, pf_settings); + std::vector ret(bub_route.size()); + for (tripoint_bub_ms p : bub_route) + { + ret.push_back(p.raw()); + } + return ret; +} + +std::vector map::route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, + const pathfinding_settings& settings) const +{ + const PathfindingSettings pf_settings = settings.to_new_pathfinding_settings(); + return route(f, t, pf_settings); +} + + +// TODO: Check the content of these functions, and merge with the above. +//std::vector map::route(const tripoint& f, const tripoint& t, +// const pathfinding_settings& settings, +// const std::function& avoid) const +//{ +// /* TODO: If the origin or destination is out of bound, figure out the closest +// * in-bounds point and go to that, then to the real origin/destination. +// */ +// std::vector ret; +// +// if (f == t || !inbounds(f)) { +// return ret; +// } +// +// if (!inbounds(t)) { +// tripoint clipped = t; +// clip_to_bounds(clipped); +// return route(f, clipped, settings, avoid); +// } +// // First, check for a simple straight line on flat ground +// // Except when the line contains a pre-closed tile - we need to do regular pathing then +// if (f.z == t.z) { +// auto line_path = straight_route(f, t); +// if (!line_path.empty()) { +// if (std::none_of(line_path.begin(), line_path.end(), avoid)) { +// return line_path; +// } +// } +// } +// +// // If expected path length is greater than max distance, allow only line path, like above +// if (rl_dist(f, t) > settings.max_dist) { +// return ret; +// } +// +// const int max_length = settings.max_length; +// +// const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! +// tripoint min(std::min(f.x, t.x) - pad, std::min(f.y, t.y) - pad, std::min(f.z, t.z)); +// tripoint max(std::max(f.x, t.x) + pad, std::max(f.y, t.y) + pad, std::max(f.z, t.z)); +// clip_to_bounds(min.x, min.y, min.z); +// clip_to_bounds(max.x, max.y, max.z); +// +// pf.reset(min.z, max.z); +// +// pf.add_point(0, 0, f, f); +// +// bool done = false; +// +// do { +// tripoint cur = pf.get_next(); +// +// const int parent_index = flat_index(cur.xy()); +// path_data_layer& layer = pf.get_layer(cur.z); +// if (layer.closed[parent_index]) { +// continue; +// } +// +// if (layer.gscore[parent_index] > max_length) { +// // Shortest path would be too long, return empty vector +// return std::vector(); +// } +// +// if (cur == t) { +// done = true; +// break; +// } +// +// layer.closed[parent_index] = true; +// +// const pathfinding_cache& pf_cache = get_pathfinding_cache_ref(cur.z); +// const pf_special cur_special = pf_cache.special[cur.x][cur.y]; +// +// // 7 3 5 +// // 1 . 2 +// // 6 4 8 +// constexpr std::array x_offset{ { -1, 1, 0, 0, 1, -1, -1, 1 } }; +// constexpr std::array y_offset{ { 0, 0, -1, 1, -1, 1, -1, 1 } }; +// for (size_t i = 0; i < 8; i++) { +// const tripoint p(cur.x + x_offset[i], cur.y + y_offset[i], cur.z); +// const int index = flat_index(p.xy()); +// +// // TODO: Remove this and instead have sentinels at the edges +// if (p.x < min.x || p.x >= max.x || p.y < min.y || p.y >= max.y) { +// continue; +// } +// +// if (p != t && avoid(p)) { +// layer.closed[index] = true; +// continue; +// } +// +// if (layer.closed[index]) { +// continue; +// } +// +// // Penalize for diagonals or the path will look "unnatural" +// int newg = layer.gscore[parent_index] + ((cur.x != p.x && cur.y != p.y) ? 1 : 0); +// +// const pf_special p_special = pf_cache.special[p.x][p.y]; +// const int cost = extra_cost(tripoint_bub_ms(cur), tripoint_bub_ms(p), settings, p_special); +// if (cost < 0) { +// if (cost == PF_IMPASSABLE) { +// layer.closed[index] = true; +// } +// continue; +// } +// newg += cost; +// +// // Special case: pathfinders that avoid traps can avoid ledges by +// // climbing down. This can't be covered by |extra_cost| because it +// // can add a new point to the search. +// if (settings.avoid_traps && (p_special & PF_TRAP)) { +// const const_maptile& tile = maptile_at_internal(p); +// const ter_t& terrain = tile.get_ter_t(); +// const trap& ter_trp = terrain.trap.obj(); +// const trap& trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; +// if (!trp.is_benign() && terrain.has_flag(ter_furn_flag::TFLAG_NO_FLOOR)) { +// // Warning: really expensive, needs a cache +// tripoint below(p.xy(), p.z - 1); +// if (valid_move(p, below, false, true)) { +// if (!has_flag(ter_furn_flag::TFLAG_NO_FLOOR, below)) { +// // Otherwise this would have been a huge fall +// path_data_layer& layer = pf.get_layer(p.z - 1); +// // From cur, not p, because we won't be walking on air +// pf.add_point(layer.gscore[parent_index] + 10, +// layer.score[parent_index] + 10 + 2 * rl_dist(below, t), +// cur, below); +// } +// +// // Close p, because we won't be walking on it +// layer.closed[index] = true; +// continue; +// } +// } +// } +// +// pf.add_point(newg, newg + 2 * rl_dist(p, t), cur, p); +// } +// +// if (!(cur_special & PF_UPDOWN) || !settings.allow_climb_stairs) { +// // The part below is only for z-level pathing +// continue; +// } +// +// bool rope_ladder = false; +// const const_maptile& parent_tile = maptile_at_internal(cur); +// const ter_t& parent_terrain = parent_tile.get_ter_t(); +// if (settings.allow_climb_stairs && cur.z > min.z && +// parent_terrain.has_flag(ter_furn_flag::TFLAG_GOES_DOWN)) { +// std::optional opt_dest = g->find_or_make_stairs(get_map(), +// cur.z - 1, rope_ladder, false, cur); +// if (!opt_dest) { +// continue; +// } +// tripoint dest = opt_dest.value(); +// if (vertical_move_destination(*this, ter_furn_flag::TFLAG_GOES_UP, dest)) { +// if (!inbounds(dest)) { +// continue; +// } +// path_data_layer& layer = pf.get_layer(dest.z); +// pf.add_point(layer.gscore[parent_index] + 2, +// layer.score[parent_index] + 2 * rl_dist(dest, t), +// cur, dest); +// } +// } +// if (settings.allow_climb_stairs && cur.z < max.z && +// parent_terrain.has_flag(ter_furn_flag::TFLAG_GOES_UP)) { +// std::optional opt_dest = g->find_or_make_stairs(get_map(), +// cur.z + 1, rope_ladder, false, cur); +// if (!opt_dest) { +// continue; +// } +// tripoint dest = opt_dest.value(); +// if (vertical_move_destination(*this, ter_furn_flag::TFLAG_GOES_DOWN, dest)) { +// if (!inbounds(dest)) { +// continue; +// } +// path_data_layer& layer = pf.get_layer(dest.z); +// pf.add_point(layer.gscore[parent_index] + 2, +// layer.score[parent_index] + 2 * rl_dist(dest, t), +// cur, dest); +// } +// } +// if (cur.z < max.z && parent_terrain.has_flag(ter_furn_flag::TFLAG_RAMP) && +// valid_move(cur, tripoint(cur.xy(), cur.z + 1), false, true)) { +// path_data_layer& layer = pf.get_layer(cur.z + 1); +// for (size_t it = 0; it < 8; it++) { +// const tripoint above(cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1); +// if (!inbounds(above)) { +// continue; +// } +// pf.add_point(layer.gscore[parent_index] + 4, +// layer.score[parent_index] + 4 + 2 * rl_dist(above, t), +// cur, above); +// } +// } +// if (cur.z < max.z && parent_terrain.has_flag(ter_furn_flag::TFLAG_RAMP_UP) && +// valid_move(cur, tripoint(cur.xy(), cur.z + 1), false, true, true)) { +// path_data_layer& layer = pf.get_layer(cur.z + 1); +// for (size_t it = 0; it < 8; it++) { +// const tripoint above(cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1); +// if (!inbounds(above)) { +// continue; +// } +// pf.add_point(layer.gscore[parent_index] + 4, +// layer.score[parent_index] + 4 + 2 * rl_dist(above, t), +// cur, above); +// } +// } +// if (cur.z > min.z && parent_terrain.has_flag(ter_furn_flag::TFLAG_RAMP_DOWN) && +// valid_move(cur, tripoint(cur.xy(), cur.z - 1), false, true, true)) { +// path_data_layer& layer = pf.get_layer(cur.z - 1); +// for (size_t it = 0; it < 8; it++) { +// const tripoint below(cur.x + x_offset[it], cur.y + y_offset[it], cur.z - 1); +// if (!inbounds(below)) { +// continue; +// } +// pf.add_point(layer.gscore[parent_index] + 4, +// layer.score[parent_index] + 4 + 2 * rl_dist(below, t), +// cur, below); +// } +// } +// +// } while (!done && !pf.empty()); +// +// if (done) { +// ret.reserve(rl_dist(f, t) * 2); +// tripoint cur = t; +// // Just to limit max distance, in case something weird happens +// for (int fdist = max_length; fdist != 0; fdist--) { +// const int cur_index = flat_index(cur.xy()); +// const path_data_layer& layer = pf.get_layer(cur.z); +// const tripoint& par = layer.parent[cur_index]; +// if (cur == f) { +// break; +// } +// +// ret.push_back(cur); +// // Jumps are acceptable on 1 z-level changes +// // This is because stairs teleport the player too +// if (rl_dist(cur, par) > 1 && std::abs(cur.z - par.z) != 1) { +// debugmsg("Jump in our route! %d:%d:%d->%d:%d:%d", +// cur.x, cur.y, cur.z, par.x, par.y, par.z); +// return ret; +// } +// +// cur = par; +// } +// +// std::reverse(ret.begin(), ret.end()); +// } +// +// return ret; +//} + + + +//std::vector map::route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, +// const pathfinding_settings& settings, +// const std::function& avoid) const +//{ +// std::vector raw_result = route(f.raw(), t.raw(), settings, avoid); +// std::vector result; +// std::transform(raw_result.begin(), raw_result.end(), std::back_inserter(result), +// [](const tripoint& p) { +// return tripoint_bub_ms(p); +// }); +// return result; +//} + + + +PathfindingSettings pathfinding_settings::to_new_pathfinding_settings() const +{ + PathfindingSettings settings; + settings.set_bash_strength(bash_strength); + settings.set_max_distance(max_dist); + settings.set_max_cost(max_length * 50); + settings.set_climb_cost(climb_cost); + settings.set_avoid_opening_doors(!allow_open_doors); + settings.set_avoid_unlocking_doors(!allow_unlock_doors); + settings.set_avoid_dangerous_traps(avoid_traps); + if (avoid_rough_terrain) { + settings.set_avoid_rough_terrain(true); + } + settings.set_avoid_sharp(avoid_sharp); + return settings; +} + + +/* + + OLD PATHFINDING!!! + + Removed for now, as none of it is kept in @prharvey's version. + TODO: Merge the logic in this code with the logic in the above pathfinder. + +*/ + + +/* // Turns two indexed to a 2D array into an index to equivalent 1D array static constexpr int flat_index( const point &p ) { @@ -309,310 +1359,310 @@ int map::cost_to_pass( const tripoint &cur, const tripoint &p, const pathfinding } return PF_IMPASSABLE; -} - -int map::cost_to_avoid( const tripoint & /*cur*/, const tripoint &p, - const pathfinding_settings &settings, pf_special p_special ) const -{ - if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { - const const_maptile &tile = maptile_at_internal( p ); - const ter_t &terrain = tile.get_ter_t(); - const trap &ter_trp = terrain.trap.obj(); - const trap &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; - - // NO_FLOOR is a special case handled in map::route - if( !trp.is_benign() && !terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { - return 500; - } - } - - if( settings.avoid_dangerous_fields && ( p_special & PF_FIELD ) ) { - // We'll walk through even known-dangerous fields if we absolutely have to. - return 500; - } - - return 0; -} - -int map::extra_cost( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, - pf_special p_special ) const -{ - int pass_cost = cost_to_pass( cur, p, settings, p_special ); - if( pass_cost < 0 ) { - return pass_cost; - } - - int avoid_cost = cost_to_avoid( cur, p, settings, p_special ); - if( avoid_cost < 0 ) { - return avoid_cost; - } - return pass_cost + avoid_cost; -} - -std::vector map::route( const tripoint &f, const tripoint &t, - const pathfinding_settings &settings, - const std::function &avoid ) const -{ - /* TODO: If the origin or destination is out of bound, figure out the closest - * in-bounds point and go to that, then to the real origin/destination. - */ - std::vector ret; - - if( f == t || !inbounds( f ) ) { - return ret; - } - - if( !inbounds( t ) ) { - tripoint clipped = t; - clip_to_bounds( clipped ); - return route( f, clipped, settings, avoid ); - } - // First, check for a simple straight line on flat ground - // Except when the line contains a pre-closed tile - we need to do regular pathing then - if( f.z == t.z ) { - auto line_path = straight_route( f, t ); - if( !line_path.empty() ) { - if( std::none_of( line_path.begin(), line_path.end(), avoid ) ) { - return line_path; - } - } - } - - // If expected path length is greater than max distance, allow only line path, like above - if( rl_dist( f, t ) > settings.max_dist ) { - return ret; - } - - const int max_length = settings.max_length; - - const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! - tripoint min( std::min( f.x, t.x ) - pad, std::min( f.y, t.y ) - pad, std::min( f.z, t.z ) ); - tripoint max( std::max( f.x, t.x ) + pad, std::max( f.y, t.y ) + pad, std::max( f.z, t.z ) ); - clip_to_bounds( min.x, min.y, min.z ); - clip_to_bounds( max.x, max.y, max.z ); - - pf.reset( min.z, max.z ); - - pf.add_point( 0, 0, f, f ); - - bool done = false; - - do { - tripoint cur = pf.get_next(); - - const int parent_index = flat_index( cur.xy() ); - path_data_layer &layer = pf.get_layer( cur.z ); - if( layer.closed[parent_index] ) { - continue; - } - - if( layer.gscore[parent_index] > max_length ) { - // Shortest path would be too long, return empty vector - return std::vector(); - } - - if( cur == t ) { - done = true; - break; - } - - layer.closed[parent_index] = true; - - const pathfinding_cache &pf_cache = get_pathfinding_cache_ref( cur.z ); - const pf_special cur_special = pf_cache.special[cur.x][cur.y]; - - // 7 3 5 - // 1 . 2 - // 6 4 8 - constexpr std::array x_offset{{ -1, 1, 0, 0, 1, -1, -1, 1 }}; - constexpr std::array y_offset{{ 0, 0, -1, 1, -1, 1, -1, 1 }}; - for( size_t i = 0; i < 8; i++ ) { - const tripoint p( cur.x + x_offset[i], cur.y + y_offset[i], cur.z ); - const int index = flat_index( p.xy() ); - - // TODO: Remove this and instead have sentinels at the edges - if( p.x < min.x || p.x >= max.x || p.y < min.y || p.y >= max.y ) { - continue; - } - - if( p != t && avoid( p ) ) { - layer.closed[index] = true; - continue; - } - - if( layer.closed[index] ) { - continue; - } - - // Penalize for diagonals or the path will look "unnatural" - int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 ); - - const pf_special p_special = pf_cache.special[p.x][p.y]; - const int cost = extra_cost( cur, p, settings, p_special ); - if( cost < 0 ) { - if( cost == PF_IMPASSABLE ) { - layer.closed[index] = true; - } - continue; - } - newg += cost; - - // Special case: pathfinders that avoid traps can avoid ledges by - // climbing down. This can't be covered by |extra_cost| because it - // can add a new point to the search. - if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { - const const_maptile &tile = maptile_at_internal( p ); - const ter_t &terrain = tile.get_ter_t(); - const trap &ter_trp = terrain.trap.obj(); - const trap &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; - if( !trp.is_benign() && terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { - // Warning: really expensive, needs a cache - tripoint below( p.xy(), p.z - 1 ); - if( valid_move( p, below, false, true ) ) { - if( !has_flag( ter_furn_flag::TFLAG_NO_FLOOR, below ) ) { - // Otherwise this would have been a huge fall - path_data_layer &layer = pf.get_layer( p.z - 1 ); - // From cur, not p, because we won't be walking on air - pf.add_point( layer.gscore[parent_index] + 10, - layer.score[parent_index] + 10 + 2 * rl_dist( below, t ), - cur, below ); - } - - // Close p, because we won't be walking on it - layer.closed[index] = true; - continue; - } - } - } - - pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p ); - } - - if( !( cur_special & PF_UPDOWN ) || !settings.allow_climb_stairs ) { - // The part below is only for z-level pathing - continue; - } - - bool rope_ladder = false; - const const_maptile &parent_tile = maptile_at_internal( cur ); - const ter_t &parent_terrain = parent_tile.get_ter_t(); - if( settings.allow_climb_stairs && cur.z > min.z && - parent_terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) ) { - std::optional opt_dest = g->find_or_make_stairs( get_map(), - cur.z - 1, rope_ladder, false, cur ); - if( !opt_dest ) { - continue; - } - tripoint dest = opt_dest.value(); - if( vertical_move_destination( *this, ter_furn_flag::TFLAG_GOES_UP, dest ) ) { - if( !inbounds( dest ) ) { - continue; - } - path_data_layer &layer = pf.get_layer( dest.z ); - pf.add_point( layer.gscore[parent_index] + 2, - layer.score[parent_index] + 2 * rl_dist( dest, t ), - cur, dest ); - } - } - if( settings.allow_climb_stairs && cur.z < max.z && - parent_terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) ) { - std::optional opt_dest = g->find_or_make_stairs( get_map(), - cur.z + 1, rope_ladder, false, cur ); - if( !opt_dest ) { - continue; - } - tripoint dest = opt_dest.value(); - if( vertical_move_destination( *this, ter_furn_flag::TFLAG_GOES_DOWN, dest ) ) { - if( !inbounds( dest ) ) { - continue; - } - path_data_layer &layer = pf.get_layer( dest.z ); - pf.add_point( layer.gscore[parent_index] + 2, - layer.score[parent_index] + 2 * rl_dist( dest, t ), - cur, dest ); - } - } - if( cur.z < max.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) && - valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true ) ) { - path_data_layer &layer = pf.get_layer( cur.z + 1 ); - for( size_t it = 0; it < 8; it++ ) { - const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); - if( !inbounds( above ) ) { - continue; - } - pf.add_point( layer.gscore[parent_index] + 4, - layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), - cur, above ); - } - } - if( cur.z < max.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) && - valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true, true ) ) { - path_data_layer &layer = pf.get_layer( cur.z + 1 ); - for( size_t it = 0; it < 8; it++ ) { - const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); - if( !inbounds( above ) ) { - continue; - } - pf.add_point( layer.gscore[parent_index] + 4, - layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), - cur, above ); - } - } - if( cur.z > min.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) && - valid_move( cur, tripoint( cur.xy(), cur.z - 1 ), false, true, true ) ) { - path_data_layer &layer = pf.get_layer( cur.z - 1 ); - for( size_t it = 0; it < 8; it++ ) { - const tripoint below( cur.x + x_offset[it], cur.y + y_offset[it], cur.z - 1 ); - if( !inbounds( below ) ) { - continue; - } - pf.add_point( layer.gscore[parent_index] + 4, - layer.score[parent_index] + 4 + 2 * rl_dist( below, t ), - cur, below ); - } - } - - } while( !done && !pf.empty() ); - - if( done ) { - ret.reserve( rl_dist( f, t ) * 2 ); - tripoint cur = t; - // Just to limit max distance, in case something weird happens - for( int fdist = max_length; fdist != 0; fdist-- ) { - const int cur_index = flat_index( cur.xy() ); - const path_data_layer &layer = pf.get_layer( cur.z ); - const tripoint &par = layer.parent[cur_index]; - if( cur == f ) { - break; - } - - ret.push_back( cur ); - // Jumps are acceptable on 1 z-level changes - // This is because stairs teleport the player too - if( rl_dist( cur, par ) > 1 && std::abs( cur.z - par.z ) != 1 ) { - debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d", - cur.x, cur.y, cur.z, par.x, par.y, par.z ); - return ret; - } - - cur = par; - } - - std::reverse( ret.begin(), ret.end() ); - } - - return ret; -} - -std::vector map::route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, - const pathfinding_settings &settings, - const std::function &avoid ) const -{ - std::vector raw_result = route( f.raw(), t.raw(), settings, avoid ); - std::vector result; - std::transform( raw_result.begin(), raw_result.end(), std::back_inserter( result ), - []( const tripoint & p ) { - return tripoint_bub_ms( p ); - } ); - return result; -} +} */ + +//int map::cost_to_avoid( const tripoint & /*cur*/, const tripoint &p, +// const pathfinding_settings &settings, pf_special p_special ) const +//{ +// if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { +// const const_maptile &tile = maptile_at_internal( p ); +// const ter_t &terrain = tile.get_ter_t(); +// const trap &ter_trp = terrain.trap.obj(); +// const trap &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; +// +// // NO_FLOOR is a special case handled in map::route +// if( !trp.is_benign() && !terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { +// return 500; +// } +// } +// +// if( settings.avoid_dangerous_fields && ( p_special & PF_FIELD ) ) { +// // We'll walk through even known-dangerous fields if we absolutely have to. +// return 500; +// } +// +// return 0; +//} + +//int map::extra_cost( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, +// pf_special p_special ) const +//{ +// int pass_cost = cost_to_pass( cur, p, settings, p_special ); +// if( pass_cost < 0 ) { +// return pass_cost; +// } +// +// int avoid_cost = cost_to_avoid( cur, p, settings, p_special ); +// if( avoid_cost < 0 ) { +// return avoid_cost; +// } +// return pass_cost + avoid_cost; +//} + +//std::vector map::route( const tripoint &f, const tripoint &t, +// const pathfinding_settings &settings, +// const std::function &avoid ) const +//{ +// /* TODO: If the origin or destination is out of bound, figure out the closest +// * in-bounds point and go to that, then to the real origin/destination. +// */ +// std::vector ret; +// +// if( f == t || !inbounds( f ) ) { +// return ret; +// } +// +// if( !inbounds( t ) ) { +// tripoint clipped = t; +// clip_to_bounds( clipped ); +// return route( f, clipped, settings, avoid ); +// } +// // First, check for a simple straight line on flat ground +// // Except when the line contains a pre-closed tile - we need to do regular pathing then +// if( f.z == t.z ) { +// auto line_path = straight_route( f, t ); +// if( !line_path.empty() ) { +// if( std::none_of( line_path.begin(), line_path.end(), avoid ) ) { +// return line_path; +// } +// } +// } +// +// // If expected path length is greater than max distance, allow only line path, like above +// if( rl_dist( f, t ) > settings.max_dist ) { +// return ret; +// } +// +// const int max_length = settings.max_length; +// +// const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! +// tripoint min( std::min( f.x, t.x ) - pad, std::min( f.y, t.y ) - pad, std::min( f.z, t.z ) ); +// tripoint max( std::max( f.x, t.x ) + pad, std::max( f.y, t.y ) + pad, std::max( f.z, t.z ) ); +// clip_to_bounds( min.x, min.y, min.z ); +// clip_to_bounds( max.x, max.y, max.z ); +// +// pf.reset( min.z, max.z ); +// +// pf.add_point( 0, 0, f, f ); +// +// bool done = false; +// +// do { +// tripoint cur = pf.get_next(); +// +// const int parent_index = flat_index( cur.xy() ); +// path_data_layer &layer = pf.get_layer( cur.z ); +// if( layer.closed[parent_index] ) { +// continue; +// } +// +// if( layer.gscore[parent_index] > max_length ) { +// // Shortest path would be too long, return empty vector +// return std::vector(); +// } +// +// if( cur == t ) { +// done = true; +// break; +// } +// +// layer.closed[parent_index] = true; +// +// const pathfinding_cache &pf_cache = get_pathfinding_cache_ref( cur.z ); +// const pf_special cur_special = pf_cache.special[cur.x][cur.y]; +// +// // 7 3 5 +// // 1 . 2 +// // 6 4 8 +// constexpr std::array x_offset{{ -1, 1, 0, 0, 1, -1, -1, 1 }}; +// constexpr std::array y_offset{{ 0, 0, -1, 1, -1, 1, -1, 1 }}; +// for( size_t i = 0; i < 8; i++ ) { +// const tripoint p( cur.x + x_offset[i], cur.y + y_offset[i], cur.z ); +// const int index = flat_index( p.xy() ); +// +// // TODO: Remove this and instead have sentinels at the edges +// if( p.x < min.x || p.x >= max.x || p.y < min.y || p.y >= max.y ) { +// continue; +// } +// +// if( p != t && avoid( p ) ) { +// layer.closed[index] = true; +// continue; +// } +// +// if( layer.closed[index] ) { +// continue; +// } +// +// // Penalize for diagonals or the path will look "unnatural" +// int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 ); +// +// const pf_special p_special = pf_cache.special[p.x][p.y]; +// const int cost = extra_cost( cur, p, settings, p_special ); +// if( cost < 0 ) { +// if( cost == PF_IMPASSABLE ) { +// layer.closed[index] = true; +// } +// continue; +// } +// newg += cost; +// +// // Special case: pathfinders that avoid traps can avoid ledges by +// // climbing down. This can't be covered by |extra_cost| because it +// // can add a new point to the search. +// if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { +// const const_maptile &tile = maptile_at_internal( p ); +// const ter_t &terrain = tile.get_ter_t(); +// const trap &ter_trp = terrain.trap.obj(); +// const trap &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; +// if( !trp.is_benign() && terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { +// // Warning: really expensive, needs a cache +// tripoint below( p.xy(), p.z - 1 ); +// if( valid_move( p, below, false, true ) ) { +// if( !has_flag( ter_furn_flag::TFLAG_NO_FLOOR, below ) ) { +// // Otherwise this would have been a huge fall +// path_data_layer &layer = pf.get_layer( p.z - 1 ); +// // From cur, not p, because we won't be walking on air +// pf.add_point( layer.gscore[parent_index] + 10, +// layer.score[parent_index] + 10 + 2 * rl_dist( below, t ), +// cur, below ); +// } +// +// // Close p, because we won't be walking on it +// layer.closed[index] = true; +// continue; +// } +// } +// } +// +// pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p ); +// } +// +// if( !( cur_special & PF_UPDOWN ) || !settings.allow_climb_stairs ) { +// // The part below is only for z-level pathing +// continue; +// } +// +// bool rope_ladder = false; +// const const_maptile &parent_tile = maptile_at_internal( cur ); +// const ter_t &parent_terrain = parent_tile.get_ter_t(); +// if( settings.allow_climb_stairs && cur.z > min.z && +// parent_terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) ) { +// std::optional opt_dest = g->find_or_make_stairs( get_map(), +// cur.z - 1, rope_ladder, false, cur ); +// if( !opt_dest ) { +// continue; +// } +// tripoint dest = opt_dest.value(); +// if( vertical_move_destination( *this, ter_furn_flag::TFLAG_GOES_UP, dest ) ) { +// if( !inbounds( dest ) ) { +// continue; +// } +// path_data_layer &layer = pf.get_layer( dest.z ); +// pf.add_point( layer.gscore[parent_index] + 2, +// layer.score[parent_index] + 2 * rl_dist( dest, t ), +// cur, dest ); +// } +// } +// if( settings.allow_climb_stairs && cur.z < max.z && +// parent_terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) ) { +// std::optional opt_dest = g->find_or_make_stairs( get_map(), +// cur.z + 1, rope_ladder, false, cur ); +// if( !opt_dest ) { +// continue; +// } +// tripoint dest = opt_dest.value(); +// if( vertical_move_destination( *this, ter_furn_flag::TFLAG_GOES_DOWN, dest ) ) { +// if( !inbounds( dest ) ) { +// continue; +// } +// path_data_layer &layer = pf.get_layer( dest.z ); +// pf.add_point( layer.gscore[parent_index] + 2, +// layer.score[parent_index] + 2 * rl_dist( dest, t ), +// cur, dest ); +// } +// } +// if( cur.z < max.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) && +// valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true ) ) { +// path_data_layer &layer = pf.get_layer( cur.z + 1 ); +// for( size_t it = 0; it < 8; it++ ) { +// const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); +// if( !inbounds( above ) ) { +// continue; +// } +// pf.add_point( layer.gscore[parent_index] + 4, +// layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), +// cur, above ); +// } +// } +// if( cur.z < max.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) && +// valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true, true ) ) { +// path_data_layer &layer = pf.get_layer( cur.z + 1 ); +// for( size_t it = 0; it < 8; it++ ) { +// const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); +// if( !inbounds( above ) ) { +// continue; +// } +// pf.add_point( layer.gscore[parent_index] + 4, +// layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), +// cur, above ); +// } +// } +// if( cur.z > min.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) && +// valid_move( cur, tripoint( cur.xy(), cur.z - 1 ), false, true, true ) ) { +// path_data_layer &layer = pf.get_layer( cur.z - 1 ); +// for( size_t it = 0; it < 8; it++ ) { +// const tripoint below( cur.x + x_offset[it], cur.y + y_offset[it], cur.z - 1 ); +// if( !inbounds( below ) ) { +// continue; +// } +// pf.add_point( layer.gscore[parent_index] + 4, +// layer.score[parent_index] + 4 + 2 * rl_dist( below, t ), +// cur, below ); +// } +// } +// +// } while( !done && !pf.empty() ); +// +// if( done ) { +// ret.reserve( rl_dist( f, t ) * 2 ); +// tripoint cur = t; +// // Just to limit max distance, in case something weird happens +// for( int fdist = max_length; fdist != 0; fdist-- ) { +// const int cur_index = flat_index( cur.xy() ); +// const path_data_layer &layer = pf.get_layer( cur.z ); +// const tripoint &par = layer.parent[cur_index]; +// if( cur == f ) { +// break; +// } +// +// ret.push_back( cur ); +// // Jumps are acceptable on 1 z-level changes +// // This is because stairs teleport the player too +// if( rl_dist( cur, par ) > 1 && std::abs( cur.z - par.z ) != 1 ) { +// debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d", +// cur.x, cur.y, cur.z, par.x, par.y, par.z ); +// return ret; +// } +// +// cur = par; +// } +// +// std::reverse( ret.begin(), ret.end() ); +// } +// +// return ret; +//}*/ + +//std::vector map::route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, +// const pathfinding_settings &settings, +// const std::function &avoid ) const +//{ +// std::vector raw_result = route( f.raw(), t.raw(), settings, avoid ); +// std::vector result; +// std::transform( raw_result.begin(), raw_result.end(), std::back_inserter( result ), +// []( const tripoint & p ) { +// return tripoint_bub_ms( p ); +// } ); +// return result; +//} diff --git a/src/pathfinding.h b/src/pathfinding.h index e62769ae1042d..5a18b67bb8238 100644 --- a/src/pathfinding.h +++ b/src/pathfinding.h @@ -2,53 +2,733 @@ #ifndef CATA_SRC_PATHFINDING_H #define CATA_SRC_PATHFINDING_H -#include "coords_fwd.h" +#include "a_star.h" +#include "coordinates.h" +#include "creature.h" #include "game_constants.h" -#include "mdarray.h" - -enum pf_special : int { - PF_NORMAL = 0x00, // Plain boring tile (grass, dirt, floor etc.) - PF_SLOW = 0x01, // Tile with move cost >2 - PF_WALL = 0x02, // Unpassable ter/furn/vehicle - PF_VEHICLE = 0x04, // Any vehicle tile (passable or not) - PF_FIELD = 0x08, // Dangerous field - PF_TRAP = 0x10, // Dangerous trap - PF_UPDOWN = 0x20, // Stairs, ramp etc. - PF_CLIMBABLE = 0x40, // 0 move cost but can be climbed on examine - PF_SHARP = 0x80, // sharp items (barbed wire, etc) +#include "mapdata.h" +#include "type_id.h" + +class map; + +enum class PathfindingFlag : uint8_t { + Ground = 0, // Can walk on + Slow, // Move cost > 2 + Swimmable, // Can swim in + Air, // Empty air + Unsheltered, // Outside and above ground level + Obstacle, // Something stopping us, might be bashable. + Bashable, // Something bashable. + Impassable, // Impassable obstacle. + Vehicle, // Vehicle tile (passable or not) + DangerousField, // Dangerous field + DangerousTrap, // Dangerous trap (i.e. not flagged benign) + GoesUp, // Valid stairs up + GoesDown, // Valid stairs down + RampUp, // Valid ramp up + RampDown, // Valid ramp down + Climbable, // Obstacle but can be climbed on examine + Sharp, // Sharp items (barbed wire, etc) + Door, // A door (any kind) + InsideDoor, // A door that can be opened from the inside only + LockedDoor, // A locked door + Pit, // A pit you can fall into / climb out of. + DeepWater, // Deep water. + Burrowable, // Can burrow into + HardGround, // Can not dig & burrow intotiny = 1, + RestrictTiny, // Tiny cannot enter + RestrictSmall, // Small cannot enter + RestrictMedium, // Medium cannot enter + RestrictLarge, // Large cannot enter + RestrictHuge, // Huge cannot enter + Lava, // Lava terrain +}; + +class PathfindingFlags +{ +public: + constexpr PathfindingFlags() = default; + + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr PathfindingFlags(PathfindingFlag flag) : flags_(uint32_t{ 1 } << static_cast + (flag)) {} + + constexpr void set_union(PathfindingFlags flags) { + flags_ |= flags.flags_; + } + constexpr void set_intersect(PathfindingFlags flags) { + flags_ &= flags.flags_; + } + constexpr void set_clear(PathfindingFlags flags) { + flags_ &= ~flags.flags_; + } + + constexpr void clear() { + flags_ = 0; + } + + constexpr bool is_set(PathfindingFlag flag) const { + return flags_ & (uint32_t{ 1 } << static_cast(flag)); + } + constexpr bool is_set(PathfindingFlags flags) const { + return (flags_ & flags.flags_) == flags.flags_; + } + constexpr bool is_any_set() const { + return flags_; + } + + constexpr explicit operator bool() const { + return is_any_set(); + } + + constexpr PathfindingFlags& operator|=(PathfindingFlags flags) { + set_union(flags); + return *this; + } + + constexpr PathfindingFlags& operator&=(PathfindingFlags flags) { + set_intersect(flags); + return *this; + } + +private: + uint32_t flags_ = 0; }; -constexpr pf_special operator | ( pf_special lhs, pf_special rhs ) +constexpr PathfindingFlags operator|(PathfindingFlags lhs, PathfindingFlags rhs) { - return static_cast( static_cast< int >( lhs ) | static_cast< int >( rhs ) ); + return lhs |= rhs; } -constexpr pf_special operator & ( pf_special lhs, pf_special rhs ) +constexpr PathfindingFlags operator&(PathfindingFlags lhs, PathfindingFlags rhs) { - return static_cast( static_cast< int >( lhs ) & static_cast< int >( rhs ) ); + return lhs &= rhs; } -inline pf_special &operator |= ( pf_special &lhs, pf_special rhs ) +constexpr PathfindingFlags operator|(PathfindingFlags lhs, PathfindingFlag rhs) { - lhs = static_cast( static_cast< int >( lhs ) | static_cast< int >( rhs ) ); - return lhs; + return lhs |= rhs; } -inline pf_special &operator &= ( pf_special &lhs, pf_special rhs ) +constexpr PathfindingFlags operator&(PathfindingFlags lhs, PathfindingFlag rhs) { - lhs = static_cast( static_cast< int >( lhs ) & static_cast< int >( rhs ) ); - return lhs; + return lhs &= rhs; } -struct pathfinding_cache { - pathfinding_cache(); +// Note that this is in reverse order for memory locality: z, y, x. +template +using RealityBubbleArray = +std::array, MAPSIZE_Y>, OVERMAP_LAYERS>; + +class RealityBubblePathfindingCache +{ +public: + RealityBubblePathfindingCache(); + + PathfindingFlags flags(const tripoint_bub_ms& p) const { + return flag_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } + + int move_cost(const tripoint_bub_ms& p) const { + return move_cost_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } + + const std::pair& bash_range(const tripoint_bub_ms& p) const { + return bash_range_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } + + const tripoint_bub_ms& up_destination(const tripoint_bub_ms& p) const { + return up_destinations_.find(p)->second; + } + + const tripoint_bub_ms& down_destination(const tripoint_bub_ms& p) const { + return down_destinations_.find(p)->second; + } + + void update(const map& here, int min_z, int max_z); + + void invalidate(int z) { + dirty_z_levels_.emplace(z); + } + + void invalidate(const tripoint_bub_ms& p) { + dirty_positions_[p.z()].emplace(p.xy()); + invalidate_dependants(p); + } + +private: + PathfindingFlags& flags_ref(const tripoint_bub_ms& p) { + return flag_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } + + char& move_cost_ref(const tripoint_bub_ms& p) { + return move_cost_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } + + std::pair& bash_range_ref(const tripoint_bub_ms& p) { + return bash_range_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } + + bool vertical_move_destination(const map& here, ter_furn_flag flag, tripoint& t) const; + + void invalidate_dependants(const tripoint_bub_ms& p); + + void update(const map& here, const tripoint_bub_ms& p); + + std::unordered_set dirty_z_levels_; + std::unordered_map> dirty_positions_; + std::unordered_map> dependants_by_position_; + + std::unordered_map up_destinations_; + std::unordered_map down_destinations_; + RealityBubbleArray flag_cache_; + RealityBubbleArray move_cost_cache_; + RealityBubbleArray> bash_range_cache_; +}; + +class RealityBubblePathfindingSettings +{ +public: + bool allow_flying() const { + return allow_flying_; + } + void set_allow_flying(bool v = true) { + allow_flying_ = v; + } + + bool allow_falling() const { + return allow_falling_; + } + void set_allow_falling(bool v = true) { + allow_falling_ = v; + } + + bool allow_stairways() const { + return allow_stairways_; + } + void set_allow_stairways(bool v = true) { + allow_stairways_ = v; + } + + int max_cost() const { + return max_cost_; + } + void set_max_cost(int v = 0) { + max_cost_ = v; + } + + const tripoint_rel_ms& padding() const { + return padding_; + } + void set_padding(const tripoint_rel_ms& v) { + padding_ = v; + } + +private: + bool allow_flying_ = false; + bool allow_falling_ = false; + bool allow_stairways_ = false; + int max_cost_ = 0; + tripoint_rel_ms padding_ = tripoint_rel_ms(16, 16, 1); +}; + +class RealityBubblePathfinder +{ +public: + // The class uses a lot of memory, and is safe to reuse as long as none of the provided + // functors to find_path make use of it. + explicit RealityBubblePathfinder(RealityBubblePathfindingCache* cache) : cache_(cache) {} + + template + std::vector find_path(const RealityBubblePathfindingSettings& settings, + const tripoint_bub_ms& from, const tripoint_bub_ms& to, + PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, HeuristicFn&& heuristic_fn); + +private: + // Minimum implementation of std::unordered_set interface that is used in the pathfinder. + class FastTripointSet + { + public: + // Empty dummy type to return in place of an iterator. It isn't possible to give a nice + // iterator implementation, and the pathfinder doesn't use it anyways. + struct NotIterator {}; + + std::pair emplace(const tripoint_bub_ms& p); + + void clear(); + + std::size_t count(const tripoint_bub_ms& p) const { + return set_[p.z() + OVERMAP_DEPTH][p.y() * MAPSIZE_X + p.x()]; + } + + private: + std::array dirty_ = {}; + std::array, OVERMAP_LAYERS> set_ = {}; + }; + + // Minimum implementation of std::unordered_map interface that is used in the pathfinder. + class FastBestPathMap + { + public: + std::pair*, bool> try_emplace(const tripoint_bub_ms& child, + int cost, const tripoint_bub_ms& parent); + + std::size_t count(const tripoint_bub_ms& p) const { + return in_.count(p); + } + + void clear() { + in_.clear(); + } + + const std::pair& at(const tripoint_bub_ms& child) const { + return best_states_[child.z() + OVERMAP_DEPTH][child.y()][child.x()]; + } + + std::pair& operator[](const tripoint_bub_ms& child) { + return *try_emplace(child, 0, child).first; + } + + private: + FastTripointSet in_; + RealityBubbleArray> best_states_; + }; + + template + std::vector find_path_impl(AStar& impl, + const RealityBubblePathfindingSettings& settings, + const tripoint_bub_ms& from, const tripoint_bub_ms& to, + PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, HeuristicFn&& heuristic_fn); - bool dirty = false; - std::unordered_set dirty_points; + RealityBubblePathfindingCache* cache_; + AStarPathfinder astar_; + BidirectionalAStarPathfinder + bidirectional_astar_; +}; + +class PathfindingSettings +{ +public: + static constexpr PathfindingFlags RoughTerrain = PathfindingFlag::Slow | PathfindingFlag::Obstacle | + PathfindingFlag::Vehicle | PathfindingFlag::Sharp | PathfindingFlag::DangerousTrap; + static constexpr PathfindingFlags AnySizeRestriction = PathfindingFlag::RestrictTiny | + PathfindingFlag::RestrictSmall | + PathfindingFlag::RestrictMedium | PathfindingFlag::RestrictLarge | PathfindingFlag::RestrictHuge; + + bool avoid_falling() const { + return !rb_settings_.allow_falling(); + } + void set_avoid_falling(bool v = true) { + rb_settings_.set_allow_falling(!v); + } + + bool avoid_unsheltered() const { + return is_set(PathfindingFlag::Unsheltered); + } + void set_avoid_unsheltered(bool v = true) { + set(PathfindingFlag::Unsheltered, v); + } + + bool avoid_swimming() const { + return is_set(PathfindingFlag::Swimmable); + } + void set_avoid_swimming(bool v = true) { + set(PathfindingFlag::Swimmable, v); + } + + bool avoid_ground() const { + return is_set(PathfindingFlag::Ground); + } + void set_avoid_ground(bool v = true) { + set(PathfindingFlag::Ground, v); + } + + bool avoid_vehicle() const { + return is_set(PathfindingFlag::Vehicle); + } + void set_avoid_vehicle(bool v = true) { + set(PathfindingFlag::Vehicle, v); + } + + bool avoid_climbing() const { + return avoid_climbing_; + } + void set_avoid_climbing(bool v = true) { + avoid_climbing_ = v; + maybe_set_avoid_obstacle(); + } + + bool avoid_climb_stairway() const { + return !rb_settings_.allow_stairways(); + } + void set_avoid_climb_stairway(bool v = true) { + rb_settings_.set_allow_stairways(!v); + } + + bool avoid_deep_water() const { + return is_set(PathfindingFlag::DeepWater); + } + void set_avoid_deep_water(bool v = true) { + set(PathfindingFlag::DeepWater, v); + } + + std::optional size_restriction() const { + return size_restriction_; + } + PathfindingFlags size_restriction_mask() const { + return size_restriction_mask_; + } + void set_size_restriction(std::optional size_restriction = std::nullopt); + + bool avoid_pits() const { + return is_set(PathfindingFlag::Pit); + } + void set_avoid_pits(bool v = true) { + set(PathfindingFlag::Pit, v); + } + + bool avoid_opening_doors() const { + return avoid_opening_doors_; + } + void set_avoid_opening_doors(bool v = true) { + avoid_opening_doors_ = v; + maybe_set_avoid_obstacle(); + } + + bool avoid_unlocking_doors() const { + return avoid_unlocking_doors_; + } + void set_avoid_unlocking_doors(bool v = true) { + avoid_unlocking_doors_ = v; + } + + bool avoid_rough_terrain() const { + return is_set(RoughTerrain); + } + void set_avoid_rough_terrain(bool v = true) { + set(RoughTerrain, v); + } + + bool avoid_dangerous_traps() const { + return is_set(PathfindingFlag::DangerousTrap); + } + void set_avoid_dangerous_traps(bool v) { + set(PathfindingFlag::DangerousTrap, v); + } + + bool avoid_sharp() const { + return is_set(PathfindingFlag::Sharp); + } + void set_avoid_sharp(bool v = true) { + set(PathfindingFlag::Sharp, v); + } + + bool avoid_lava() const { + return is_set(PathfindingFlag::Lava); + } + void set_avoid_lava(bool v = true) { + set(PathfindingFlag::Lava, v); + } + + bool avoid_hard_ground() const { + return is_set(PathfindingFlag::HardGround); + } + void set_avoid_hard_ground(bool v = true) { + set(PathfindingFlag::HardGround, v); + } + + const std::function& maybe_avoid_dangerous_fields_fn() const { + return maybe_avoid_dangerous_fields_fn_; + } + void set_maybe_avoid_dangerous_fields_fn(std::function fn = + nullptr) { + maybe_avoid_dangerous_fields_fn_ = std::move(fn); + } + + const std::function& maybe_avoid_fn() const { + return maybe_avoid_fn_; + } + void set_maybe_avoid_fn(std::function fn = nullptr) { + maybe_avoid_fn_ = std::move(fn); + } + + int max_distance() const { + return max_distance_; + } + void set_max_distance(int v = 0) { + max_distance_ = v; + } + + int max_cost() const { + return rb_settings_.max_cost(); + } + void set_max_cost(int v = 0) { + rb_settings_.set_max_cost(v); + } + + int climb_cost() const { + return climb_cost_; + } + void set_climb_cost(int v = 0) { + climb_cost_ = v; + maybe_set_avoid_obstacle(); + } + + bool is_digging() const { + return is_digging_; + } + void set_is_digging(bool v = true) { + is_digging_ = v; + maybe_set_avoid_obstacle(); + } + + bool is_flying() const { + return rb_settings_.allow_flying(); + } + void set_is_flying(bool v = true) { + rb_settings_.set_allow_flying(v); + } + + PathfindingFlags avoid_mask() const { + return avoid_mask_; + } - cata::mdarray special; + bool avoid_bashing() const { + return avoid_bashing_; + } + void set_avoid_bashing(bool v = true) { + avoid_bashing_ = v; + maybe_set_avoid_obstacle(); + } + + int bash_strength() const { + return bash_strength_; + } + void set_bash_strength(int v = 0) { + bash_strength_ = v; + maybe_set_avoid_obstacle(); + } + int bash_rating_from_range(int min, int max) const; + +protected: + const RealityBubblePathfindingSettings& rb_settings() const { + return rb_settings_; + } + + friend class map; + +private: + bool is_set(PathfindingFlags flag) const { + return avoid_mask_.is_set(flag); + } + void set(PathfindingFlags flags, bool v) { + if (v) { + avoid_mask_.set_union(flags); + } + else { + avoid_mask_.set_clear(flags); + } + } + + void maybe_set_avoid_obstacle() { + // Check if we can short circuit checking obstacles. Significantly faster if so. + set(PathfindingFlag::Obstacle, !is_digging() && (climb_cost() <= 0 || avoid_climbing()) && + avoid_opening_doors() && (avoid_bashing() || bash_strength() <= 0)); + } + + PathfindingFlags avoid_mask_ = PathfindingFlag::Impassable; + + std::optional size_restriction_; + PathfindingFlags size_restriction_mask_; + int max_distance_ = 0; + + bool avoid_bashing_ = false; + int bash_strength_ = 0; + + // Expected terrain cost (2 is flat ground) of climbing a wire fence, 0 means no climbing + bool avoid_climbing_ = false; + int climb_cost_ = 0; + + bool is_digging_ = false; + RealityBubblePathfindingSettings rb_settings_; + + bool avoid_opening_doors_ = false; + bool avoid_unlocking_doors_ = false; + std::function maybe_avoid_dangerous_fields_fn_; + std::function maybe_avoid_fn_; }; +// Implementation Details + +template +std::vector RealityBubblePathfinder::find_path(const + RealityBubblePathfindingSettings& settings, const tripoint_bub_ms& from, + const tripoint_bub_ms& to, PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, + HeuristicFn&& heuristic_fn) +{ + // The bidirectional pathfinder doesn't handle paths generated by falling, so we have to fall + // back to normal A* in this case. + const bool might_fall = !settings.allow_flying() && settings.allow_falling(); + if (might_fall) { + return find_path_impl(astar_, settings, from, to, std::forward(p_cost_fn), + std::forward(m_cost_fn), std::forward(heuristic_fn)); + } + else { + return find_path_impl(bidirectional_astar_, settings, from, to, + std::forward(p_cost_fn), std::forward(m_cost_fn), + std::forward(heuristic_fn)); + } +} + +template +std::vector RealityBubblePathfinder::find_path_impl(AStar& impl, const + RealityBubblePathfindingSettings& settings, const tripoint_bub_ms& from, + const tripoint_bub_ms& to, PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, + HeuristicFn&& heuristic_fn) +{ + const tripoint_bub_ms min = coord_max(coord_min(from, to) - settings.padding(), + tripoint_bub_ms(0, 0, -OVERMAP_DEPTH)); + const tripoint_bub_ms max = coord_min(coord_max(from, to) + settings.padding(), + tripoint_bub_ms(MAPSIZE_X - 1, MAPSIZE_Y - 1, OVERMAP_HEIGHT)); + const inclusive_cuboid bounds(min, max); + + return impl.find_path(settings.max_cost(), from, to, std::forward(p_cost_fn), + std::forward(m_cost_fn), std::forward(heuristic_fn), [this, &settings, + bounds](tripoint_bub_ms p, tripoint_bub_ms c, auto&& emit_fn) { + const tripoint_rel_ms offset(1, 1, 1); + const tripoint_bub_ms min = clamp(c - offset, bounds); + const tripoint_bub_ms max = clamp(c + offset, bounds); + if (settings.allow_flying()) { + for (int z = min.z(); z <= max.z(); ++z) { + for (int y = min.y(); y <= max.y(); ++y) { + for (int x = min.x(); x <= max.x(); ++x) { + if (x == c.x() && y == c.y() && z == c.z()) { + continue; + } + const tripoint_bub_ms next(x, y, z); + emit_fn(next); + } + } + } + return; + } + + const PathfindingFlags flags = cache_->flags(c); + + // If we're falling, we can only continue falling. + if (c.z() > -OVERMAP_DEPTH && flags.is_set(PathfindingFlag::Air)) { + const tripoint_bub_ms down(c.xy(), c.z() - 1); + emit_fn(down); + return; + } + + const point_rel_ms d((c.x() > p.x()) - (c.x() < p.x()), + (c.y() > p.y()) - (c.y() < p.y())); + const point_bub_ms n = c.xy() + d; + + if (d.x() != 0 && d.y() != 0) { + // Diagonal movement. Visit the following states: + // + // * * n + // . c * + // p . * + // + // where * is a state to be visited + // n is the state in movement direction, visited + // p is the parent state (not visited) + // c is the current state (not visited) + // . is not visited + if (min.x() <= n.x() && n.x() <= max.x()) { + for (int y = min.y(); y <= max.y(); ++y) { + const tripoint_bub_ms next(n.x(), y, c.z()); + emit_fn(next); + } + } + if (min.y() <= n.y() && n.y() <= max.y()) { + for (int x = min.x(); x <= max.x(); ++x) { + if (x == n.x()) { + continue; + } + const tripoint_bub_ms next(x, n.y(), c.z()); + emit_fn(next); + } + } + } + else if (d.x() != 0) { + // Horizontal movement. Visit the following states: + // + // . . * + // p c n + // . . * + // + // where * is a state to be visited + // n is the state in movement direction, visited + // p is the parent state (not visited) + // c is the current state (not visited) + // . is not visited + if (min.x() <= n.x() && n.x() <= max.x()) { + for (int y = min.y(); y <= max.y(); ++y) { + const tripoint_bub_ms next(n.x(), y, c.z()); + emit_fn(next); + } + } + } + else if (d.y() != 0) { + // Vertical movement. Visit the following states: + // + // * n * + // . c . + // . p . + // + // where * is a state to be visited + // n is the state in movement direction, visited + // p is the parent state (not visited) + // c is the current state (not visited) + // . is not visited + if (min.y() <= n.y() && n.y() <= max.y()) { + for (int x = min.x(); x <= max.x(); ++x) { + const tripoint_bub_ms next(x, n.y(), c.z()); + emit_fn(next); + } + } + } + else { + // We arrived in this state with same x/y, which means + // we got here by traversing a staircase or similar. Need to + // visit all directions. + for (int y = min.y(); y <= max.y(); ++y) { + for (int x = min.x(); x <= max.x(); ++x) { + if (x == c.x() && y == c.y()) { + continue; + } + const tripoint_bub_ms next(x, y, c.z()); + emit_fn(next); + } + } + } + + if (settings.allow_stairways()) { + if (flags.is_set(PathfindingFlag::GoesDown)) { + emit_fn(cache_->down_destination(c)); + } + if (flags.is_set(PathfindingFlag::GoesUp)) { + emit_fn(cache_->up_destination(c)); + } + } + if (flags.is_set(PathfindingFlag::RampUp)) { + const tripoint_bub_ms above(c.xy(), c.z() + 1); + emit_fn(above); + } + if (flags.is_set(PathfindingFlag::RampDown)) { + const tripoint_bub_ms below(c.xy(), c.z() - 1); + emit_fn(below); + } + }); +} + + +// Legacy Pathfinding. + struct pathfinding_settings { int bash_strength = 0; int max_dist = 0; @@ -77,6 +757,9 @@ struct pathfinding_settings { avoid_rough_terrain( art ), avoid_sharp( as ) {} pathfinding_settings &operator=( const pathfinding_settings & ) = default; + + // Converion to the new settings, while everything is being migrated. + PathfindingSettings to_new_pathfinding_settings() const; }; #endif // CATA_SRC_PATHFINDING_H diff --git a/src/point.cpp b/src/point.cpp index d2ceed95f6758..400b811af0dc6 100644 --- a/src/point.cpp +++ b/src/point.cpp @@ -159,26 +159,10 @@ std::vector closest_points_first( const point ¢er, int min_dist, int std::vector result; result.reserve( n + ( is_center_included ? 1 : 0 ) ); - if( is_center_included ) { - result.push_back( center ); - } - - int x_init = std::max( min_dist, 1 ); - point p( x_init, 1 - x_init ); - - point d( point_east ); - - for( int i = 0; i < n; i++ ) { - result.push_back( center + p ); - - if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) { - std::swap( d.x, d.y ); - d.x = -d.x; - } - - p.x += d.x; - p.y += d.y; - } + find_point_closest_first(center, min_dist, max_dist, [&result](const point& p) { + result.push_back(p); + return false; + } ); return result; } diff --git a/src/point.h b/src/point.h index 3047978be41d7..b2708dc3c0ac4 100644 --- a/src/point.h +++ b/src/point.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -290,6 +291,11 @@ std::vector closest_points_first( const tripoint ¢er, int min_dist std::vector closest_points_first( const point ¢er, int max_dist ); std::vector closest_points_first( const point ¢er, int min_dist, int max_dist ); +// TODO: Make this into an Input Iterator. +template +std::optional find_point_closest_first(const point& center, int min_dist, int max_dist, + PredicateFn&& fn); + inline constexpr tripoint tripoint_min { INT_MIN, INT_MIN, INT_MIN }; inline constexpr tripoint tripoint_max{ INT_MAX, INT_MAX, INT_MAX }; @@ -363,4 +369,50 @@ inline const std::array eight_horizontal_neighbors = { { } }; +template +std::optional find_point_closest_first(const point& center, int min_dist, int max_dist, + PredicateFn&& predicate_fn) +{ + min_dist = std::max(min_dist, 0); + max_dist = std::max(max_dist, 0); + + if (min_dist > max_dist) { + return std::nullopt; + } + + const int min_edge = min_dist * 2 + 1; + const int max_edge = max_dist * 2 + 1; + + const int n = max_edge * max_edge - (min_edge - 2) * (min_edge - 2); + const bool is_center_included = min_dist == 0; + + if (is_center_included) { + if (predicate_fn(center)) { + return center; + } + } + + int x_init = std::max(min_dist, 1); + point p(x_init, 1 - x_init); + + point d(point_east); + + for (int i = 0; i < n; i++) { + const point next = center + p; + if (predicate_fn(next)) { + return next; + } + + if (p.x == p.y || (p.x < 0 && p.x == -p.y) || (p.x > 0 && p.x == 1 - p.y)) { + std::swap(d.x, d.y); + d.x = -d.x; + } + + p.x += d.x; + p.y += d.y; + } + + return std::nullopt; +} + #endif // CATA_SRC_POINT_H From 6c389189555bd073a69134696e9f40548f9a6549 Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:40:31 +1000 Subject: [PATCH 2/5] Remove fluff. --- src/monmove.cpp | 21 +- src/monster.h | 2 - src/npcmove.cpp | 6 - src/pathfinding.cpp | 892 -------------------------------------------- 4 files changed, 10 insertions(+), 911 deletions(-) diff --git a/src/monmove.cpp b/src/monmove.cpp index db715e252745b..dd97e958a0514 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1041,7 +1041,6 @@ void monster::move() } tripoint_abs_ms next_step; - const tripoint starting_pos = pos(); const bool can_open_doors = has_flag( mon_flag_CAN_OPEN_DOORS ) && !is_hallucination(); const bool staggers = has_flag( mon_flag_STUMBLES ); if( moved ) { @@ -1051,8 +1050,8 @@ void monster::move() const bool can_bash = bash_skill() > 0; // This is a float and using trig_dist() because that Does the Right Thing(tm) // in both circular and roguelike distance modes. - const float distance_to_target = trig_dist(starting_pos, destination); - for (tripoint& candidate : squares_closer_to(starting_pos, destination)) { + const float distance_to_target = trig_dist(pos(), destination); + for (tripoint& candidate : squares_closer_to(pos(), destination)) { // rare scenario when monster is on the border of the map and it's goal is outside of the map if( !here.inbounds( candidate ) ) { continue; @@ -1071,18 +1070,18 @@ void monster::move() } const tripoint_abs_ms candidate_abs = get_map().getglobal( candidate ); - if( candidate.z != starting_pos.z ) { + if( candidate.z != pos().z) { bool can_z_move = true; - if( !here.valid_move( starting_pos, candidate, false, true, via_ramp ) ) { + if( !here.valid_move( pos(), candidate, false, true, via_ramp)) { // Can't phase through floor can_z_move = false; } // If we're trying to go up but can't fly, check if we can climb. If we can't, then don't // This prevents non-climb/fly enemies running up walls - if( candidate.z > starting_pos.z && !( via_ramp || flies() ) ) { + if( candidate.z > pos().z && !(via_ramp || flies())) { if( !can_climb() || !here.has_floor_or_support( candidate ) ) { - if( !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, starting_pos ) || + if( !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, pos() ) || !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, candidate ) ) { // Can't "jump" up a whole z-level can_z_move = false; @@ -1095,8 +1094,8 @@ void monster::move() if( !can_z_move && posx() / ( SEEX * 2 ) == candidate.x / ( SEEX * 2 ) && posy() / ( SEEY * 2 ) == candidate.y / ( SEEY * 2 ) ) { - const tripoint upper = candidate.z > starting_pos.z ? candidate : starting_pos; - const tripoint lower = candidate.z > starting_pos.z ? starting_pos : candidate; + const tripoint upper = candidate.z > pos().z ? candidate : pos(); + const tripoint lower = candidate.z > pos().z ? pos() : candidate; if( here.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, upper ) && here.has_flag( ter_furn_flag::TFLAG_GOES_UP, lower ) ) { can_z_move = true; @@ -1140,7 +1139,7 @@ void monster::move() // is there an openable door? if( can_open_doors && - here.open_door( *this, candidate, !here.is_outside( starting_pos ), true ) ) { + here.open_door( *this, candidate, !here.is_outside( pos() ), true ) ) { moved = true; next_step = candidate_abs; continue; @@ -1213,7 +1212,7 @@ void monster::move() // If we ended up in a place we didn't plan to, maybe we stumbled or fell, either way the path is // invalid now. const tripoint new_pos = pos(); - if (!path.empty() && path.front() != new_pos && new_pos != starting_pos) { + if (!path.empty() && path.front() != new_pos && new_pos != pos()) { path.clear(); } diff --git a/src/monster.h b/src/monster.h index 6db678c1bf296..b1511e3dcd9b1 100644 --- a/src/monster.h +++ b/src/monster.h @@ -217,8 +217,6 @@ class monster : public Creature // Get the pathfinding settings for this monster. PathfindingSettings get_pathfinding_settings(bool avoid_bashing = true) const; - bool monster_move_in_vehicle(const tripoint& p) const; - // Returns true if the monster has a current goal bool has_dest() const; // Returns point at the end of the monster's current plans diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 833fd946f90c1..5d3e272754b5a 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -705,13 +705,11 @@ std::optional npc_short_term_cache::closest_enemy_to_friendly_distance() co return distance; } -// PERF: 52.61% void npc::assess_danger() { float highest_priority = 1.0f; int hostile_count = 0; // for tallying nearby threatening enemies int friendly_count = 1; // count yourself as a friendly - // PERF: 2.20% int def_radius = rules.has_flag( ally_rule::follow_close ) ? follow_distance() : 6; bool npc_ranged = get_wielded_item() && get_wielded_item()->is_gun(); @@ -728,12 +726,10 @@ void npc::assess_danger() preferred_close_range = std::min( preferred_close_range, preferred_medium_range / 2 ); Character &player_character = get_player_character(); - // PERF: 1.19% bool sees_player = sees( player_character.pos() ); const bool self_defense_only = rules.engagement == combat_engagement::NO_MOVE || rules.engagement == combat_engagement::NONE; const bool no_fighting = rules.has_flag( ally_rule::forbid_engage ); - // PERF: 1.25% const bool must_retreat = rules.has_flag( ally_rule::follow_close ) && !too_close( pos(), player_character.pos(), follow_distance() ) && !is_guarding(); @@ -805,7 +801,6 @@ void npc::assess_danger() if( &guy == this ) { continue; } - // PERF: 31.03% (has_potential_los()) if( !clairvoyant && !here.has_potential_los( pos(), guy.pos() ) ) { continue; } @@ -824,7 +819,6 @@ void npc::assess_danger() } for( const monster &critter : g->all_monsters() ) { - // PERF: 10.27% (has_potential_los()) if( !clairvoyant && !here.has_potential_los( pos(), critter.pos() ) ) { continue; } diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 7dafd49cd0b19..c00bedbe5c860 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -643,7 +643,6 @@ bool map::can_move(const tripoint_bub_ms& from, const tripoint_bub_ms& to, return false; } - std::optional map::move_cost(const tripoint_bub_ms& from, const tripoint_bub_ms& to, const PathfindingSettings& settings) const { @@ -663,21 +662,6 @@ std::optional map::move_cost(const tripoint_bub_ms& from, const tripoint_bu return std::nullopt; } -// TODO: Find the old straight_route() implementations and merge with the below. -//std::vector map::straight_route(const tripoint& f, const tripoint& t) const -//{ -// const std::vector temp = map::straight_route(tripoint_bub_ms(f), -// tripoint_bub_ms(t)); -// std::vector result; -// result.reserve(temp.size()); -// -// for (const tripoint_bub_ms pt : temp) { -// result.push_back(pt.raw()); -// } -// -// return result; -//} - std::vector map::straight_route(const tripoint_bub_ms& from, const tripoint_bub_ms& to, const PathfindingSettings& settings) const @@ -774,280 +758,6 @@ std::vector map::route(const tripoint_bub_ms& f, const tripoint return route(f, t, pf_settings); } - -// TODO: Check the content of these functions, and merge with the above. -//std::vector map::route(const tripoint& f, const tripoint& t, -// const pathfinding_settings& settings, -// const std::function& avoid) const -//{ -// /* TODO: If the origin or destination is out of bound, figure out the closest -// * in-bounds point and go to that, then to the real origin/destination. -// */ -// std::vector ret; -// -// if (f == t || !inbounds(f)) { -// return ret; -// } -// -// if (!inbounds(t)) { -// tripoint clipped = t; -// clip_to_bounds(clipped); -// return route(f, clipped, settings, avoid); -// } -// // First, check for a simple straight line on flat ground -// // Except when the line contains a pre-closed tile - we need to do regular pathing then -// if (f.z == t.z) { -// auto line_path = straight_route(f, t); -// if (!line_path.empty()) { -// if (std::none_of(line_path.begin(), line_path.end(), avoid)) { -// return line_path; -// } -// } -// } -// -// // If expected path length is greater than max distance, allow only line path, like above -// if (rl_dist(f, t) > settings.max_dist) { -// return ret; -// } -// -// const int max_length = settings.max_length; -// -// const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! -// tripoint min(std::min(f.x, t.x) - pad, std::min(f.y, t.y) - pad, std::min(f.z, t.z)); -// tripoint max(std::max(f.x, t.x) + pad, std::max(f.y, t.y) + pad, std::max(f.z, t.z)); -// clip_to_bounds(min.x, min.y, min.z); -// clip_to_bounds(max.x, max.y, max.z); -// -// pf.reset(min.z, max.z); -// -// pf.add_point(0, 0, f, f); -// -// bool done = false; -// -// do { -// tripoint cur = pf.get_next(); -// -// const int parent_index = flat_index(cur.xy()); -// path_data_layer& layer = pf.get_layer(cur.z); -// if (layer.closed[parent_index]) { -// continue; -// } -// -// if (layer.gscore[parent_index] > max_length) { -// // Shortest path would be too long, return empty vector -// return std::vector(); -// } -// -// if (cur == t) { -// done = true; -// break; -// } -// -// layer.closed[parent_index] = true; -// -// const pathfinding_cache& pf_cache = get_pathfinding_cache_ref(cur.z); -// const pf_special cur_special = pf_cache.special[cur.x][cur.y]; -// -// // 7 3 5 -// // 1 . 2 -// // 6 4 8 -// constexpr std::array x_offset{ { -1, 1, 0, 0, 1, -1, -1, 1 } }; -// constexpr std::array y_offset{ { 0, 0, -1, 1, -1, 1, -1, 1 } }; -// for (size_t i = 0; i < 8; i++) { -// const tripoint p(cur.x + x_offset[i], cur.y + y_offset[i], cur.z); -// const int index = flat_index(p.xy()); -// -// // TODO: Remove this and instead have sentinels at the edges -// if (p.x < min.x || p.x >= max.x || p.y < min.y || p.y >= max.y) { -// continue; -// } -// -// if (p != t && avoid(p)) { -// layer.closed[index] = true; -// continue; -// } -// -// if (layer.closed[index]) { -// continue; -// } -// -// // Penalize for diagonals or the path will look "unnatural" -// int newg = layer.gscore[parent_index] + ((cur.x != p.x && cur.y != p.y) ? 1 : 0); -// -// const pf_special p_special = pf_cache.special[p.x][p.y]; -// const int cost = extra_cost(tripoint_bub_ms(cur), tripoint_bub_ms(p), settings, p_special); -// if (cost < 0) { -// if (cost == PF_IMPASSABLE) { -// layer.closed[index] = true; -// } -// continue; -// } -// newg += cost; -// -// // Special case: pathfinders that avoid traps can avoid ledges by -// // climbing down. This can't be covered by |extra_cost| because it -// // can add a new point to the search. -// if (settings.avoid_traps && (p_special & PF_TRAP)) { -// const const_maptile& tile = maptile_at_internal(p); -// const ter_t& terrain = tile.get_ter_t(); -// const trap& ter_trp = terrain.trap.obj(); -// const trap& trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; -// if (!trp.is_benign() && terrain.has_flag(ter_furn_flag::TFLAG_NO_FLOOR)) { -// // Warning: really expensive, needs a cache -// tripoint below(p.xy(), p.z - 1); -// if (valid_move(p, below, false, true)) { -// if (!has_flag(ter_furn_flag::TFLAG_NO_FLOOR, below)) { -// // Otherwise this would have been a huge fall -// path_data_layer& layer = pf.get_layer(p.z - 1); -// // From cur, not p, because we won't be walking on air -// pf.add_point(layer.gscore[parent_index] + 10, -// layer.score[parent_index] + 10 + 2 * rl_dist(below, t), -// cur, below); -// } -// -// // Close p, because we won't be walking on it -// layer.closed[index] = true; -// continue; -// } -// } -// } -// -// pf.add_point(newg, newg + 2 * rl_dist(p, t), cur, p); -// } -// -// if (!(cur_special & PF_UPDOWN) || !settings.allow_climb_stairs) { -// // The part below is only for z-level pathing -// continue; -// } -// -// bool rope_ladder = false; -// const const_maptile& parent_tile = maptile_at_internal(cur); -// const ter_t& parent_terrain = parent_tile.get_ter_t(); -// if (settings.allow_climb_stairs && cur.z > min.z && -// parent_terrain.has_flag(ter_furn_flag::TFLAG_GOES_DOWN)) { -// std::optional opt_dest = g->find_or_make_stairs(get_map(), -// cur.z - 1, rope_ladder, false, cur); -// if (!opt_dest) { -// continue; -// } -// tripoint dest = opt_dest.value(); -// if (vertical_move_destination(*this, ter_furn_flag::TFLAG_GOES_UP, dest)) { -// if (!inbounds(dest)) { -// continue; -// } -// path_data_layer& layer = pf.get_layer(dest.z); -// pf.add_point(layer.gscore[parent_index] + 2, -// layer.score[parent_index] + 2 * rl_dist(dest, t), -// cur, dest); -// } -// } -// if (settings.allow_climb_stairs && cur.z < max.z && -// parent_terrain.has_flag(ter_furn_flag::TFLAG_GOES_UP)) { -// std::optional opt_dest = g->find_or_make_stairs(get_map(), -// cur.z + 1, rope_ladder, false, cur); -// if (!opt_dest) { -// continue; -// } -// tripoint dest = opt_dest.value(); -// if (vertical_move_destination(*this, ter_furn_flag::TFLAG_GOES_DOWN, dest)) { -// if (!inbounds(dest)) { -// continue; -// } -// path_data_layer& layer = pf.get_layer(dest.z); -// pf.add_point(layer.gscore[parent_index] + 2, -// layer.score[parent_index] + 2 * rl_dist(dest, t), -// cur, dest); -// } -// } -// if (cur.z < max.z && parent_terrain.has_flag(ter_furn_flag::TFLAG_RAMP) && -// valid_move(cur, tripoint(cur.xy(), cur.z + 1), false, true)) { -// path_data_layer& layer = pf.get_layer(cur.z + 1); -// for (size_t it = 0; it < 8; it++) { -// const tripoint above(cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1); -// if (!inbounds(above)) { -// continue; -// } -// pf.add_point(layer.gscore[parent_index] + 4, -// layer.score[parent_index] + 4 + 2 * rl_dist(above, t), -// cur, above); -// } -// } -// if (cur.z < max.z && parent_terrain.has_flag(ter_furn_flag::TFLAG_RAMP_UP) && -// valid_move(cur, tripoint(cur.xy(), cur.z + 1), false, true, true)) { -// path_data_layer& layer = pf.get_layer(cur.z + 1); -// for (size_t it = 0; it < 8; it++) { -// const tripoint above(cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1); -// if (!inbounds(above)) { -// continue; -// } -// pf.add_point(layer.gscore[parent_index] + 4, -// layer.score[parent_index] + 4 + 2 * rl_dist(above, t), -// cur, above); -// } -// } -// if (cur.z > min.z && parent_terrain.has_flag(ter_furn_flag::TFLAG_RAMP_DOWN) && -// valid_move(cur, tripoint(cur.xy(), cur.z - 1), false, true, true)) { -// path_data_layer& layer = pf.get_layer(cur.z - 1); -// for (size_t it = 0; it < 8; it++) { -// const tripoint below(cur.x + x_offset[it], cur.y + y_offset[it], cur.z - 1); -// if (!inbounds(below)) { -// continue; -// } -// pf.add_point(layer.gscore[parent_index] + 4, -// layer.score[parent_index] + 4 + 2 * rl_dist(below, t), -// cur, below); -// } -// } -// -// } while (!done && !pf.empty()); -// -// if (done) { -// ret.reserve(rl_dist(f, t) * 2); -// tripoint cur = t; -// // Just to limit max distance, in case something weird happens -// for (int fdist = max_length; fdist != 0; fdist--) { -// const int cur_index = flat_index(cur.xy()); -// const path_data_layer& layer = pf.get_layer(cur.z); -// const tripoint& par = layer.parent[cur_index]; -// if (cur == f) { -// break; -// } -// -// ret.push_back(cur); -// // Jumps are acceptable on 1 z-level changes -// // This is because stairs teleport the player too -// if (rl_dist(cur, par) > 1 && std::abs(cur.z - par.z) != 1) { -// debugmsg("Jump in our route! %d:%d:%d->%d:%d:%d", -// cur.x, cur.y, cur.z, par.x, par.y, par.z); -// return ret; -// } -// -// cur = par; -// } -// -// std::reverse(ret.begin(), ret.end()); -// } -// -// return ret; -//} - - - -//std::vector map::route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, -// const pathfinding_settings& settings, -// const std::function& avoid) const -//{ -// std::vector raw_result = route(f.raw(), t.raw(), settings, avoid); -// std::vector result; -// std::transform(raw_result.begin(), raw_result.end(), std::back_inserter(result), -// [](const tripoint& p) { -// return tripoint_bub_ms(p); -// }); -// return result; -//} - - - PathfindingSettings pathfinding_settings::to_new_pathfinding_settings() const { PathfindingSettings settings; @@ -1064,605 +774,3 @@ PathfindingSettings pathfinding_settings::to_new_pathfinding_settings() const settings.set_avoid_sharp(avoid_sharp); return settings; } - - -/* - - OLD PATHFINDING!!! - - Removed for now, as none of it is kept in @prharvey's version. - TODO: Merge the logic in this code with the logic in the above pathfinder. - -*/ - - -/* -// Turns two indexed to a 2D array into an index to equivalent 1D array -static constexpr int flat_index( const point &p ) -{ - return ( p.x * MAPSIZE_Y ) + p.y; -} - -// Flattened 2D array representing a single z-level worth of pathfinding data -struct path_data_layer { - // Closed/open is accessed way more often than all other values here - std::bitset< MAPSIZE_X *MAPSIZE_Y > closed; - std::bitset< MAPSIZE_X *MAPSIZE_Y > open; - std::array< int, MAPSIZE_X *MAPSIZE_Y > score; - std::array< int, MAPSIZE_X *MAPSIZE_Y > gscore; - std::array< tripoint, MAPSIZE_X *MAPSIZE_Y > parent; - - void reset() { - closed.reset(); - open.reset(); - } -}; - -struct pathfinder { - using queue_type = - std::priority_queue< std::pair, std::vector< std::pair >, pair_greater_cmp_first >; - queue_type open; - std::array< std::unique_ptr< path_data_layer >, OVERMAP_LAYERS > path_data; - - path_data_layer &get_layer( const int z ) { - std::unique_ptr< path_data_layer > &ptr = path_data[z + OVERMAP_DEPTH]; - if( ptr != nullptr ) { - return *ptr; - } - ptr = std::make_unique(); - return *ptr; - } - - void reset( int minz, int maxz ) { - for( int i = minz; i <= maxz; ++i ) { - std::unique_ptr< path_data_layer > &ptr = path_data[i + OVERMAP_DEPTH]; - if( ptr != nullptr ) { - path_data[i + OVERMAP_DEPTH]->reset(); - } - } - open = queue_type(); - } - - bool empty() const { - return open.empty(); - } - - tripoint get_next() { - const auto pt = open.top(); - open.pop(); - return pt.second; - } - - void add_point( const int gscore, const int score, const tripoint &from, const tripoint &to ) { - path_data_layer &layer = get_layer( to.z ); - const int index = flat_index( to.xy() ); - if( layer.closed[index] ) { - return; - } - if( layer.open[index] && gscore >= layer.gscore[index] ) { - return; - } - - layer.open[index] = true; - layer.gscore[index] = gscore; - layer.parent[index] = from; - layer.score [index] = score; - open.emplace( score, to ); - } - - void close_point( const tripoint &p ) { - path_data_layer &layer = get_layer( p.z ); - const int index = flat_index( p.xy() ); - layer.closed[index] = true; - } - - void unclose_point( const tripoint &p ) { - path_data_layer &layer = get_layer( p.z ); - const int index = flat_index( p.xy() ); - layer.closed[index] = false; - } -}; - -static pathfinder pf; - -// Modifies `t` to point to a tile with `flag` in a 1-submap radius of `t`'s original value, -// searching nearest points first (starting with `t` itself). -// return false if it could not find a suitable point -static bool vertical_move_destination( const map &m, ter_furn_flag flag, tripoint &t ) -{ - const pathfinding_cache &pf_cache = m.get_pathfinding_cache_ref( t.z ); - for( const point &p : closest_points_first( t.xy(), SEEX ) ) { - if( pf_cache.special[p.x][p.y] & PF_UPDOWN ) { - const tripoint t2( p, t.z ); - if( m.has_flag( flag, t2 ) ) { - t = t2; - return true; - } - } - } - return false; -} - -template -static bool is_disjoint( const Set1 &set1, const Set2 &set2 ) -{ - if( set1.empty() || set2.empty() ) { - return true; - } - - typename Set1::const_iterator it1 = set1.begin(); - typename Set1::const_iterator it1_end = set1.end(); - - typename Set2::const_iterator it2 = set2.begin(); - typename Set2::const_iterator it2_end = set2.end(); - - if( *set2.rbegin() < *it1 || *set1.rbegin() < *it2 ) { - return true; - } - - while( it1 != it1_end && it2 != it2_end ) { - if( *it1 == *it2 ) { - return false; - } - if( *it1 < *it2 ) { - it1++; - } else { - it2++; - } - } - - return true; -} - -std::vector map::straight_route( const tripoint &f, const tripoint &t ) const -{ - std::vector ret; - if( f == t || !inbounds( f ) ) { - return ret; - } - if( !inbounds( t ) ) { - tripoint clipped = t; - clip_to_bounds( clipped ); - return straight_route( f, clipped ); - } - if( f.z == t.z ) { - ret = line_to( f, t ); - const pathfinding_cache &pf_cache = get_pathfinding_cache_ref( f.z ); - // Check all points for any special case (including just hard terrain) - if( std::any_of( ret.begin(), ret.end(), [&pf_cache]( const tripoint & p ) { - constexpr pf_special non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP | PF_SHARP; - return pf_cache.special[p.x][p.y] & non_normal; - } ) ) { - ret.clear(); - } - } - return ret; -} - -static constexpr int PF_IMPASSABLE = -1; -static constexpr int PF_IMPASSABLE_FROM_HERE = -2; -int map::cost_to_pass( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, - pf_special p_special ) const -{ - constexpr pf_special non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP | PF_SHARP; - if( !( p_special & non_normal ) ) { - // Boring flat dirt - the most common case above the ground - return 2; - } - - if( settings.avoid_rough_terrain ) { - return PF_IMPASSABLE; - } - - if( settings.avoid_sharp && ( p_special & PF_SHARP ) ) { - return PF_IMPASSABLE; - } - - const int bash = settings.bash_strength; - const bool allow_open_doors = settings.allow_open_doors; - const bool allow_unlock_doors = settings.allow_unlock_doors; - const int climb_cost = settings.climb_cost; - - int part = -1; - const const_maptile &tile = maptile_at_internal( p ); - const ter_t &terrain = tile.get_ter_t(); - const furn_t &furniture = tile.get_furn_t(); - const field &field = tile.get_field(); - const vehicle *veh = veh_at_internal( p, part ); - - const int cost = move_cost_internal( furniture, terrain, field, veh, part ); - - // If we can just walk into the tile, great. That's the cost. - if( cost != 0 ) { - return cost; - } - - // Otherwise we'll consider climbing, opening doors, and bashing, in that order. - // Should match logic in monster::move_to and npc::move_to. - - // If there's a vehicle here, we need to assess that instead of the terrain. - if( veh != nullptr ) { - const auto vpobst = vpart_position( const_cast( *veh ), part ).obstacle_at_part(); - part = vpobst ? vpobst->part_index() : -1; - int dummy = -1; - const bool is_outside_veh = veh_at_internal( cur, dummy ) != veh; - - if( part >= 0 ) { - if( allow_open_doors && veh->next_part_to_open( part, is_outside_veh ) != -1 ) { - // Handle car doors, but don't try to path through curtains - return 10; // One turn to open, 4 to move there - } else if( allow_unlock_doors && veh->next_part_to_unlock( part, is_outside_veh ) != -1 ) { - return 12; // 2 turns to open, 4 to move there - } else if( bash > 0 ) { - // Car obstacle that isn't a door - // TODO: Account for armor - int hp = veh->part( part ).hp(); - if( hp / 20 > bash ) { - // Threshold damage thing means we just can't bash this down - return PF_IMPASSABLE; - } else if( hp / 10 > bash ) { - // Threshold damage thing means we will fail to deal damage pretty often - hp *= 2; - } - - return 2 * hp / bash + 8 + 4; - } else { - const vehicle_part &vp = veh->part( part ); - if( allow_open_doors && vp.info().has_flag( VPFLAG_OPENABLE ) ) { - // If we can open doors in general but weren't - // able to open this one, we might be able to - // open it if we try from another direction. - return PF_IMPASSABLE_FROM_HERE; - } else { - // Won't be openable, don't try from other sides - return PF_IMPASSABLE; - } - } - } - } - - // If we can climb it, great! - if( climb_cost > 0 && p_special & PF_CLIMBABLE ) { - return climb_cost; - } - - // If it's a door and we can open it from the tile we're on, cool. - if( allow_open_doors && ( terrain.open || furniture.open ) && - ( ( !terrain.has_flag( ter_furn_flag::TFLAG_OPENCLOSE_INSIDE ) && - !furniture.has_flag( ter_furn_flag::TFLAG_OPENCLOSE_INSIDE ) ) || - !is_outside( cur ) ) ) { - // Only try to open INSIDE doors from the inside - // To open and then move onto the tile - return 4; - } - - // Otherwise, if we can bash, we'll consider that. - if( bash > 0 ) { - const int rating = bash_rating_internal( bash, furniture, terrain, false, veh, part ); - - if( rating > 1 ) { - // Expected number of turns to bash it down, 1 turn to move there - // and 5 turns of penalty not to trash everything just because we can - return ( 20 / rating ) + 2 + 10; - } - - if( rating == 1 ) { - // Desperate measures, avoid whenever possible - return 500; - } - } - - // If we can open doors generally but couldn't open this one, maybe we can - // try from another direction. - if( allow_open_doors && terrain.open && furniture.open ) { - return PF_IMPASSABLE_FROM_HERE; - } - - return PF_IMPASSABLE; -} */ - -//int map::cost_to_avoid( const tripoint & /*cur*/, const tripoint &p, -// const pathfinding_settings &settings, pf_special p_special ) const -//{ -// if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { -// const const_maptile &tile = maptile_at_internal( p ); -// const ter_t &terrain = tile.get_ter_t(); -// const trap &ter_trp = terrain.trap.obj(); -// const trap &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; -// -// // NO_FLOOR is a special case handled in map::route -// if( !trp.is_benign() && !terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { -// return 500; -// } -// } -// -// if( settings.avoid_dangerous_fields && ( p_special & PF_FIELD ) ) { -// // We'll walk through even known-dangerous fields if we absolutely have to. -// return 500; -// } -// -// return 0; -//} - -//int map::extra_cost( const tripoint &cur, const tripoint &p, const pathfinding_settings &settings, -// pf_special p_special ) const -//{ -// int pass_cost = cost_to_pass( cur, p, settings, p_special ); -// if( pass_cost < 0 ) { -// return pass_cost; -// } -// -// int avoid_cost = cost_to_avoid( cur, p, settings, p_special ); -// if( avoid_cost < 0 ) { -// return avoid_cost; -// } -// return pass_cost + avoid_cost; -//} - -//std::vector map::route( const tripoint &f, const tripoint &t, -// const pathfinding_settings &settings, -// const std::function &avoid ) const -//{ -// /* TODO: If the origin or destination is out of bound, figure out the closest -// * in-bounds point and go to that, then to the real origin/destination. -// */ -// std::vector ret; -// -// if( f == t || !inbounds( f ) ) { -// return ret; -// } -// -// if( !inbounds( t ) ) { -// tripoint clipped = t; -// clip_to_bounds( clipped ); -// return route( f, clipped, settings, avoid ); -// } -// // First, check for a simple straight line on flat ground -// // Except when the line contains a pre-closed tile - we need to do regular pathing then -// if( f.z == t.z ) { -// auto line_path = straight_route( f, t ); -// if( !line_path.empty() ) { -// if( std::none_of( line_path.begin(), line_path.end(), avoid ) ) { -// return line_path; -// } -// } -// } -// -// // If expected path length is greater than max distance, allow only line path, like above -// if( rl_dist( f, t ) > settings.max_dist ) { -// return ret; -// } -// -// const int max_length = settings.max_length; -// -// const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! -// tripoint min( std::min( f.x, t.x ) - pad, std::min( f.y, t.y ) - pad, std::min( f.z, t.z ) ); -// tripoint max( std::max( f.x, t.x ) + pad, std::max( f.y, t.y ) + pad, std::max( f.z, t.z ) ); -// clip_to_bounds( min.x, min.y, min.z ); -// clip_to_bounds( max.x, max.y, max.z ); -// -// pf.reset( min.z, max.z ); -// -// pf.add_point( 0, 0, f, f ); -// -// bool done = false; -// -// do { -// tripoint cur = pf.get_next(); -// -// const int parent_index = flat_index( cur.xy() ); -// path_data_layer &layer = pf.get_layer( cur.z ); -// if( layer.closed[parent_index] ) { -// continue; -// } -// -// if( layer.gscore[parent_index] > max_length ) { -// // Shortest path would be too long, return empty vector -// return std::vector(); -// } -// -// if( cur == t ) { -// done = true; -// break; -// } -// -// layer.closed[parent_index] = true; -// -// const pathfinding_cache &pf_cache = get_pathfinding_cache_ref( cur.z ); -// const pf_special cur_special = pf_cache.special[cur.x][cur.y]; -// -// // 7 3 5 -// // 1 . 2 -// // 6 4 8 -// constexpr std::array x_offset{{ -1, 1, 0, 0, 1, -1, -1, 1 }}; -// constexpr std::array y_offset{{ 0, 0, -1, 1, -1, 1, -1, 1 }}; -// for( size_t i = 0; i < 8; i++ ) { -// const tripoint p( cur.x + x_offset[i], cur.y + y_offset[i], cur.z ); -// const int index = flat_index( p.xy() ); -// -// // TODO: Remove this and instead have sentinels at the edges -// if( p.x < min.x || p.x >= max.x || p.y < min.y || p.y >= max.y ) { -// continue; -// } -// -// if( p != t && avoid( p ) ) { -// layer.closed[index] = true; -// continue; -// } -// -// if( layer.closed[index] ) { -// continue; -// } -// -// // Penalize for diagonals or the path will look "unnatural" -// int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 ); -// -// const pf_special p_special = pf_cache.special[p.x][p.y]; -// const int cost = extra_cost( cur, p, settings, p_special ); -// if( cost < 0 ) { -// if( cost == PF_IMPASSABLE ) { -// layer.closed[index] = true; -// } -// continue; -// } -// newg += cost; -// -// // Special case: pathfinders that avoid traps can avoid ledges by -// // climbing down. This can't be covered by |extra_cost| because it -// // can add a new point to the search. -// if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { -// const const_maptile &tile = maptile_at_internal( p ); -// const ter_t &terrain = tile.get_ter_t(); -// const trap &ter_trp = terrain.trap.obj(); -// const trap &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; -// if( !trp.is_benign() && terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { -// // Warning: really expensive, needs a cache -// tripoint below( p.xy(), p.z - 1 ); -// if( valid_move( p, below, false, true ) ) { -// if( !has_flag( ter_furn_flag::TFLAG_NO_FLOOR, below ) ) { -// // Otherwise this would have been a huge fall -// path_data_layer &layer = pf.get_layer( p.z - 1 ); -// // From cur, not p, because we won't be walking on air -// pf.add_point( layer.gscore[parent_index] + 10, -// layer.score[parent_index] + 10 + 2 * rl_dist( below, t ), -// cur, below ); -// } -// -// // Close p, because we won't be walking on it -// layer.closed[index] = true; -// continue; -// } -// } -// } -// -// pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p ); -// } -// -// if( !( cur_special & PF_UPDOWN ) || !settings.allow_climb_stairs ) { -// // The part below is only for z-level pathing -// continue; -// } -// -// bool rope_ladder = false; -// const const_maptile &parent_tile = maptile_at_internal( cur ); -// const ter_t &parent_terrain = parent_tile.get_ter_t(); -// if( settings.allow_climb_stairs && cur.z > min.z && -// parent_terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) ) { -// std::optional opt_dest = g->find_or_make_stairs( get_map(), -// cur.z - 1, rope_ladder, false, cur ); -// if( !opt_dest ) { -// continue; -// } -// tripoint dest = opt_dest.value(); -// if( vertical_move_destination( *this, ter_furn_flag::TFLAG_GOES_UP, dest ) ) { -// if( !inbounds( dest ) ) { -// continue; -// } -// path_data_layer &layer = pf.get_layer( dest.z ); -// pf.add_point( layer.gscore[parent_index] + 2, -// layer.score[parent_index] + 2 * rl_dist( dest, t ), -// cur, dest ); -// } -// } -// if( settings.allow_climb_stairs && cur.z < max.z && -// parent_terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) ) { -// std::optional opt_dest = g->find_or_make_stairs( get_map(), -// cur.z + 1, rope_ladder, false, cur ); -// if( !opt_dest ) { -// continue; -// } -// tripoint dest = opt_dest.value(); -// if( vertical_move_destination( *this, ter_furn_flag::TFLAG_GOES_DOWN, dest ) ) { -// if( !inbounds( dest ) ) { -// continue; -// } -// path_data_layer &layer = pf.get_layer( dest.z ); -// pf.add_point( layer.gscore[parent_index] + 2, -// layer.score[parent_index] + 2 * rl_dist( dest, t ), -// cur, dest ); -// } -// } -// if( cur.z < max.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) && -// valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true ) ) { -// path_data_layer &layer = pf.get_layer( cur.z + 1 ); -// for( size_t it = 0; it < 8; it++ ) { -// const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); -// if( !inbounds( above ) ) { -// continue; -// } -// pf.add_point( layer.gscore[parent_index] + 4, -// layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), -// cur, above ); -// } -// } -// if( cur.z < max.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) && -// valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true, true ) ) { -// path_data_layer &layer = pf.get_layer( cur.z + 1 ); -// for( size_t it = 0; it < 8; it++ ) { -// const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); -// if( !inbounds( above ) ) { -// continue; -// } -// pf.add_point( layer.gscore[parent_index] + 4, -// layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), -// cur, above ); -// } -// } -// if( cur.z > min.z && parent_terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) && -// valid_move( cur, tripoint( cur.xy(), cur.z - 1 ), false, true, true ) ) { -// path_data_layer &layer = pf.get_layer( cur.z - 1 ); -// for( size_t it = 0; it < 8; it++ ) { -// const tripoint below( cur.x + x_offset[it], cur.y + y_offset[it], cur.z - 1 ); -// if( !inbounds( below ) ) { -// continue; -// } -// pf.add_point( layer.gscore[parent_index] + 4, -// layer.score[parent_index] + 4 + 2 * rl_dist( below, t ), -// cur, below ); -// } -// } -// -// } while( !done && !pf.empty() ); -// -// if( done ) { -// ret.reserve( rl_dist( f, t ) * 2 ); -// tripoint cur = t; -// // Just to limit max distance, in case something weird happens -// for( int fdist = max_length; fdist != 0; fdist-- ) { -// const int cur_index = flat_index( cur.xy() ); -// const path_data_layer &layer = pf.get_layer( cur.z ); -// const tripoint &par = layer.parent[cur_index]; -// if( cur == f ) { -// break; -// } -// -// ret.push_back( cur ); -// // Jumps are acceptable on 1 z-level changes -// // This is because stairs teleport the player too -// if( rl_dist( cur, par ) > 1 && std::abs( cur.z - par.z ) != 1 ) { -// debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d", -// cur.x, cur.y, cur.z, par.x, par.y, par.z ); -// return ret; -// } -// -// cur = par; -// } -// -// std::reverse( ret.begin(), ret.end() ); -// } -// -// return ret; -//}*/ - -//std::vector map::route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, -// const pathfinding_settings &settings, -// const std::function &avoid ) const -//{ -// std::vector raw_result = route( f.raw(), t.raw(), settings, avoid ); -// std::vector result; -// std::transform( raw_result.begin(), raw_result.end(), std::back_inserter( result ), -// []( const tripoint & p ) { -// return tripoint_bub_ms( p ); -// } ); -// return result; -//} From d53f6e919f2d6f333353fa22e1226a0883e7b7a8 Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:50:06 +1000 Subject: [PATCH 3/5] astyle --- src/a_star.h | 228 ++++--- src/activity_actor.cpp | 4 +- src/activity_handlers.cpp | 4 +- src/activity_item_handling.cpp | 4 +- src/coordinates.h | 6 +- src/debug_menu.cpp | 2 +- src/do_turn.cpp | 28 +- src/game.cpp | 37 +- src/game.h | 6 +- src/handle_action.cpp | 2 +- src/line.cpp | 10 +- src/line.h | 2 +- src/map.cpp | 40 +- src/map.h | 38 +- src/monmove.cpp | 142 +++-- src/monster.cpp | 2 +- src/monster.h | 8 +- src/npcmove.cpp | 5 +- src/npctrade_utils.cpp | 2 +- src/pathfinding.cpp | 798 ++++++++++++------------ src/pathfinding.h | 1051 ++++++++++++++++---------------- src/point.cpp | 6 +- src/point.h | 34 +- 23 files changed, 1217 insertions(+), 1242 deletions(-) diff --git a/src/a_star.h b/src/a_star.h index 1990678c455cc..3581ebf254654 100644 --- a/src/a_star.h +++ b/src/a_star.h @@ -13,28 +13,28 @@ #include template , typename BestPathMap = std::unordered_map>> -class AStarState + class AStarState { public: - void reset(const Node& start, Cost cost); + void reset( const Node &start, Cost cost ); - std::optional get_next(Cost max); + std::optional get_next( Cost max ); template - void generate_neighbors(const Node& current, StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, - HeuristicFn&& heuristic_fn, NeighborsFn&& neighbors_fn); + void generate_neighbors( const Node ¤t, StateCostFn &&s_cost_fn, TransitionCostFn &&t_cost_fn, + HeuristicFn &&heuristic_fn, NeighborsFn &&neighbors_fn ); - bool has_path_to(const Node& end) const; + bool has_path_to( const Node &end ) const; - Cost path_cost(const Node& end) const; + Cost path_cost( const Node &end ) const; - std::vector path_to(const Node& end) const; + std::vector path_to( const Node &end ) const; private: struct FirstElementGreaterThan { template - bool operator()(const std::tuple& lhs, const std::tuple& rhs) const { - return std::get<0>(lhs) > std::get<0>(rhs); + bool operator()( const std::tuple &lhs, const std::tuple &rhs ) const { + return std::get<0>( lhs ) > std::get<0>( rhs ); } }; @@ -48,26 +48,26 @@ class AStarState }; template , typename BestStateMap = std::unordered_map>> -class AStarPathfinder + class AStarPathfinder { public: template - std::vector find_path(Cost max, const Node& from, const Node& to, - StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, HeuristicFn&& heuristic_fn, - NeighborsFn&& neighbors_fn); + std::vector find_path( Cost max, const Node &from, const Node &to, + StateCostFn &&s_cost_fn, TransitionCostFn &&t_cost_fn, HeuristicFn &&heuristic_fn, + NeighborsFn &&neighbors_fn ); private: AStarState state_; }; template , typename BestStateMap = std::unordered_map>> -class BidirectionalAStarPathfinder + class BidirectionalAStarPathfinder { public: template - std::vector find_path(Cost max, const Node& from, const Node& to, - StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, HeuristicFn&& heuristic_fn, - NeighborsFn&& neighbors_fn); + std::vector find_path( Cost max, const Node &from, const Node &to, + StateCostFn &&s_cost_fn, TransitionCostFn &&t_cost_fn, HeuristicFn &&heuristic_fn, + NeighborsFn &&neighbors_fn ); private: AStarState forward_; @@ -77,7 +77,7 @@ class BidirectionalAStarPathfinder // Implementation Details template -void AStarState::reset(const Node& start, Cost cost) +void AStarState::reset( const Node &start, Cost cost ) { visited_.clear(); best_paths_.clear(); @@ -86,52 +86,52 @@ void AStarState::reset(const Node& start, C // get quite large, so we explicitly create the underlying container and reserve // space beforehand. std::vector queue_data; - queue_data.reserve(400); - frontier_ = FrontierQueue(FirstElementGreaterThan(), std::move(queue_data)); + queue_data.reserve( 400 ); + frontier_ = FrontierQueue( FirstElementGreaterThan(), std::move( queue_data ) ); - best_paths_.try_emplace(start, 0, start); - frontier_.emplace(cost, start); + best_paths_.try_emplace( start, 0, start ); + frontier_.emplace( cost, start ); } template -bool AStarState::has_path_to(const Node& end) const +bool AStarState::has_path_to( const Node &end ) const { - return best_paths_.count(end); + return best_paths_.count( end ); } template -Cost AStarState::path_cost(const Node& end) const +Cost AStarState::path_cost( const Node &end ) const { - return best_paths_.at(end).first; + return best_paths_.at( end ).first; } template -std::vector AStarState::path_to(const Node& end) const +std::vector AStarState::path_to( const Node &end ) const { std::vector result; - if (has_path_to(end)) { + if( has_path_to( end ) ) { Node current = end; - while (best_paths_.at(current).first != 0) { - result.push_back(current); - current = best_paths_.at(current).second; + while( best_paths_.at( current ).first != 0 ) { + result.push_back( current ); + current = best_paths_.at( current ).second; } - std::reverse(result.begin(), result.end()); + std::reverse( result.begin(), result.end() ); } return result; } template -std::optional AStarState::get_next(Cost max) +std::optional AStarState::get_next( Cost max ) { - while (!frontier_.empty()) { + while( !frontier_.empty() ) { auto [cost, current] = frontier_.top(); frontier_.pop(); - if (cost >= max) { + if( cost >= max ) { return std::nullopt; } - if (const auto& [_, inserted] = visited_.emplace(current); !inserted) { + if( const auto& [_, inserted] = visited_.emplace( current ); !inserted ) { continue; } return current; @@ -142,59 +142,58 @@ std::optional AStarState::get_next(Co template template void AStarState::generate_neighbors( - const Node& current, StateCostFn&& s_cost_fn, TransitionCostFn&& t_cost_fn, - HeuristicFn&& heuristic_fn, - NeighborsFn&& neighbors_fn) + const Node ¤t, StateCostFn &&s_cost_fn, TransitionCostFn &&t_cost_fn, + HeuristicFn &&heuristic_fn, + NeighborsFn &&neighbors_fn ) { // Can't use structured bindings here due to a defect in Clang 10. - const std::pair& best_path = best_paths_[current]; + const std::pair &best_path = best_paths_[current]; const Cost current_cost = best_path.first; - const Node& current_parent = best_path.second; - neighbors_fn(current_parent, current, [this, &s_cost_fn, &t_cost_fn, &heuristic_fn, ¤t, - current_cost](const Node& neighbour) { - if (visited_.count(neighbour)) { - return; - } - if (const std::optional s_cost = s_cost_fn(neighbour)) { - if (const std::optional t_cost = t_cost_fn(current, neighbour)) { - const auto& [iter, _] = best_paths_.try_emplace(neighbour, std::numeric_limits::max(), - Node()); - auto& [best_cost, parent] = *iter; - const Cost new_cost = current_cost + *s_cost + *t_cost; - if (new_cost < best_cost) { - best_cost = new_cost; - parent = current; - const Cost estimated_cost = new_cost + heuristic_fn(neighbour); - frontier_.emplace(estimated_cost, neighbour); - } + const Node ¤t_parent = best_path.second; + neighbors_fn( current_parent, current, [this, &s_cost_fn, &t_cost_fn, &heuristic_fn, ¤t, + current_cost]( const Node & neighbour ) { + if( visited_.count( neighbour ) ) { + return; + } + if( const std::optional s_cost = s_cost_fn( neighbour ) ) { + if( const std::optional t_cost = t_cost_fn( current, neighbour ) ) { + const auto& [iter, _] = best_paths_.try_emplace( neighbour, std::numeric_limits::max(), + Node() ); + auto& [best_cost, parent] = *iter; + const Cost new_cost = current_cost + *s_cost + *t_cost; + if( new_cost < best_cost ) { + best_cost = new_cost; + parent = current; + const Cost estimated_cost = new_cost + heuristic_fn( neighbour ); + frontier_.emplace( estimated_cost, neighbour ); } } - else { - visited_.emplace(neighbour); - } - }); + } else { + visited_.emplace( neighbour ); + } + } ); } template template std::vector AStarPathfinder::find_path( - Cost max_cost, const Node& from, const Node& to, StateCostFn&& s_cost_fn, - TransitionCostFn&& t_cost_fn, - HeuristicFn&& heuristic_fn, NeighborsFn&& neighbors_fn) + Cost max_cost, const Node &from, const Node &to, StateCostFn &&s_cost_fn, + TransitionCostFn &&t_cost_fn, + HeuristicFn &&heuristic_fn, NeighborsFn &&neighbors_fn ) { - if (!s_cost_fn(from) || !s_cost_fn(to)) { + if( !s_cost_fn( from ) || !s_cost_fn( to ) ) { return {}; } - state_.reset(from, heuristic_fn(from, to)); - while (const std::optional current = state_.get_next(max_cost)) { - if (*current == to) { - return state_.path_to(to); + state_.reset( from, heuristic_fn( from, to ) ); + while( const std::optional current = state_.get_next( max_cost ) ) { + if( *current == to ) { + return state_.path_to( to ); } - state_.generate_neighbors(*current, s_cost_fn, t_cost_fn, [&heuristic_fn, - to](const Node& from) { - return heuristic_fn(from, to); - }, neighbors_fn); + state_.generate_neighbors( *current, s_cost_fn, t_cost_fn, [&heuristic_fn, + to]( const Node & from ) { + return heuristic_fn( from, to ); + }, neighbors_fn ); } return {}; } @@ -203,11 +202,11 @@ std::vector AStarPathfinder::find_pa template template std::vector BidirectionalAStarPathfinder::find_path( - Cost max_cost, const Node& from, const Node& to, StateCostFn&& s_cost_fn, - TransitionCostFn&& t_cost_fn, - HeuristicFn&& heuristic_fn, NeighborsFn&& neighbors_fn) + Cost max_cost, const Node &from, const Node &to, StateCostFn &&s_cost_fn, + TransitionCostFn &&t_cost_fn, + HeuristicFn &&heuristic_fn, NeighborsFn &&neighbors_fn ) { - if (!s_cost_fn(from) || !s_cost_fn(to)) { + if( !s_cost_fn( from ) || !s_cost_fn( to ) ) { return {}; } @@ -216,59 +215,58 @@ std::vector BidirectionalAStarPathfinder f_current_state = forward_.get_next(partial_max_cost); - if (!f_current_state) { + forward_.reset( from, heuristic_fn( from, to ) ); + backward_.reset( to, heuristic_fn( to, from ) ); + for( ;; ) { + const std::optional f_current_state = forward_.get_next( partial_max_cost ); + if( !f_current_state ) { break; } - const std::optional b_current_state = backward_.get_next(partial_max_cost); - if (!b_current_state) { + const std::optional b_current_state = backward_.get_next( partial_max_cost ); + if( !b_current_state ) { break; } - bool f_links = backward_.has_path_to(*f_current_state); - bool b_links = forward_.has_path_to(*b_current_state); + bool f_links = backward_.has_path_to( *f_current_state ); + bool b_links = forward_.has_path_to( *b_current_state ); - if (f_links && b_links) { - const Cost f_cost = forward_.path_cost(*f_current_state) + backward_.path_cost( - *f_current_state); - const Cost b_cost = forward_.path_cost(*b_current_state) + backward_.path_cost( - *b_current_state); - if (b_cost < f_cost) { + if( f_links && b_links ) { + const Cost f_cost = forward_.path_cost( *f_current_state ) + backward_.path_cost( + *f_current_state ); + const Cost b_cost = forward_.path_cost( *b_current_state ) + backward_.path_cost( + *b_current_state ); + if( b_cost < f_cost ) { f_links = false; - } - else { + } else { b_links = false; } } - if (f_links || b_links) { - const Node& midpoint = f_links ? *f_current_state : *b_current_state; - std::vector forward_path = forward_.path_to(midpoint); - std::vector backward_path = backward_.path_to(midpoint); - if (backward_path.empty()) { + if( f_links || b_links ) { + const Node &midpoint = f_links ? *f_current_state : *b_current_state; + std::vector forward_path = forward_.path_to( midpoint ); + std::vector backward_path = backward_.path_to( midpoint ); + if( backward_path.empty() ) { return forward_path; } backward_path.pop_back(); - std::for_each(backward_path.rbegin(), backward_path.rend(), [&forward_path](const Node& node) { - forward_path.push_back(node); - }); - forward_path.push_back(to); + std::for_each( backward_path.rbegin(), backward_path.rend(), [&forward_path]( const Node & node ) { + forward_path.push_back( node ); + } ); + forward_path.push_back( to ); return forward_path; } - forward_.generate_neighbors(*f_current_state, s_cost_fn, t_cost_fn, [&heuristic_fn, - &to](const Node& from) { - return heuristic_fn(from, to); - }, neighbors_fn); - backward_.generate_neighbors(*b_current_state, s_cost_fn, [&t_cost_fn](const Node& from, - const Node& to) { - return t_cost_fn(to, from); - }, [&heuristic_fn, &from](const Node& to) { - return heuristic_fn(to, from); - }, neighbors_fn); + forward_.generate_neighbors( *f_current_state, s_cost_fn, t_cost_fn, [&heuristic_fn, + &to]( const Node & from ) { + return heuristic_fn( from, to ); + }, neighbors_fn ); + backward_.generate_neighbors( *b_current_state, s_cost_fn, [&t_cost_fn]( const Node & from, + const Node & to ) { + return t_cost_fn( to, from ); + }, [&heuristic_fn, &from]( const Node & to ) { + return heuristic_fn( to, from ); + }, neighbors_fn ); } return {}; } diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 702f4cf073124..a53c95e7b1ef2 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -7188,7 +7188,7 @@ void unload_loot_activity_actor::do_turn( player_activity &act, Character &you ) return; } std::vector route; - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings() ); if( route.empty() ) { // can't get there, can't do anything, skip it continue; @@ -7224,7 +7224,7 @@ void unload_loot_activity_actor::do_turn( player_activity &act, Character &you ) // get either direct route or route to nearest adjacent tile if // source tile is impassable if( here.passable( src_loc ) ) { - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings() ); } else { // impassable source tile (locker etc.), // get route to nearest adjacent tile instead diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 96d320b6b30bb..2165f465f06fd 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -2911,7 +2911,7 @@ void activity_handlers::travel_do_turn( player_activity *act, Character *you ) } } const std::vector route_to = - here.route( you->pos_bub(), centre_sub, you->get_pathfinding_settings()); + here.route( you->pos_bub(), centre_sub, you->get_pathfinding_settings() ); if( !route_to.empty() ) { const activity_id act_travel = ACT_TRAVELLING; you->set_destination( route_to, player_activity( act_travel ) ); @@ -3597,7 +3597,7 @@ static void perform_zone_activity_turn( const tripoint_bub_ms &tile_loc = here.bub_from_abs( tile ); std::vector route = - here.route( you->pos_bub(), tile_loc, you->get_pathfinding_settings()); + here.route( you->pos_bub(), tile_loc, you->get_pathfinding_settings() ); if( route.size() > 1 ) { route.pop_back(); diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index aada313cd77bd..e86f8d9835310 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -2055,7 +2055,7 @@ void activity_on_turn_move_loot( player_activity &act, Character &you ) return; } std::vector route; - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings() ); if( route.empty() ) { // can't get there, can't do anything, skip it continue; @@ -2111,7 +2111,7 @@ void activity_on_turn_move_loot( player_activity &act, Character &you ) // get either direct route or route to nearest adjacent tile if // source tile is impassable if( here.passable( src_loc ) ) { - route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings()); + route = here.route( you.pos_bub(), src_loc, you.get_pathfinding_settings() ); } else { // impassable source tile (locker etc.), // get route to nearest adjacent tile instead diff --git a/src/coordinates.h b/src/coordinates.h index eaeb0adde6ddc..4ce8f38d960bb 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -711,10 +711,10 @@ inline int octile_dist( const coords::coord_point -inline float octile_dist_exact(const coords::coord_point& loc1, - const coords::coord_point& loc2) +inline float octile_dist_exact( const coords::coord_point &loc1, + const coords::coord_point &loc2 ) { - return octile_dist_exact(loc1.raw(), loc2.raw()); + return octile_dist_exact( loc1.raw(), loc2.raw() ); } template diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index be3a166418ba5..bcb044796a78d 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -3427,7 +3427,7 @@ static void set_automove() // TODO: fix point types auto rt = get_map().route( player_character.pos_bub(), tripoint_bub_ms( *dest ), - player_character.get_pathfinding_settings()); + player_character.get_pathfinding_settings() ); if( !rt.empty() ) { player_character.set_destination( rt ); } else { diff --git a/src/do_turn.cpp b/src/do_turn.cpp index c0583da040ba0..408703bb5c39a 100644 --- a/src/do_turn.cpp +++ b/src/do_turn.cpp @@ -269,27 +269,27 @@ void monmove() for( monster &critter : g->all_monsters() ) { // Critters in impassable tiles get pushed away, unless it's not impassable for them - if (!critter.is_dead() && m.impassable(critter.pos())) { + if( !critter.is_dead() && m.impassable( critter.pos() ) ) { const PathfindingSettings settings = critter.get_pathfinding_settings(); - if (!critter.can_move_to(critter.pos(), settings)) { - dbg(D_ERROR) << "game:monmove: " << critter.name() - << " can't move to its location! (" << critter.posx() - << ":" << critter.posy() << ":" << critter.posz() << "), " - << m.tername(critter.pos()); - add_msg_debug(debugmode::DF_MONSTER, "%s can't move to its location! (%d,%d,%d), %s", - critter.name(), - critter.posx(), critter.posy(), critter.posz(), m.tername(critter.pos())); + if( !critter.can_move_to( critter.pos(), settings ) ) { + dbg( D_ERROR ) << "game:monmove: " << critter.name() + << " can't move to its location! (" << critter.posx() + << ":" << critter.posy() << ":" << critter.posz() << "), " + << m.tername( critter.pos() ); + add_msg_debug( debugmode::DF_MONSTER, "%s can't move to its location! (%d,%d,%d), %s", + critter.name(), + critter.posx(), critter.posy(), critter.posz(), m.tername( critter.pos() ) ); bool okay = false; - for (const tripoint& dest : m.points_in_radius(critter.pos(), 3)) { - if (critter.can_move_to(dest, settings) && g->is_empty(dest)) { - critter.setpos(dest); + for( const tripoint &dest : m.points_in_radius( critter.pos(), 3 ) ) { + if( critter.can_move_to( dest, settings ) && g->is_empty( dest ) ) { + critter.setpos( dest ); okay = true; break; } } - if (!okay) { + if( !okay ) { // die of "natural" cause (overpopulation is natural) - critter.die(nullptr); + critter.die( nullptr ); } } } diff --git a/src/game.cpp b/src/game.cpp index 5f4f9556cd954..0bf6db60cf794 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5009,7 +5009,7 @@ static bool can_place_monster( const monster &mon, const tripoint &p ) if( creatures.creature_at( p ) ) { return false; } - return mon.can_move_to(p); + return mon.can_move_to( p ); } static bool can_place_npc( const tripoint &p ) @@ -7458,7 +7458,7 @@ std::optional> game::safe_route_to( Character &who, } // TODO: Check the new pathfinding settings returns an equivalent result. const route_t route = here.route( who.pos_bub(), p, - who.get_pathfinding_settings()); + who.get_pathfinding_settings() ); if( route.empty() ) { continue; // no route } @@ -12323,17 +12323,17 @@ void game::start_hauling( const tripoint &pos ) u.assign_activity( actor ); } -std::optional game::find_stairs(const map& mp, const int z_after, const tripoint& pos) +std::optional game::find_stairs( const map &mp, const int z_after, const tripoint &pos ) { // Try to find the stairs. const int movez = z_after - pos.z; const bool going_down_1 = movez == -1; // If there are stairs on the same x and y as we currently are, use those - if (going_down_1 && mp.has_flag(ter_furn_flag::TFLAG_GOES_UP, pos + tripoint_below)) { + if( going_down_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_UP, pos + tripoint_below ) ) { return pos + tripoint_below; } const bool going_up_1 = movez == 1; - if (going_up_1 && mp.has_flag(ter_furn_flag::TFLAG_GOES_DOWN, pos + tripoint_above)) { + if( going_up_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, pos + tripoint_above ) ) { return pos + tripoint_above; } @@ -12341,31 +12341,32 @@ std::optional game::find_stairs(const map& mp, const int z_after, cons int best = INT_MAX; std::optional stairs; const int omtilesz = SEEX * 2 - 1; - real_coords rc(mp.getabs(pos.xy())); - tripoint omtile_align_start(mp.getlocal(rc.begin_om_pos()), z_after); - tripoint omtile_align_end(omtile_align_start + point(omtilesz, omtilesz)); - for (const tripoint& dest : mp.points_in_rectangle(omtile_align_start, omtile_align_end)) { - if (rl_dist(pos, dest) <= best && - ((going_down_1 && mp.has_flag(ter_furn_flag::TFLAG_GOES_UP, dest)) || - (going_up_1 && (mp.has_flag(ter_furn_flag::TFLAG_GOES_DOWN, dest) || - mp.ter(dest) == ter_t_manhole_cover)) || - ((movez == 2 || movez == -2) && mp.ter(dest) == ter_t_elevator))) { - stairs.emplace(dest); - best = rl_dist(pos, dest); + real_coords rc( mp.getabs( pos.xy() ) ); + tripoint omtile_align_start( mp.getlocal( rc.begin_om_pos() ), z_after ); + tripoint omtile_align_end( omtile_align_start + point( omtilesz, omtilesz ) ); + for( const tripoint &dest : mp.points_in_rectangle( omtile_align_start, omtile_align_end ) ) { + if( rl_dist( pos, dest ) <= best && + ( ( going_down_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_UP, dest ) ) || + ( going_up_1 && ( mp.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, dest ) || + mp.ter( dest ) == ter_t_manhole_cover ) ) || + ( ( movez == 2 || movez == -2 ) && mp.ter( dest ) == ter_t_elevator ) ) ) { + stairs.emplace( dest ); + best = rl_dist( pos, dest ); } } return stairs; } -std::optional game::find_or_make_stairs( const map &mp, const int z_after, bool &rope_ladder, +std::optional game::find_or_make_stairs( const map &mp, const int z_after, + bool &rope_ladder, bool peeking, const tripoint &pos ) { const bool is_avatar = u.pos() == pos; const int movez = z_after - pos.z; // Try to find the stairs. - std::optional stairs = find_stairs(mp, z_after, pos); + std::optional stairs = find_stairs( mp, z_after, pos ); creature_tracker &creatures = get_creature_tracker(); if( stairs.has_value() ) { diff --git a/src/game.h b/src/game.h index 2f15cff0f0fa2..5f1b0c01334c8 100644 --- a/src/game.h +++ b/src/game.h @@ -289,9 +289,9 @@ class game /** Returns the other end of the stairs (if any). May query, affect u etc. * @param pos Disable queries and msgs if not the same position as player. */ - std::optional find_stairs(const map& mp, int z_after, const tripoint& pos); - std::optional find_or_make_stairs(const map& mp, int z_after, bool& rope_ladder, - bool peeking, const tripoint& pos); + std::optional find_stairs( const map &mp, int z_after, const tripoint &pos ); + std::optional find_or_make_stairs( const map &mp, int z_after, bool &rope_ladder, + bool peeking, const tripoint &pos ); /* * Prompt player on direction they want to climb up or down. diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 81f85a5b5885e..559de84432014 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -2243,7 +2243,7 @@ bool game::do_regular_action( action_id &act, avatar &player_character, player_character.pos_bub() + dest_delta * ( SEEX - i ); destination_preview = m.route( player_character.pos_bub(), auto_travel_destination, - player_character.get_pathfinding_settings()); + player_character.get_pathfinding_settings() ); if( !destination_preview.empty() ) { destination_preview.erase( destination_preview.begin() + 1, destination_preview.end() ); diff --git a/src/line.cpp b/src/line.cpp index f6485a3123ddd..1a1fa59468fb8 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -285,17 +285,17 @@ float octile_dist_exact( const point &loc1, const point &loc2 ) return d.x + d.y - 2 * mind + mind * M_SQRT2; } -float octile_dist_exact(const tripoint& from, const tripoint& to) +float octile_dist_exact( const tripoint &from, const tripoint &to ) { - const tripoint d = (from - to).abs(); - const int min = std::min(d.x, std::min(d.y, d.z)); - const int max = std::max(d.x, std::max(d.y, d.z)); + const tripoint d = ( from - to ).abs(); + const int min = std::min( d.x, std::min( d.y, d.z ) ); + const int max = std::max( d.x, std::max( d.y, d.z ) ); const int mid = d.x + d.y + d.z - min - max; constexpr int one_axis = 1; constexpr float two_axis = M_SQRT2; constexpr float three_axis = 1.73205f; - return (three_axis - two_axis) * min + (two_axis - one_axis) * mid + one_axis * max; + return ( three_axis - two_axis ) * min + ( two_axis - one_axis ) * mid + one_axis * max; } units::angle atan2( const point &p ) diff --git a/src/line.h b/src/line.h index 52803cb8026d5..32c5c6a02c457 100644 --- a/src/line.h +++ b/src/line.h @@ -254,7 +254,7 @@ int manhattan_dist( const point &loc1, const point &loc2 ); // cost sqrt(2) or sqrt(3) and cardinal moves cost 1. int octile_dist( const point &loc1, const point &loc2, int multiplier = 1 ); float octile_dist_exact( const point &loc1, const point &loc2 ); -float octile_dist_exact(const tripoint& from, const tripoint& to); +float octile_dist_exact( const tripoint &from, const tripoint &to ); // get angle of direction represented by point units::angle atan2( const point & ); diff --git a/src/map.cpp b/src/map.cpp index b2794155e1d4d..fa9b6dde31dea 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2328,7 +2328,7 @@ bool map::ter_set( const tripoint &p, const ter_id &new_terrain, bool avoid_crea get_creature_tracker().invalidate_reachability_cache(); } // TODO: Limit to changes that affect move cost, traps and stairs - set_pathfinding_cache_dirty(tripoint_bub_ms(p)); + set_pathfinding_cache_dirty( tripoint_bub_ms( p ) ); tripoint above( p.xy(), p.z + 1 ); // Make sure that if we supported something and no longer do so, it falls down @@ -3351,37 +3351,37 @@ int map::bash_rating( const int str, const tripoint &p, const bool allow_floor ) return bash_rating_internal( str, furniture, terrain, allow_floor, veh, part ); } -std::optional> map::bash_range(const tripoint& p, - const bool allow_floor) const +std::optional> map::bash_range( const tripoint &p, + const bool allow_floor ) const { - if (!inbounds(p)) { - DebugLog(D_WARNING, D_MAP) << "Looking for out-of-bounds is_bashable at " - << p.x << ", " << p.y << ", " << p.z; + if( !inbounds( p ) ) { + DebugLog( D_WARNING, D_MAP ) << "Looking for out-of-bounds is_bashable at " + << p.x << ", " << p.y << ", " << p.z; return std::nullopt; } - if (const optional_vpart_position vp = veh_at(p)) { - if (const auto vpobst = vp->obstacle_at_part()) { + if( const optional_vpart_position vp = veh_at( p ) ) { + if( const auto vpobst = vp->obstacle_at_part() ) { const int bash_part = vpobst->part_index(); // Car obstacle that isn't a door // TODO: Account for armor - const int hp = vp->vehicle().part(bash_part).hp(); + const int hp = vp->vehicle().part( bash_part ).hp(); const int bash_min = hp / 20 + 1; // Large max to discourage bashing. const int bash_max = bash_min + 100; - return std::make_pair(bash_min, bash_max); + return std::make_pair( bash_min, bash_max ); } } - const furn_t& furniture = furn(p).obj(); + const furn_t &furniture = furn( p ).obj(); ///\EFFECT_STR determines what furniture can be smashed - if (furniture.id && furniture.bash.str_max != -1) { - return std::make_pair(furniture.bash.str_min, furniture.bash.str_max); + if( furniture.id && furniture.bash.str_max != -1 ) { + return std::make_pair( furniture.bash.str_min, furniture.bash.str_max ); } - const ter_t& terrain = ter(p).obj(); - if (terrain.bash.str_max != -1 && (!terrain.bash.bash_below || allow_floor)) { - return std::make_pair(terrain.bash.str_min, terrain.bash.str_max); + const ter_t &terrain = ter( p ).obj(); + if( terrain.bash.str_max != -1 && ( !terrain.bash.bash_below || allow_floor ) ) { + return std::make_pair( terrain.bash.str_min, terrain.bash.str_max ); } return std::nullopt; @@ -10485,15 +10485,15 @@ const level_cache &map::access_cache( int zlev ) const void map::set_pathfinding_cache_dirty( const int zlev ) { - if (pathfinding_cache_ && inbounds_z(zlev)) { - pathfinding_cache_->invalidate(zlev); + if( pathfinding_cache_ && inbounds_z( zlev ) ) { + pathfinding_cache_->invalidate( zlev ); } } void map::set_pathfinding_cache_dirty( const tripoint_bub_ms &p ) { - if (pathfinding_cache_ && inbounds(p)) { - pathfinding_cache_->invalidate(p); + if( pathfinding_cache_ && inbounds( p ) ) { + pathfinding_cache_->invalidate( p ); } } diff --git a/src/map.h b/src/map.h index ffa4d708d269d..94821bd49e5c7 100644 --- a/src/map.h +++ b/src/map.h @@ -420,16 +420,16 @@ class map void invalidate_map_cache( int zlev ); - RealityBubblePathfindingCache* pathfinding_cache() const { - if (!pathfinding_cache_) { + RealityBubblePathfindingCache *pathfinding_cache() const { + if( !pathfinding_cache_ ) { pathfinding_cache_ = std::make_unique(); } return pathfinding_cache_.get(); } - RealityBubblePathfinder* pathfinder() const { - if (!pathfinder_) { - pathfinder_ = std::make_unique(pathfinding_cache()); + RealityBubblePathfinder *pathfinder() const { + if( !pathfinder_ ) { + pathfinder_ = std::make_unique( pathfinding_cache() ); } return pathfinder_.get(); } @@ -754,23 +754,23 @@ class map // return false; //} ) const; - bool can_teleport(const tripoint_bub_ms& t, const PathfindingSettings& settings) const; - bool can_move(const tripoint_bub_ms& f, const tripoint_bub_ms& t, - const PathfindingSettings& settings) const; - std::optional move_cost(const tripoint_bub_ms& f, const tripoint_bub_ms& t, - const PathfindingSettings& settings) const; - std::vector straight_route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, - const PathfindingSettings& settings) const; - std::vector route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, - const PathfindingSettings& settings) const; + bool can_teleport( const tripoint_bub_ms &t, const PathfindingSettings &settings ) const; + bool can_move( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + const PathfindingSettings &settings ) const; + std::optional move_cost( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + const PathfindingSettings &settings ) const; + std::vector straight_route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + const PathfindingSettings &settings ) const; + std::vector route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + const PathfindingSettings &settings ) const; // TODO: Update callers to the new PathfindingSettings so that this overload is not necessary. - std::vector route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, - const pathfinding_settings& settings) const; + std::vector route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + const pathfinding_settings &settings ) const; // TODO: Replace this with a typesafe variant. - std::vector route(const tripoint& f, const tripoint& t, - const pathfinding_settings& settings); + std::vector route( const tripoint &f, const tripoint &t, + const pathfinding_settings &settings ); // Get a straight route from f to t, only along non-rough terrain. Returns an empty vector // if that is not possible. @@ -1185,7 +1185,7 @@ class map // The range of minimum and maximum bash strength needed to bash something on the given tile. // Returns std::nullopt if there is nothing bashable. - std::optional> bash_range(const tripoint& p, bool allow_floor = false) const; + std::optional> bash_range( const tripoint &p, bool allow_floor = false ) const; // Rubble /** Generates rubble at the given location, if overwrite is true it just writes on top of what currently exists diff --git a/src/monmove.cpp b/src/monmove.cpp index dd97e958a0514..e14335eee7dc7 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -129,117 +129,111 @@ bool monster::is_immune_field( const field_type_id &fid ) const } -PathfindingSettings monster::get_pathfinding_settings(bool avoid_bashing) const +PathfindingSettings monster::get_pathfinding_settings( bool avoid_bashing ) const { PathfindingSettings settings = type->path_settings.to_new_pathfinding_settings(); - settings.set_size_restriction(get_size()); + settings.set_size_restriction( get_size() ); - settings.set_avoid_bashing(avoid_bashing); + settings.set_avoid_bashing( avoid_bashing ); - settings.set_is_digging(digging()); + settings.set_is_digging( digging() ); - settings.set_avoid_climbing(!can_climb()); + settings.set_avoid_climbing( !can_climb() ); const bool can_fly = flies(); - settings.set_avoid_deep_water(!can_submerge() && !can_fly); + settings.set_avoid_deep_water( !can_submerge() && !can_fly ); - settings.set_avoid_hard_ground(digs()); + settings.set_avoid_hard_ground( digs() ); - const bool is_aquatic = has_flag(mon_flag_AQUATIC); - settings.set_avoid_ground(is_aquatic); + const bool is_aquatic = has_flag( mon_flag_AQUATIC ); + settings.set_avoid_ground( is_aquatic ); // AQUATIC (confined to water) monster avoid vehicles, unless they are already underneath one - settings.set_avoid_vehicle(is_aquatic && !get_map().veh_at(pos())); + settings.set_avoid_vehicle( is_aquatic && !get_map().veh_at( pos() ) ); // If we hate the sun, stay inside when it is out. - settings.set_avoid_unsheltered(has_flag(mon_flag_SUNDEATH) && - incident_sun_irradiance(get_weather().weather_id, calendar::turn) > irradiance::minimal); + settings.set_avoid_unsheltered( has_flag( mon_flag_SUNDEATH ) && + incident_sun_irradiance( get_weather().weather_id, calendar::turn ) > irradiance::minimal ); // Various avoiding behaviors. - bool avoid_fire = has_flag(mon_flag_PATH_AVOID_FIRE); - bool avoid_fall = has_flag(mon_flag_PATH_AVOID_FALL); - bool avoid_simple = has_flag(mon_flag_PATH_AVOID_DANGER); + bool avoid_fire = has_flag( mon_flag_PATH_AVOID_FIRE ); + bool avoid_fall = has_flag( mon_flag_PATH_AVOID_FALL ); + bool avoid_simple = has_flag( mon_flag_PATH_AVOID_DANGER ); bool avoid_sharp = type->path_settings.avoid_sharp; bool avoid_traps = type->path_settings.avoid_traps; bool avoid_dangerous_fields = type->path_settings.avoid_dangerous_fields; - + // avoid_simple implies avoid fire, fall, and sharp. - if (avoid_simple) { + if( avoid_simple ) { avoid_fire = true; avoid_fall = true; avoid_sharp = true; } // Don't enter lava if we have any concept of heat being bad - settings.set_avoid_lava(avoid_fire); + settings.set_avoid_lava( avoid_fire ); - settings.set_is_flying(can_fly); - if (can_fly) { + settings.set_is_flying( can_fly ); + if( can_fly ) { avoid_fall = false; } // Don't throw ourselves off cliffs if we have a concept of falling - settings.set_avoid_falling(avoid_fall); + settings.set_avoid_falling( avoid_fall ); // Don't enter open pits ever unless tiny, can fly or climb well - settings.set_avoid_pits(avoid_fall && type->size != creature_size::tiny && !can_climb()); + settings.set_avoid_pits( avoid_fall && type->size != creature_size::tiny && !can_climb() ); // Some things are only avoided if we're not attacking the player - if (get_player_character().get_location() != get_dest() || - attitude(&get_player_character()) != MATT_ATTACK) { + if( get_player_character().get_location() != get_dest() || + attitude( &get_player_character() ) != MATT_ATTACK ) { // Sharp terrain is ignored while attacking - settings.set_avoid_sharp(avoid_sharp && !(type->size == creature_size::tiny || can_fly || - get_armor_type(damage_cut, bodypart_id("torso")) >= 10)); - } - else { + settings.set_avoid_sharp( avoid_sharp && !( type->size == creature_size::tiny || can_fly || + get_armor_type( damage_cut, bodypart_id( "torso" ) ) >= 10 ) ); + } else { // yolo, get that bread, etc - settings.set_avoid_sharp(false); + settings.set_avoid_sharp( false ); } // Don't step on any traps (if we can see) - settings.set_avoid_dangerous_traps(avoid_traps && has_flag(mon_flag_SEES)); + settings.set_avoid_dangerous_traps( avoid_traps && has_flag( mon_flag_SEES ) ); - if (avoid_dangerous_fields) { + if( avoid_dangerous_fields ) { // Don't enter any dangerous fields - settings.set_maybe_avoid_dangerous_fields_fn([this](const field_type_id& field_id) { - return !is_immune_field(field_id); - }); - } - else { + settings.set_maybe_avoid_dangerous_fields_fn( [this]( const field_type_id & field_id ) { + return !is_immune_field( field_id ); + } ); + } else { // Without avoid_complex, only fire and electricity are checked for field avoidance. - const bool should_avoid_fire = avoid_fire && !is_immune_field(fd_fire); - const bool should_avoid_simple = avoid_simple && !is_immune_field(fd_electricity); - if (should_avoid_fire && should_avoid_simple) { - settings.set_maybe_avoid_dangerous_fields_fn([](const field_type_id& field_id) { + const bool should_avoid_fire = avoid_fire && !is_immune_field( fd_fire ); + const bool should_avoid_simple = avoid_simple && !is_immune_field( fd_electricity ); + if( should_avoid_fire && should_avoid_simple ) { + settings.set_maybe_avoid_dangerous_fields_fn( []( const field_type_id & field_id ) { return field_id == fd_fire || field_id == fd_electricity; - }); - } - else if (should_avoid_fire) { - settings.set_maybe_avoid_dangerous_fields_fn([](const field_type_id& field_id) { + } ); + } else if( should_avoid_fire ) { + settings.set_maybe_avoid_dangerous_fields_fn( []( const field_type_id & field_id ) { return field_id == fd_fire; - }); - } - else if (should_avoid_simple) { - settings.set_maybe_avoid_dangerous_fields_fn([](const field_type_id& field_id) { + } ); + } else if( should_avoid_simple ) { + settings.set_maybe_avoid_dangerous_fields_fn( []( const field_type_id & field_id ) { return field_id == fd_electricity; - }); - } - else { + } ); + } else { settings.set_maybe_avoid_dangerous_fields_fn(); } } return settings; } -bool monster::can_move_to(const tripoint& p, const PathfindingSettings& settings) const +bool monster::can_move_to( const tripoint &p, const PathfindingSettings &settings ) const { - const map& here = get_map(); - const tripoint_bub_ms& from = pos_bub(); - if (here.inbounds(from)) { - return here.can_move(from, tripoint_bub_ms(p), settings); - } - else { - return here.can_teleport(tripoint_bub_ms(p), settings); + const map &here = get_map(); + const tripoint_bub_ms &from = pos_bub(); + if( here.inbounds( from ) ) { + return here.can_move( from, tripoint_bub_ms( p ), settings ); + } else { + return here.can_teleport( tripoint_bub_ms( p ), settings ); } } @@ -964,30 +958,30 @@ void monster::move() path.erase( path.begin() ); } - if (settings.max_distance() >= rl_dist(get_location(), get_dest()) && + if( settings.max_distance() >= rl_dist( get_location(), get_dest() ) && ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != local_dest ) ) { // We need a new path path.clear(); // Temporarily allow bashing to find a path through bashable terrain. - settings.set_avoid_bashing(false); + settings.set_avoid_bashing( false ); if( can_pathfind() ) { - for (const tripoint_bub_ms& p : here.route(pos_bub(), tripoint_bub_ms(local_dest), settings)) { - path.push_back(p.raw()); + for( const tripoint_bub_ms &p : here.route( pos_bub(), tripoint_bub_ms( local_dest ), settings ) ) { + path.push_back( p.raw() ); } - if (path.empty()) { + if( path.empty() ) { increment_pathfinding_cd(); } } else { - for (const tripoint_bub_ms& p : here.straight_route(pos_bub(), tripoint_bub_ms(local_dest), - settings)) { - path.push_back(p.raw()); + for( const tripoint_bub_ms &p : here.straight_route( pos_bub(), tripoint_bub_ms( local_dest ), + settings ) ) { + path.push_back( p.raw() ); } } if( !path.empty() ) { reset_pathfinding_cd(); } - settings.set_avoid_bashing(true); + settings.set_avoid_bashing( true ); } // Try to respect old paths, even if we can't pathfind at the moment @@ -1050,8 +1044,8 @@ void monster::move() const bool can_bash = bash_skill() > 0; // This is a float and using trig_dist() because that Does the Right Thing(tm) // in both circular and roguelike distance modes. - const float distance_to_target = trig_dist(pos(), destination); - for (tripoint& candidate : squares_closer_to(pos(), destination)) { + const float distance_to_target = trig_dist( pos(), destination ); + for( tripoint &candidate : squares_closer_to( pos(), destination ) ) { // rare scenario when monster is on the border of the map and it's goal is outside of the map if( !here.inbounds( candidate ) ) { continue; @@ -1070,16 +1064,16 @@ void monster::move() } const tripoint_abs_ms candidate_abs = get_map().getglobal( candidate ); - if( candidate.z != pos().z) { + if( candidate.z != pos().z ) { bool can_z_move = true; - if( !here.valid_move( pos(), candidate, false, true, via_ramp)) { + if( !here.valid_move( pos(), candidate, false, true, via_ramp ) ) { // Can't phase through floor can_z_move = false; } // If we're trying to go up but can't fly, check if we can climb. If we can't, then don't // This prevents non-climb/fly enemies running up walls - if( candidate.z > pos().z && !(via_ramp || flies())) { + if( candidate.z > pos().z && !( via_ramp || flies() ) ) { if( !can_climb() || !here.has_floor_or_support( candidate ) ) { if( !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, pos() ) || !here.has_flag( ter_furn_flag::TFLAG_SWIMMABLE, candidate ) ) { @@ -1212,7 +1206,7 @@ void monster::move() // If we ended up in a place we didn't plan to, maybe we stumbled or fell, either way the path is // invalid now. const tripoint new_pos = pos(); - if (!path.empty() && path.front() != new_pos && new_pos != pos()) { + if( !path.empty() && path.front() != new_pos && new_pos != pos() ) { path.clear(); } diff --git a/src/monster.cpp b/src/monster.cpp index 9c65ecd650e5a..b151562649749 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1384,7 +1384,7 @@ tripoint_abs_ms monster::get_dest() const void monster::set_dest( const tripoint_abs_ms &p ) { - if (!goal || rl_dist(p, *goal) > 4) { + if( !goal || rl_dist( p, *goal ) > 4 ) { reset_pathfinding_cd(); } goal = p; diff --git a/src/monster.h b/src/monster.h index b1511e3dcd9b1..61add08f5d237 100644 --- a/src/monster.h +++ b/src/monster.h @@ -209,13 +209,13 @@ class monster : public Creature * If called multiple times in a loop, prefer getting the settings separately and * reusing them. */ - bool can_move_to(const tripoint& p) const { - return can_move_to(p, get_pathfinding_settings()); + bool can_move_to( const tripoint &p ) const { + return can_move_to( p, get_pathfinding_settings() ); } - bool can_move_to(const tripoint& p, const PathfindingSettings& settings) const; + bool can_move_to( const tripoint &p, const PathfindingSettings &settings ) const; // Get the pathfinding settings for this monster. - PathfindingSettings get_pathfinding_settings(bool avoid_bashing = true) const; + PathfindingSettings get_pathfinding_settings( bool avoid_bashing = true ) const; // Returns true if the monster has a current goal bool has_dest() const; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 5d3e272754b5a..46761aa3c3c4c 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -2828,7 +2828,8 @@ bool npc::update_path( const tripoint &p, const bool no_bashing, bool force ) } } - std::vector new_path = get_map().route( pos(), p, get_pathfinding_settings( no_bashing )); + std::vector new_path = get_map().route( pos(), p, + get_pathfinding_settings( no_bashing ) ); if( new_path.empty() ) { if( !ai_cache.sound_alerts.empty() ) { ai_cache.sound_alerts.erase( ai_cache.sound_alerts.begin() ); @@ -4992,7 +4993,7 @@ void npc::go_to_omt_destination() } } } - path = here.route( pos(), centre_sub, get_pathfinding_settings()); + path = here.route( pos(), centre_sub, get_pathfinding_settings() ); add_msg_debug( debugmode::DF_NPC, "%s going %s->%s", get_name(), omt_pos.to_string_writable(), goal.to_string_writable() ); diff --git a/src/npctrade_utils.cpp b/src/npctrade_utils.cpp index c406cf529ae22..2194646a30db3 100644 --- a/src/npctrade_utils.cpp +++ b/src/npctrade_utils.cpp @@ -94,7 +94,7 @@ void add_fallback_zone( npc &guy ) ( here.furn( t_here )->max_volume > ter_t_floor->max_volume || here.furn( t_here )->has_flag( ter_furn_flag::TFLAG_CONTAINER ) ) && here.can_put_items_ter_furn( t_here ) && - !here.route( guy.pos_bub(), t_here, guy.get_pathfinding_settings()) + !here.route( guy.pos_bub(), t_here, guy.get_pathfinding_settings() ) .empty() ) { points.emplace_back( t ); } diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index c00bedbe5c860..536d7e99e6941 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -27,352 +27,345 @@ #include "vehicle.h" #include "vpart_position.h" -static const ter_id ter_t_pit = ter_id("t_pit"); -static const ter_id ter_t_pit_spiked = ter_id("t_pit_spiked"); -static const ter_id ter_t_pit_glass = ter_id("t_pit_glass"); -static const ter_id ter_t_lava = ter_id("t_lava"); +static const ter_id ter_t_pit = ter_id( "t_pit" ); +static const ter_id ter_t_pit_spiked = ter_id( "t_pit_spiked" ); +static const ter_id ter_t_pit_glass = ter_id( "t_pit_glass" ); +static const ter_id ter_t_lava = ter_id( "t_lava" ); RealityBubblePathfindingCache::RealityBubblePathfindingCache() { - for (int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; ++z) { - dirty_z_levels_.emplace(z); + for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; ++z ) { + dirty_z_levels_.emplace( z ); } } // Modifies `t` to point to a tile with `flag` in a 1-submap radius of `t`'s original value, searching // nearest points first (starting with `t` itself). // Returns false if it could not find a suitable point -bool RealityBubblePathfindingCache::vertical_move_destination(const map& here, ter_furn_flag flag, - tripoint& t) const +bool RealityBubblePathfindingCache::vertical_move_destination( const map &here, ter_furn_flag flag, + tripoint &t ) const { const int z = t.z; - if (const std::optional p = find_point_closest_first(t.xy(), 0, SEEX, [&here, flag, - z](const point& p) { - if (p.x >= 0 && p.x < MAPSIZE_X && p.y >= 0 && p.y < MAPSIZE_Y) { - const tripoint t2(p, z); - return here.has_flag(flag, t2); - } - return false; - })) { - t = tripoint(*p, z); + if( const std::optional p = find_point_closest_first( t.xy(), 0, SEEX, [&here, flag, + z]( const point & p ) { + if( p.x >= 0 && p.x < MAPSIZE_X && p.y >= 0 && p.y < MAPSIZE_Y ) { + const tripoint t2( p, z ); + return here.has_flag( flag, t2 ); + } + return false; + } ) ) { + t = tripoint( *p, z ); return true; } return false; } -void RealityBubblePathfindingCache::update(const map& here, int min_z, int max_z) +void RealityBubblePathfindingCache::update( const map &here, int min_z, int max_z ) { - if (dirty_z_levels_.empty() && dirty_positions_.empty()) { + if( dirty_z_levels_.empty() && dirty_positions_.empty() ) { return; } - cata_assert(min_z <= max_z); + cata_assert( min_z <= max_z ); const int size = here.getmapsize(); - for (const int z : dirty_z_levels_) { - if (z < min_z || z > max_z || !here.inbounds_z(z)) { + for( const int z : dirty_z_levels_ ) { + if( z < min_z || z > max_z || !here.inbounds_z( z ) ) { continue; } - for (int y = 0; y < size * SEEY; ++y) { - for (int x = 0; x < size * SEEX; ++x) { - const tripoint_bub_ms p(x, y, z); - invalidate_dependants(p); - update(here, p); + for( int y = 0; y < size * SEEY; ++y ) { + for( int x = 0; x < size * SEEX; ++x ) { + const tripoint_bub_ms p( x, y, z ); + invalidate_dependants( p ); + update( here, p ); } } } - for (const int z : dirty_z_levels_) { - if (min_z <= z && z <= max_z) { - dirty_positions_.erase(z); + for( const int z : dirty_z_levels_ ) { + if( min_z <= z && z <= max_z ) { + dirty_positions_.erase( z ); } } - for (const auto& [z, dirty_points] : dirty_positions_) { - if (z < min_z || z > max_z || !here.inbounds_z(z)) { + for( const auto& [z, dirty_points] : dirty_positions_ ) { + if( z < min_z || z > max_z || !here.inbounds_z( z ) ) { continue; } - for (const point_bub_ms& p : dirty_points) { - tripoint_bub_ms t(p, z); - if (here.inbounds(t)) { - update(here, t); + for( const point_bub_ms &p : dirty_points ) { + tripoint_bub_ms t( p, z ); + if( here.inbounds( t ) ) { + update( here, t ); } } } - for (int i = min_z; i <= max_z; ++i) { - dirty_z_levels_.erase(i); - dirty_positions_.erase(i); + for( int i = min_z; i <= max_z; ++i ) { + dirty_z_levels_.erase( i ); + dirty_positions_.erase( i ); } } -void RealityBubblePathfindingCache::update(const map& here, const tripoint_bub_ms& p) +void RealityBubblePathfindingCache::update( const map &here, const tripoint_bub_ms &p ) { PathfindingFlags flags; - const const_maptile& tile = here.maptile_at(p); - const ter_t& terrain = tile.get_ter_t(); + const const_maptile &tile = here.maptile_at( p ); + const ter_t &terrain = tile.get_ter_t(); const ter_id terrain_id = tile.get_ter(); - const furn_t& furniture = tile.get_furn_t(); - const optional_vpart_position veh = here.veh_at(p); + const furn_t &furniture = tile.get_furn_t(); + const optional_vpart_position veh = here.veh_at( p ); - const int orig_cost = here.move_cost(p); + const int orig_cost = here.move_cost( p ); const int cost = orig_cost < 0 ? 0 : orig_cost; - if (cost > 2) { + if( cost > 2 ) { flags |= PathfindingFlag::Slow; } - if (!terrain.has_flag(ter_furn_flag::TFLAG_BURROWABLE) && - !terrain.has_flag(ter_furn_flag::TFLAG_DIGGABLE)) { + if( !terrain.has_flag( ter_furn_flag::TFLAG_BURROWABLE ) && + !terrain.has_flag( ter_furn_flag::TFLAG_DIGGABLE ) ) { flags |= PathfindingFlag::HardGround; } - if (veh) { + if( veh ) { flags |= PathfindingFlag::Vehicle; } bool try_to_bash = false; - bool impassable = flags.is_set(PathfindingFlag::HardGround); - if (cost == 0) { + bool impassable = flags.is_set( PathfindingFlag::HardGround ); + if( cost == 0 ) { flags |= PathfindingFlag::Obstacle; try_to_bash = true; - if (terrain.open || furniture.open) { + if( terrain.open || furniture.open ) { impassable = false; flags |= PathfindingFlag::Door; - if (terrain.has_flag(ter_furn_flag::TFLAG_OPENCLOSE_INSIDE) || - furniture.has_flag(ter_furn_flag::TFLAG_OPENCLOSE_INSIDE)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_OPENCLOSE_INSIDE ) || + furniture.has_flag( ter_furn_flag::TFLAG_OPENCLOSE_INSIDE ) ) { flags |= PathfindingFlag::InsideDoor; } } - if (veh) { - if (const auto vpobst = veh->obstacle_at_part()) { + if( veh ) { + if( const auto vpobst = veh->obstacle_at_part() ) { const int vpobst_i = vpobst->part_index(); - const vehicle& v = veh->vehicle(); - const int open_inside = v.next_part_to_open(vpobst_i, false); - if (open_inside != -1) { + const vehicle &v = veh->vehicle(); + const int open_inside = v.next_part_to_open( vpobst_i, false ); + if( open_inside != -1 ) { impassable = false; flags |= PathfindingFlag::Door; - const int open_outside = v.next_part_to_open(vpobst_i, true); - if (open_inside != open_outside) { + const int open_outside = v.next_part_to_open( vpobst_i, true ); + if( open_inside != open_outside ) { flags |= PathfindingFlag::InsideDoor; } - const int lock = v.next_part_to_unlock(vpobst_i, false); - if (lock != -1) { + const int lock = v.next_part_to_unlock( vpobst_i, false ); + if( lock != -1 ) { flags |= PathfindingFlag::LockedDoor; } } } } - if (terrain.has_flag(ter_furn_flag::TFLAG_CLIMBABLE)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_CLIMBABLE ) ) { impassable = false; flags |= PathfindingFlag::Climbable; } - } - else { + } else { impassable = false; // Size restrictions only apply to otherwise passable tiles. - if (terrain.has_flag(ter_furn_flag::TFLAG_SMALL_PASSAGE)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_SMALL_PASSAGE ) ) { flags |= PathfindingFlag::RestrictLarge | PathfindingFlag::RestrictHuge; } - if (veh) { - if (auto cargo = veh->cargo(); cargo && !cargo->has_feature("CARGO_PASSABLE")) { + if( veh ) { + if( auto cargo = veh->cargo(); cargo && !cargo->has_feature( "CARGO_PASSABLE" ) ) { const units::volume free_volume = cargo->items().free_volume(); - if (free_volume < 11719_ml) { + if( free_volume < 11719_ml ) { flags |= PathfindingFlag::RestrictTiny; } - if (free_volume < 23438_ml) { + if( free_volume < 23438_ml ) { flags |= PathfindingFlag::RestrictSmall; } - if (free_volume < 46875_ml) { + if( free_volume < 46875_ml ) { flags |= PathfindingFlag::RestrictMedium; } - if (free_volume < 93750_ml) { + if( free_volume < 93750_ml ) { flags |= PathfindingFlag::RestrictLarge; } - if (free_volume < 187500_ml) { + if( free_volume < 187500_ml ) { flags |= PathfindingFlag::RestrictHuge; } } } - if (flags & PathfindingSettings::AnySizeRestriction) { + if( flags & PathfindingSettings::AnySizeRestriction ) { try_to_bash = true; } } - if (try_to_bash && here.is_bashable(p.raw())) { - if (const auto bash_range = here.bash_range(p.raw())) { + if( try_to_bash && here.is_bashable( p.raw() ) ) { + if( const auto bash_range = here.bash_range( p.raw() ) ) { flags |= PathfindingFlag::Bashable; impassable = false; - bash_range_ref(p) = *bash_range; + bash_range_ref( p ) = *bash_range; } } - if (impassable) { + if( impassable ) { flags |= PathfindingFlag::Impassable; } - if (cost > std::numeric_limits::max()) { - debugmsg("Tile move cost too large for cache: %s, %d", terrain_id.id().str(), cost); - } - else { - move_cost_ref(p) = cost < 2 ? 2 : cost; + if( cost > std::numeric_limits::max() ) { + debugmsg( "Tile move cost too large for cache: %s, %d", terrain_id.id().str(), cost ); + } else { + move_cost_ref( p ) = cost < 2 ? 2 : cost; } - if (terrain.has_flag(ter_furn_flag::TFLAG_NO_FLOOR)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_NO_FLOOR ) ) { flags |= PathfindingFlag::Air; - } - else if (terrain.has_flag(ter_furn_flag::TFLAG_SWIMMABLE)) { + } else if( terrain.has_flag( ter_furn_flag::TFLAG_SWIMMABLE ) ) { flags |= PathfindingFlag::Swimmable; - } - else { + } else { flags |= PathfindingFlag::Ground; } - const bool has_vehicle_floor = here.has_vehicle_floor(p); - if (!has_vehicle_floor) { - if (terrain_id == ter_t_pit || terrain_id == ter_t_pit_spiked || terrain_id == ter_t_pit_glass) { + const bool has_vehicle_floor = here.has_vehicle_floor( p ); + if( !has_vehicle_floor ) { + if( terrain_id == ter_t_pit || terrain_id == ter_t_pit_spiked || terrain_id == ter_t_pit_glass ) { flags |= PathfindingFlag::Pit; } - if (terrain_id == ter_t_lava) { + if( terrain_id == ter_t_lava ) { flags |= PathfindingFlag::Lava; } - if (terrain.has_flag(ter_furn_flag::TFLAG_DEEP_WATER)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_DEEP_WATER ) ) { flags |= PathfindingFlag::DeepWater; } - if (!tile.get_trap_t().is_benign()) { + if( !tile.get_trap_t().is_benign() ) { flags |= PathfindingFlag::DangerousTrap; } - if (terrain.has_flag(ter_furn_flag::TFLAG_SHARP)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_SHARP ) ) { flags |= PathfindingFlag::Sharp; } } - if (terrain.has_flag(ter_furn_flag::TFLAG_BURROWABLE)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_BURROWABLE ) ) { flags |= PathfindingFlag::Burrowable; } - if (!g->is_sheltered(p.raw())) { + if( !g->is_sheltered( p.raw() ) ) { flags |= PathfindingFlag::Unsheltered; } - for (const auto& fld : tile.get_field()) { - const field_entry& cur = fld.second; - if (cur.is_dangerous()) { + for( const auto &fld : tile.get_field() ) { + const field_entry &cur = fld.second; + if( cur.is_dangerous() ) { flags |= PathfindingFlag::DangerousField; break; } } - if (p.z() < OVERMAP_HEIGHT) { - up_destinations_.erase(p); - const tripoint_bub_ms up(p.xy(), p.z() + 1); - if (terrain.has_flag(ter_furn_flag::TFLAG_GOES_UP)) { - if (std::optional dest = g->find_stairs(here, up.z(), p.raw())) { - if (vertical_move_destination(here, ter_furn_flag::TFLAG_GOES_DOWN, *dest)) { - tripoint_bub_ms d(*dest); + if( p.z() < OVERMAP_HEIGHT ) { + up_destinations_.erase( p ); + const tripoint_bub_ms up( p.xy(), p.z() + 1 ); + if( terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) ) { + if( std::optional dest = g->find_stairs( here, up.z(), p.raw() ) ) { + if( vertical_move_destination( here, ter_furn_flag::TFLAG_GOES_DOWN, *dest ) ) { + tripoint_bub_ms d( *dest ); flags |= PathfindingFlag::GoesUp; - up_destinations_.emplace(p, d); - dependants_by_position_[d].push_back(p); + up_destinations_.emplace( p, d ); + dependants_by_position_[d].push_back( p ); } - } - else { - dependants_by_position_[up].push_back(p); + } else { + dependants_by_position_[up].push_back( p ); } } - if (terrain.has_flag(ter_furn_flag::TFLAG_RAMP) || - terrain.has_flag(ter_furn_flag::TFLAG_RAMP_UP)) { - dependants_by_position_[up].push_back(p); - if (here.valid_move(p.raw(), up.raw(), false, true, true)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) || + terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) ) { + dependants_by_position_[up].push_back( p ); + if( here.valid_move( p.raw(), up.raw(), false, true, true ) ) { flags |= PathfindingFlag::RampUp; } } } - if (p.z() > -OVERMAP_DEPTH) { - down_destinations_.erase(p); - const tripoint_bub_ms down(p.xy(), p.z() - 1); - if (terrain.has_flag(ter_furn_flag::TFLAG_GOES_DOWN)) { - if (std::optional dest = g->find_stairs(here, down.z(), p.raw())) { - if (vertical_move_destination(here, ter_furn_flag::TFLAG_GOES_UP, *dest)) { - tripoint_bub_ms d(*dest); + if( p.z() > -OVERMAP_DEPTH ) { + down_destinations_.erase( p ); + const tripoint_bub_ms down( p.xy(), p.z() - 1 ); + if( terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) ) { + if( std::optional dest = g->find_stairs( here, down.z(), p.raw() ) ) { + if( vertical_move_destination( here, ter_furn_flag::TFLAG_GOES_UP, *dest ) ) { + tripoint_bub_ms d( *dest ); flags |= PathfindingFlag::GoesDown; - down_destinations_.emplace(p, d); - dependants_by_position_[d].push_back(p); + down_destinations_.emplace( p, d ); + dependants_by_position_[d].push_back( p ); } - } - else { - dependants_by_position_[down].push_back(p); + } else { + dependants_by_position_[down].push_back( p ); } } - if (terrain.has_flag(ter_furn_flag::TFLAG_RAMP_DOWN)) { - dependants_by_position_[down].push_back(p); - if (here.valid_move(p.raw(), down.raw(), false, true, true)) { + if( terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) ) { + dependants_by_position_[down].push_back( p ); + if( here.valid_move( p.raw(), down.raw(), false, true, true ) ) { flags |= PathfindingFlag::RampDown; } } } - flags_ref(p) = flags; + flags_ref( p ) = flags; } -void PathfindingSettings::set_size_restriction(std::optional size_restriction) +void PathfindingSettings::set_size_restriction( std::optional size_restriction ) { size_restriction_mask_.clear(); - if (size_restriction) { - switch (*size_restriction) { - case creature_size::tiny: - size_restriction_mask_ = PathfindingFlag::RestrictTiny; - break; - case creature_size::small: - size_restriction_mask_ = PathfindingFlag::RestrictSmall; - break; - case creature_size::medium: - size_restriction_mask_ = PathfindingFlag::RestrictMedium; - break; - case creature_size::large: - size_restriction_mask_ = PathfindingFlag::RestrictLarge; - break; - case creature_size::huge: - size_restriction_mask_ = PathfindingFlag::RestrictHuge; - break; - default: - break; + if( size_restriction ) { + switch( *size_restriction ) { + case creature_size::tiny: + size_restriction_mask_ = PathfindingFlag::RestrictTiny; + break; + case creature_size::small: + size_restriction_mask_ = PathfindingFlag::RestrictSmall; + break; + case creature_size::medium: + size_restriction_mask_ = PathfindingFlag::RestrictMedium; + break; + case creature_size::large: + size_restriction_mask_ = PathfindingFlag::RestrictLarge; + break; + case creature_size::huge: + size_restriction_mask_ = PathfindingFlag::RestrictHuge; + break; + default: + break; } } size_restriction_ = size_restriction; } -int PathfindingSettings::bash_rating_from_range(int min, int max) const +int PathfindingSettings::bash_rating_from_range( int min, int max ) const { - if (avoid_bashing_) { + if( avoid_bashing_ ) { return 0; } // TODO: Move all the bash stuff to map so this logic isn't duplicated. ///\EFFECT_STR increases smashing damage - if (bash_strength_ < min) { + if( bash_strength_ < min ) { return 0; - } - else if (bash_strength_ >= max) { + } else if( bash_strength_ >= max ) { return 10; } - const double ret = (10.0 * (bash_strength_ - min)) / (max - min); + const double ret = ( 10.0 * ( bash_strength_ - min ) ) / ( max - min ); // Round up to 1, so that desperate NPCs can try to bash down walls - return std::max(ret, 1.0); + return std::max( ret, 1.0 ); } std::pair -RealityBubblePathfinder::FastTripointSet::emplace(const tripoint_bub_ms& p) +RealityBubblePathfinder::FastTripointSet::emplace( const tripoint_bub_ms &p ) { const int z = p.z() + OVERMAP_DEPTH; dirty_[z] = true; @@ -381,13 +374,13 @@ RealityBubblePathfinder::FastTripointSet::emplace(const tripoint_bub_ms& p) auto ref = set_[z][p.y() * MAPSIZE_X + p.x()]; const bool missing = !ref; ref = true; - return std::make_pair(NotIterator(), missing); + return std::make_pair( NotIterator(), missing ); } void RealityBubblePathfinder::FastTripointSet::clear() { - for (int z = 0; z < OVERMAP_LAYERS; ++z) { - if (!dirty_[z]) { + for( int z = 0; z < OVERMAP_LAYERS; ++z ) { + if( !dirty_[z] ) { continue; } dirty_[z] = false; @@ -396,30 +389,30 @@ void RealityBubblePathfinder::FastTripointSet::clear() } std::pair*, bool> -RealityBubblePathfinder::FastBestPathMap::try_emplace(const tripoint_bub_ms& child, int cost, - const tripoint_bub_ms& parent) +RealityBubblePathfinder::FastBestPathMap::try_emplace( const tripoint_bub_ms &child, int cost, + const tripoint_bub_ms &parent ) { - std::pair& result = best_states_[child.z() + - OVERMAP_DEPTH][child.y()][child.x()]; - if (const auto [_, inserted] = in_.emplace(child); inserted) { + std::pair &result = best_states_[child.z() + + OVERMAP_DEPTH][child.y()][child.x()]; + if( const auto [_, inserted] = in_.emplace( child ); inserted ) { result.first = cost; result.second = parent; - return std::make_pair(&result, true); + return std::make_pair( &result, true ); } - return std::make_pair(&result, false); + return std::make_pair( &result, false ); } namespace { - constexpr int one_axis = 50; - constexpr int two_axis = 71; - constexpr int three_axis = 87; +constexpr int one_axis = 50; +constexpr int two_axis = 71; +constexpr int three_axis = 87; - int adjacent_octile_distance(const tripoint_bub_ms& from, const tripoint_bub_ms& to) - { - switch (std::abs(from.x() - to.x()) + std::abs(from.y() - to.y()) + std::abs( - from.z() - to.z())) { +int adjacent_octile_distance( const tripoint_bub_ms &from, const tripoint_bub_ms &to ) +{ + switch( std::abs( from.x() - to.x() ) + std::abs( from.y() - to.y() ) + std::abs( + from.z() - to.z() ) ) { case 1: return one_axis; case 2: @@ -428,270 +421,264 @@ namespace return three_axis; default: return 0; - } } +} - int octile_distance(const tripoint_bub_ms& from, const tripoint_bub_ms& to) - { - const tripoint d(std::abs(from.x() - to.x()), std::abs(from.y() - to.y()), - std::abs(from.z() - to.z())); - const int min = std::min(d.x, std::min(d.y, d.z)); - const int max = std::max(d.x, std::max(d.y, d.z)); - const int mid = d.x + d.y + d.z - min - max; - return (three_axis - two_axis) * min + (two_axis - one_axis) * mid + one_axis * max; - } +int octile_distance( const tripoint_bub_ms &from, const tripoint_bub_ms &to ) +{ + const tripoint d( std::abs( from.x() - to.x() ), std::abs( from.y() - to.y() ), + std::abs( from.z() - to.z() ) ); + const int min = std::min( d.x, std::min( d.y, d.z ) ); + const int max = std::max( d.x, std::max( d.y, d.z ) ); + const int mid = d.x + d.y + d.z - min - max; + return ( three_axis - two_axis ) * min + ( two_axis - one_axis ) * mid + one_axis * max; +} - int adjacent_distance_metric(const tripoint_bub_ms& from, const tripoint_bub_ms& to) - { - return trigdist ? adjacent_octile_distance(from, to) : 50 * square_dist(from, to); - } +int adjacent_distance_metric( const tripoint_bub_ms &from, const tripoint_bub_ms &to ) +{ + return trigdist ? adjacent_octile_distance( from, to ) : 50 * square_dist( from, to ); +} - int distance_metric(const tripoint_bub_ms& from, const tripoint_bub_ms& to) - { - return trigdist ? octile_distance(from, to) : 50 * square_dist(from, to); - } +int distance_metric( const tripoint_bub_ms &from, const tripoint_bub_ms &to ) +{ + return trigdist ? octile_distance( from, to ) : 50 * square_dist( from, to ); +} - std::optional position_cost(const map& here, const tripoint_bub_ms& p, - const PathfindingSettings& settings, const RealityBubblePathfindingCache& cache) - { - const PathfindingFlags flags = cache.flags(p); - if (flags & settings.avoid_mask()) { - return std::nullopt; - } +std::optional position_cost( const map &here, const tripoint_bub_ms &p, + const PathfindingSettings &settings, const RealityBubblePathfindingCache &cache ) +{ + const PathfindingFlags flags = cache.flags( p ); + if( flags & settings.avoid_mask() ) { + return std::nullopt; + } - std::optional cost; - if (flags & (PathfindingFlag::Obstacle | PathfindingSettings::AnySizeRestriction)) { - if (flags.is_set(PathfindingFlag::Obstacle)) { - if (settings.is_digging()) { - if (!flags.is_set(PathfindingFlag::Burrowable)) { - return std::nullopt; - } - cost = 0; + std::optional cost; + if( flags & ( PathfindingFlag::Obstacle | PathfindingSettings::AnySizeRestriction ) ) { + if( flags.is_set( PathfindingFlag::Obstacle ) ) { + if( settings.is_digging() ) { + if( !flags.is_set( PathfindingFlag::Burrowable ) ) { + return std::nullopt; } - else { - if (flags.is_set(PathfindingFlag::Door) && !settings.avoid_opening_doors() && - (!flags.is_set(PathfindingFlag::LockedDoor) || !settings.avoid_unlocking_doors())) { - const int this_cost = flags.is_set(PathfindingFlag::LockedDoor) ? 4 : 2; - cost = std::min(this_cost, cost.value_or(this_cost)); - } - if (flags.is_set(PathfindingFlag::Climbable) && !settings.avoid_climbing() && - settings.climb_cost() > 0) { - // Climbing fences - const int this_cost = settings.climb_cost(); - cost = std::min(this_cost, cost.value_or(this_cost)); - } + cost = 0; + } else { + if( flags.is_set( PathfindingFlag::Door ) && !settings.avoid_opening_doors() && + ( !flags.is_set( PathfindingFlag::LockedDoor ) || !settings.avoid_unlocking_doors() ) ) { + const int this_cost = flags.is_set( PathfindingFlag::LockedDoor ) ? 4 : 2; + cost = std::min( this_cost, cost.value_or( this_cost ) ); } - } - - if (flags.is_set(PathfindingFlag::Bashable)) { - const auto [bash_min, bash_max] = cache.bash_range(p); - const int bash_rating = settings.bash_rating_from_range(bash_min, bash_max); - if (bash_rating >= 1) { - // Expected number of turns to bash it down - const int this_cost = 20 / bash_rating; - cost = std::min(this_cost, cost.value_or(this_cost)); + if( flags.is_set( PathfindingFlag::Climbable ) && !settings.avoid_climbing() && + settings.climb_cost() > 0 ) { + // Climbing fences + const int this_cost = settings.climb_cost(); + cost = std::min( this_cost, cost.value_or( this_cost ) ); } } + } - // Don't check size restrictions if we can bash it down, open it, or climb over it. - if (!cost && (flags & PathfindingSettings::AnySizeRestriction)) { - // Any tile with a size restriction is passable otherwise. - if (flags & settings.size_restriction_mask()) { - return std::nullopt; - } - cost = 0; + if( flags.is_set( PathfindingFlag::Bashable ) ) { + const auto [bash_min, bash_max] = cache.bash_range( p ); + const int bash_rating = settings.bash_rating_from_range( bash_min, bash_max ); + if( bash_rating >= 1 ) { + // Expected number of turns to bash it down + const int this_cost = 20 / bash_rating; + cost = std::min( this_cost, cost.value_or( this_cost ) ); } + } - if (!cost) { - // Can't enter the tile at all. + // Don't check size restrictions if we can bash it down, open it, or climb over it. + if( !cost && ( flags & PathfindingSettings::AnySizeRestriction ) ) { + // Any tile with a size restriction is passable otherwise. + if( flags & settings.size_restriction_mask() ) { return std::nullopt; } - } - else { cost = 0; } - const auto& maybe_avoid_dangerous_fields_fn = settings.maybe_avoid_dangerous_fields_fn(); - if (flags.is_set(PathfindingFlag::DangerousField) && maybe_avoid_dangerous_fields_fn) { - const field& target_field = here.field_at(p.raw()); - for (const auto& dfield : target_field) { - if (dfield.second.is_dangerous() && maybe_avoid_dangerous_fields_fn(dfield.first)) { - return std::nullopt; - } - } - } - - const auto& maybe_avoid_fn = settings.maybe_avoid_fn(); - if (maybe_avoid_fn && maybe_avoid_fn(p)) { + if( !cost ) { + // Can't enter the tile at all. return std::nullopt; } - - return *cost * 50; + } else { + cost = 0; } - std::optional transition_cost(const map& here, const tripoint_bub_ms& from, - const tripoint_bub_ms& to, const PathfindingSettings& settings, - const RealityBubblePathfindingCache& cache) - { - const PathfindingFlags from_flags = cache.flags(from); - const bool is_falling = from_flags.is_set(PathfindingFlag::Air) && !settings.is_flying(); - if (is_falling) { - // Can only fall straight down. - if (from.z() < to.z() || from.xy() != to.xy()) { + const auto &maybe_avoid_dangerous_fields_fn = settings.maybe_avoid_dangerous_fields_fn(); + if( flags.is_set( PathfindingFlag::DangerousField ) && maybe_avoid_dangerous_fields_fn ) { + const field &target_field = here.field_at( p.raw() ); + for( const auto &dfield : target_field ) { + if( dfield.second.is_dangerous() && maybe_avoid_dangerous_fields_fn( dfield.first ) ) { return std::nullopt; } } + } - const bool is_vertical_movement = from.z() != to.z(); - if (!is_falling && is_vertical_movement) { - const tripoint_bub_ms& upper = from.z() > to.z() ? from : to; - const tripoint_bub_ms& lower = from.z() < to.z() ? from : to; - if (cache.flags(lower).is_set(PathfindingFlag::GoesUp) && - cache.flags(upper).is_set(PathfindingFlag::GoesDown)) { - if (settings.avoid_climb_stairway()) { - return std::nullopt; - } - // Stairs can teleport us, so we need to use non-adjacent calc. - return 2 * distance_metric(from, to); + const auto &maybe_avoid_fn = settings.maybe_avoid_fn(); + if( maybe_avoid_fn && maybe_avoid_fn( p ) ) { + return std::nullopt; + } + + return *cost * 50; +} + +std::optional transition_cost( const map &here, const tripoint_bub_ms &from, + const tripoint_bub_ms &to, const PathfindingSettings &settings, + const RealityBubblePathfindingCache &cache ) +{ + const PathfindingFlags from_flags = cache.flags( from ); + const bool is_falling = from_flags.is_set( PathfindingFlag::Air ) && !settings.is_flying(); + if( is_falling ) { + // Can only fall straight down. + if( from.z() < to.z() || from.xy() != to.xy() ) { + return std::nullopt; + } + } + + const bool is_vertical_movement = from.z() != to.z(); + if( !is_falling && is_vertical_movement ) { + const tripoint_bub_ms &upper = from.z() > to.z() ? from : to; + const tripoint_bub_ms &lower = from.z() < to.z() ? from : to; + if( cache.flags( lower ).is_set( PathfindingFlag::GoesUp ) && + cache.flags( upper ).is_set( PathfindingFlag::GoesDown ) ) { + if( settings.avoid_climb_stairway() ) { + return std::nullopt; } - else if (settings.is_flying()) { - const tripoint_bub_ms above_lower(lower.xy(), lower.z() + 1); - if (!(cache.flags(upper).is_set(PathfindingFlag::Air) || - cache.flags(above_lower).is_set(PathfindingFlag::Air))) { - return std::nullopt; - } + // Stairs can teleport us, so we need to use non-adjacent calc. + return 2 * distance_metric( from, to ); + } else if( settings.is_flying() ) { + const tripoint_bub_ms above_lower( lower.xy(), lower.z() + 1 ); + if( !( cache.flags( upper ).is_set( PathfindingFlag::Air ) || + cache.flags( above_lower ).is_set( PathfindingFlag::Air ) ) ) { + return std::nullopt; } - else if (to.z() > from.z()) { - if (!cache.flags(to).is_set(PathfindingFlag::RampDown)) { - return std::nullopt; - } + } else if( to.z() > from.z() ) { + if( !cache.flags( to ).is_set( PathfindingFlag::RampDown ) ) { + return std::nullopt; } - else if (to.z() < from.z()) { - if (!cache.flags(to).is_set(PathfindingFlag::RampUp)) { - return std::nullopt; - } + } else if( to.z() < from.z() ) { + if( !cache.flags( to ).is_set( PathfindingFlag::RampUp ) ) { + return std::nullopt; } } + } - const PathfindingFlags to_flags = cache.flags(to); - if (to_flags.is_set(PathfindingFlag::Obstacle)) { - // Can't interact with obstacles across z-levels. - // TODO: allow this - if (is_vertical_movement) { - return std::nullopt; - } - if (!settings.is_digging()) { - if (to_flags.is_set(PathfindingFlag::Door) && !settings.avoid_opening_doors() && - (!to_flags.is_set(PathfindingFlag::LockedDoor) || !settings.avoid_unlocking_doors())) { - const bool is_inside_door = to_flags.is_set(PathfindingFlag::InsideDoor); - if (is_inside_door) { - int dummy; - const bool is_vehicle = to_flags.is_set(PathfindingFlag::Vehicle); - const bool is_outside = is_vehicle ? here.veh_at_internal(from.raw(), - dummy) != here.veh_at_internal(to.raw(), dummy) : here.is_outside(from.raw()); - if (is_outside) { - return std::nullopt; - } + const PathfindingFlags to_flags = cache.flags( to ); + if( to_flags.is_set( PathfindingFlag::Obstacle ) ) { + // Can't interact with obstacles across z-levels. + // TODO: allow this + if( is_vertical_movement ) { + return std::nullopt; + } + if( !settings.is_digging() ) { + if( to_flags.is_set( PathfindingFlag::Door ) && !settings.avoid_opening_doors() && + ( !to_flags.is_set( PathfindingFlag::LockedDoor ) || !settings.avoid_unlocking_doors() ) ) { + const bool is_inside_door = to_flags.is_set( PathfindingFlag::InsideDoor ); + if( is_inside_door ) { + int dummy; + const bool is_vehicle = to_flags.is_set( PathfindingFlag::Vehicle ); + const bool is_outside = is_vehicle ? here.veh_at_internal( from.raw(), + dummy ) != here.veh_at_internal( to.raw(), dummy ) : here.is_outside( from.raw() ); + if( is_outside ) { + return std::nullopt; } } } } - else if (to_flags.is_set(PathfindingFlag::Air)) { - // This is checking horizontal movement only. - if (settings.avoid_falling() && !settings.is_flying()) { - return std::nullopt; - } + } else if( to_flags.is_set( PathfindingFlag::Air ) ) { + // This is checking horizontal movement only. + if( settings.avoid_falling() && !settings.is_flying() ) { + return std::nullopt; } + } - // TODO: Move the move cost cache into map so this logic isn't duplicated. - const int mult = adjacent_distance_metric(from, to); - - // Flying monsters aren't slowed down by terrain. - if (settings.is_flying()) { - return 2 * mult; - } + // TODO: Move the move cost cache into map so this logic isn't duplicated. + const int mult = adjacent_distance_metric( from, to ); - const int cost = cache.move_cost(from) + cache.move_cost(to); - return static_cast(mult * cost) / 2; + // Flying monsters aren't slowed down by terrain. + if( settings.is_flying() ) { + return 2 * mult; } + const int cost = cache.move_cost( from ) + cache.move_cost( to ); + return static_cast( mult * cost ) / 2; +} + } // namespace -bool map::can_teleport(const tripoint_bub_ms& to, const PathfindingSettings& settings) const +bool map::can_teleport( const tripoint_bub_ms &to, const PathfindingSettings &settings ) const { - if (!inbounds(to)) { + if( !inbounds( to ) ) { return false; } - pathfinding_cache()->update(*this, to.z(), to.z()); - return position_cost(*this, to, settings, *pathfinding_cache()).has_value(); + pathfinding_cache()->update( *this, to.z(), to.z() ); + return position_cost( *this, to, settings, *pathfinding_cache() ).has_value(); } -bool map::can_move(const tripoint_bub_ms& from, const tripoint_bub_ms& to, - const PathfindingSettings& settings) const +bool map::can_move( const tripoint_bub_ms &from, const tripoint_bub_ms &to, + const PathfindingSettings &settings ) const { - if (!inbounds(from) || !inbounds(to)) { + if( !inbounds( from ) || !inbounds( to ) ) { return false; } - if (from == to) { + if( from == to ) { return true; } - pathfinding_cache()->update(*this, std::min(from.z(), to.z()), std::max(from.z(), to.z())); - if (position_cost(*this, to, settings, *pathfinding_cache()).has_value()) { - return transition_cost(*this, from, to, settings, *pathfinding_cache()).has_value(); + pathfinding_cache()->update( *this, std::min( from.z(), to.z() ), std::max( from.z(), to.z() ) ); + if( position_cost( *this, to, settings, *pathfinding_cache() ).has_value() ) { + return transition_cost( *this, from, to, settings, *pathfinding_cache() ).has_value(); } return false; } -std::optional map::move_cost(const tripoint_bub_ms& from, const tripoint_bub_ms& to, - const PathfindingSettings& settings) const +std::optional map::move_cost( const tripoint_bub_ms &from, const tripoint_bub_ms &to, + const PathfindingSettings &settings ) const { - if (!inbounds(from) || !inbounds(to)) { + if( !inbounds( from ) || !inbounds( to ) ) { return std::nullopt; } - if (from == to) { + if( from == to ) { return 0; } - pathfinding_cache()->update(*this, std::min(from.z(), to.z()), std::max(from.z(), to.z())); - if (const std::optional p_cost = position_cost(*this, to, settings, *pathfinding_cache())) { - if (const std::optional t_cost = transition_cost(*this, from, to, settings, - *pathfinding_cache())) { + pathfinding_cache()->update( *this, std::min( from.z(), to.z() ), std::max( from.z(), to.z() ) ); + if( const std::optional p_cost = position_cost( *this, to, settings, *pathfinding_cache() ) ) { + if( const std::optional t_cost = transition_cost( *this, from, to, settings, + *pathfinding_cache() ) ) { return *p_cost + *t_cost; } } return std::nullopt; } -std::vector map::straight_route(const tripoint_bub_ms& from, - const tripoint_bub_ms& to, - const PathfindingSettings& settings) const +std::vector map::straight_route( const tripoint_bub_ms &from, + const tripoint_bub_ms &to, + const PathfindingSettings &settings ) const { - if (from == to || !inbounds(from) || !inbounds(to)) { + if( from == to || !inbounds( from ) || !inbounds( to ) ) { return {}; } - RealityBubblePathfindingCache& cache = *pathfinding_cache(); + RealityBubblePathfindingCache &cache = *pathfinding_cache(); const int pad = settings.rb_settings().padding().z(); - cache.update(*this, std::min(from.z(), to.z()) - pad, std::max(from.z(), to.z()) + pad); + cache.update( *this, std::min( from.z(), to.z() ) - pad, std::max( from.z(), to.z() ) + pad ); - std::vector line_path = line_to(from, to); + std::vector line_path = line_to( from, to ); const PathfindingFlags avoid = settings.avoid_mask() | PathfindingSettings::RoughTerrain; // Check all points for all fast avoidance. - if (!std::any_of(line_path.begin(), line_path.end(), [&cache, avoid](const tripoint_bub_ms& p) { - return cache.flags(p) & avoid; - })) { + if( !std::any_of( line_path.begin(), line_path.end(), [&cache, avoid]( const tripoint_bub_ms & p ) { + return cache.flags( p ) & avoid; + } ) ) { // Now do the slow check. Check if all the positions are valid. - if (std::all_of(line_path.begin(), line_path.end(), [this, &cache, - &settings](const tripoint_bub_ms& p) { - return position_cost(*this, p, settings, cache); - })) { + if( std::all_of( line_path.begin(), line_path.end(), [this, &cache, + &settings]( const tripoint_bub_ms & p ) { + return position_cost( *this, p, settings, cache ); + } ) ) { // Now check that all the transitions between each position are valid. - const tripoint_bub_ms* prev = &from; - if (std::find_if_not(line_path.begin(), line_path.end(), [this, &prev, &cache, - &settings](const tripoint_bub_ms& p) { - return transition_cost(*this, *std::exchange(prev, &p), p, settings, cache).has_value(); - }) == line_path.end()) { + const tripoint_bub_ms *prev = &from; + if( std::find_if_not( line_path.begin(), line_path.end(), [this, &prev, &cache, + &settings]( const tripoint_bub_ms & p ) { + return transition_cost( *this, *std::exchange( prev, &p ), p, settings, cache ).has_value(); + } ) == line_path.end() ) { return line_path; } } @@ -699,78 +686,77 @@ std::vector map::straight_route(const tripoint_bub_ms& from, return {}; } -std::vector map::route(const tripoint_bub_ms& from, const tripoint_bub_ms& to, - const PathfindingSettings& settings) const +std::vector map::route( const tripoint_bub_ms &from, const tripoint_bub_ms &to, + const PathfindingSettings &settings ) const { - if (from == to || !inbounds(from) || !inbounds(to)) { + if( from == to || !inbounds( from ) || !inbounds( to ) ) { return {}; } - RealityBubblePathfindingCache& cache = *pathfinding_cache(); + RealityBubblePathfindingCache &cache = *pathfinding_cache(); const int pad = settings.rb_settings().padding().z(); - cache.update(*this, std::min(from.z(), to.z()) - pad, std::max(from.z(), to.z()) + pad); + cache.update( *this, std::min( from.z(), to.z() ) - pad, std::max( from.z(), to.z() ) + pad ); // First, check for a simple straight line on flat ground. - if (from.z() == to.z()) { - std::vector line_path = straight_route(from, to, settings); - if (!line_path.empty()) { + if( from.z() == to.z() ) { + std::vector line_path = straight_route( from, to, settings ); + if( !line_path.empty() ) { return line_path; } } // If expected path length is greater than max distance, allow only line path, like above - if (rl_dist(from, to) > settings.max_distance()) { + if( rl_dist( from, to ) > settings.max_distance() ) { return {}; } - return pathfinder()->find_path(settings.rb_settings(), from, to, - [this, &settings, &cache](const tripoint_bub_ms& p) { - return position_cost(*this, p, settings, cache); - }, - [this, &settings, &cache](const tripoint_bub_ms& from, const tripoint_bub_ms& to) { - return transition_cost(*this, from, to, settings, cache); - }, - [](const tripoint_bub_ms& from, const tripoint_bub_ms& to) { - return 2 * distance_metric(from, to); - }); + return pathfinder()->find_path( settings.rb_settings(), from, to, + [this, &settings, &cache]( const tripoint_bub_ms & p ) { + return position_cost( *this, p, settings, cache ); + }, + [this, &settings, &cache]( const tripoint_bub_ms & from, const tripoint_bub_ms & to ) { + return transition_cost( *this, from, to, settings, cache ); + }, + []( const tripoint_bub_ms & from, const tripoint_bub_ms & to ) { + return 2 * distance_metric( from, to ); + } ); } -std::vector map::route(const tripoint& f, const tripoint& t, - const pathfinding_settings& settings) +std::vector map::route( const tripoint &f, const tripoint &t, + const pathfinding_settings &settings ) { const PathfindingSettings pf_settings = settings.to_new_pathfinding_settings(); // TODO: Get rid of this. - const tripoint_bub_ms from = tripoint_bub_ms::make_unchecked(f); - const tripoint_bub_ms to = tripoint_bub_ms::make_unchecked(t); - std::vector bub_route = route(from, to, pf_settings); - std::vector ret(bub_route.size()); - for (tripoint_bub_ms p : bub_route) - { - ret.push_back(p.raw()); - } - return ret; + const tripoint_bub_ms from = tripoint_bub_ms::make_unchecked( f ); + const tripoint_bub_ms to = tripoint_bub_ms::make_unchecked( t ); + std::vector bub_route = route( from, to, pf_settings ); + std::vector ret( bub_route.size() ); + for( tripoint_bub_ms p : bub_route ) { + ret.push_back( p.raw() ); + } + return ret; } -std::vector map::route(const tripoint_bub_ms& f, const tripoint_bub_ms& t, - const pathfinding_settings& settings) const +std::vector map::route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, + const pathfinding_settings &settings ) const { const PathfindingSettings pf_settings = settings.to_new_pathfinding_settings(); - return route(f, t, pf_settings); + return route( f, t, pf_settings ); } PathfindingSettings pathfinding_settings::to_new_pathfinding_settings() const { PathfindingSettings settings; - settings.set_bash_strength(bash_strength); - settings.set_max_distance(max_dist); - settings.set_max_cost(max_length * 50); - settings.set_climb_cost(climb_cost); - settings.set_avoid_opening_doors(!allow_open_doors); - settings.set_avoid_unlocking_doors(!allow_unlock_doors); - settings.set_avoid_dangerous_traps(avoid_traps); - if (avoid_rough_terrain) { - settings.set_avoid_rough_terrain(true); - } - settings.set_avoid_sharp(avoid_sharp); + settings.set_bash_strength( bash_strength ); + settings.set_max_distance( max_dist ); + settings.set_max_cost( max_length * 50 ); + settings.set_climb_cost( climb_cost ); + settings.set_avoid_opening_doors( !allow_open_doors ); + settings.set_avoid_unlocking_doors( !allow_unlock_doors ); + settings.set_avoid_dangerous_traps( avoid_traps ); + if( avoid_rough_terrain ) { + settings.set_avoid_rough_terrain( true ); + } + settings.set_avoid_sharp( avoid_sharp ); return settings; } diff --git a/src/pathfinding.h b/src/pathfinding.h index 5a18b67bb8238..759ff6d263ebe 100644 --- a/src/pathfinding.h +++ b/src/pathfinding.h @@ -46,71 +46,71 @@ enum class PathfindingFlag : uint8_t { class PathfindingFlags { -public: - constexpr PathfindingFlags() = default; + public: + constexpr PathfindingFlags() = default; - // NOLINTNEXTLINE(google-explicit-constructor) - constexpr PathfindingFlags(PathfindingFlag flag) : flags_(uint32_t{ 1 } << static_cast - (flag)) {} + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr PathfindingFlags( PathfindingFlag flag ) : flags_( uint32_t{ 1 } << static_cast + ( flag ) ) {} - constexpr void set_union(PathfindingFlags flags) { - flags_ |= flags.flags_; - } - constexpr void set_intersect(PathfindingFlags flags) { - flags_ &= flags.flags_; - } - constexpr void set_clear(PathfindingFlags flags) { - flags_ &= ~flags.flags_; - } + constexpr void set_union( PathfindingFlags flags ) { + flags_ |= flags.flags_; + } + constexpr void set_intersect( PathfindingFlags flags ) { + flags_ &= flags.flags_; + } + constexpr void set_clear( PathfindingFlags flags ) { + flags_ &= ~flags.flags_; + } - constexpr void clear() { - flags_ = 0; - } + constexpr void clear() { + flags_ = 0; + } - constexpr bool is_set(PathfindingFlag flag) const { - return flags_ & (uint32_t{ 1 } << static_cast(flag)); - } - constexpr bool is_set(PathfindingFlags flags) const { - return (flags_ & flags.flags_) == flags.flags_; - } - constexpr bool is_any_set() const { - return flags_; - } + constexpr bool is_set( PathfindingFlag flag ) const { + return flags_ & ( uint32_t{ 1 } << static_cast( flag ) ); + } + constexpr bool is_set( PathfindingFlags flags ) const { + return ( flags_ & flags.flags_ ) == flags.flags_; + } + constexpr bool is_any_set() const { + return flags_; + } - constexpr explicit operator bool() const { - return is_any_set(); - } + constexpr explicit operator bool() const { + return is_any_set(); + } - constexpr PathfindingFlags& operator|=(PathfindingFlags flags) { - set_union(flags); - return *this; - } + constexpr PathfindingFlags &operator|=( PathfindingFlags flags ) { + set_union( flags ); + return *this; + } - constexpr PathfindingFlags& operator&=(PathfindingFlags flags) { - set_intersect(flags); - return *this; - } + constexpr PathfindingFlags &operator&=( PathfindingFlags flags ) { + set_intersect( flags ); + return *this; + } -private: - uint32_t flags_ = 0; + private: + uint32_t flags_ = 0; }; -constexpr PathfindingFlags operator|(PathfindingFlags lhs, PathfindingFlags rhs) +constexpr PathfindingFlags operator|( PathfindingFlags lhs, PathfindingFlags rhs ) { return lhs |= rhs; } -constexpr PathfindingFlags operator&(PathfindingFlags lhs, PathfindingFlags rhs) +constexpr PathfindingFlags operator&( PathfindingFlags lhs, PathfindingFlags rhs ) { return lhs &= rhs; } -constexpr PathfindingFlags operator|(PathfindingFlags lhs, PathfindingFlag rhs) +constexpr PathfindingFlags operator|( PathfindingFlags lhs, PathfindingFlag rhs ) { return lhs |= rhs; } -constexpr PathfindingFlags operator&(PathfindingFlags lhs, PathfindingFlag rhs) +constexpr PathfindingFlags operator&( PathfindingFlags lhs, PathfindingFlag rhs ) { return lhs &= rhs; } @@ -118,612 +118,607 @@ constexpr PathfindingFlags operator&(PathfindingFlags lhs, PathfindingFlag rhs) // Note that this is in reverse order for memory locality: z, y, x. template using RealityBubbleArray = -std::array, MAPSIZE_Y>, OVERMAP_LAYERS>; + std::array, MAPSIZE_Y>, OVERMAP_LAYERS>; class RealityBubblePathfindingCache { -public: - RealityBubblePathfindingCache(); + public: + RealityBubblePathfindingCache(); - PathfindingFlags flags(const tripoint_bub_ms& p) const { - return flag_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; - } + PathfindingFlags flags( const tripoint_bub_ms &p ) const { + return flag_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } - int move_cost(const tripoint_bub_ms& p) const { - return move_cost_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; - } + int move_cost( const tripoint_bub_ms &p ) const { + return move_cost_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } - const std::pair& bash_range(const tripoint_bub_ms& p) const { - return bash_range_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; - } + const std::pair &bash_range( const tripoint_bub_ms &p ) const { + return bash_range_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } - const tripoint_bub_ms& up_destination(const tripoint_bub_ms& p) const { - return up_destinations_.find(p)->second; - } + const tripoint_bub_ms &up_destination( const tripoint_bub_ms &p ) const { + return up_destinations_.find( p )->second; + } - const tripoint_bub_ms& down_destination(const tripoint_bub_ms& p) const { - return down_destinations_.find(p)->second; - } + const tripoint_bub_ms &down_destination( const tripoint_bub_ms &p ) const { + return down_destinations_.find( p )->second; + } - void update(const map& here, int min_z, int max_z); + void update( const map &here, int min_z, int max_z ); - void invalidate(int z) { - dirty_z_levels_.emplace(z); - } + void invalidate( int z ) { + dirty_z_levels_.emplace( z ); + } - void invalidate(const tripoint_bub_ms& p) { - dirty_positions_[p.z()].emplace(p.xy()); - invalidate_dependants(p); - } + void invalidate( const tripoint_bub_ms &p ) { + dirty_positions_[p.z()].emplace( p.xy() ); + invalidate_dependants( p ); + } -private: - PathfindingFlags& flags_ref(const tripoint_bub_ms& p) { - return flag_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; - } + private: + PathfindingFlags &flags_ref( const tripoint_bub_ms &p ) { + return flag_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } - char& move_cost_ref(const tripoint_bub_ms& p) { - return move_cost_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; - } + char &move_cost_ref( const tripoint_bub_ms &p ) { + return move_cost_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } - std::pair& bash_range_ref(const tripoint_bub_ms& p) { - return bash_range_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; - } + std::pair &bash_range_ref( const tripoint_bub_ms &p ) { + return bash_range_cache_[p.z() + OVERMAP_DEPTH][p.y()][p.x()]; + } - bool vertical_move_destination(const map& here, ter_furn_flag flag, tripoint& t) const; + bool vertical_move_destination( const map &here, ter_furn_flag flag, tripoint &t ) const; - void invalidate_dependants(const tripoint_bub_ms& p); + void invalidate_dependants( const tripoint_bub_ms &p ); - void update(const map& here, const tripoint_bub_ms& p); + void update( const map &here, const tripoint_bub_ms &p ); - std::unordered_set dirty_z_levels_; - std::unordered_map> dirty_positions_; - std::unordered_map> dependants_by_position_; + std::unordered_set dirty_z_levels_; + std::unordered_map> dirty_positions_; + std::unordered_map> dependants_by_position_; - std::unordered_map up_destinations_; - std::unordered_map down_destinations_; - RealityBubbleArray flag_cache_; - RealityBubbleArray move_cost_cache_; - RealityBubbleArray> bash_range_cache_; + std::unordered_map up_destinations_; + std::unordered_map down_destinations_; + RealityBubbleArray flag_cache_; + RealityBubbleArray move_cost_cache_; + RealityBubbleArray> bash_range_cache_; }; class RealityBubblePathfindingSettings { -public: - bool allow_flying() const { - return allow_flying_; - } - void set_allow_flying(bool v = true) { - allow_flying_ = v; - } + public: + bool allow_flying() const { + return allow_flying_; + } + void set_allow_flying( bool v = true ) { + allow_flying_ = v; + } - bool allow_falling() const { - return allow_falling_; - } - void set_allow_falling(bool v = true) { - allow_falling_ = v; - } + bool allow_falling() const { + return allow_falling_; + } + void set_allow_falling( bool v = true ) { + allow_falling_ = v; + } - bool allow_stairways() const { - return allow_stairways_; - } - void set_allow_stairways(bool v = true) { - allow_stairways_ = v; - } + bool allow_stairways() const { + return allow_stairways_; + } + void set_allow_stairways( bool v = true ) { + allow_stairways_ = v; + } - int max_cost() const { - return max_cost_; - } - void set_max_cost(int v = 0) { - max_cost_ = v; - } + int max_cost() const { + return max_cost_; + } + void set_max_cost( int v = 0 ) { + max_cost_ = v; + } - const tripoint_rel_ms& padding() const { - return padding_; - } - void set_padding(const tripoint_rel_ms& v) { - padding_ = v; - } + const tripoint_rel_ms &padding() const { + return padding_; + } + void set_padding( const tripoint_rel_ms &v ) { + padding_ = v; + } -private: - bool allow_flying_ = false; - bool allow_falling_ = false; - bool allow_stairways_ = false; - int max_cost_ = 0; - tripoint_rel_ms padding_ = tripoint_rel_ms(16, 16, 1); + private: + bool allow_flying_ = false; + bool allow_falling_ = false; + bool allow_stairways_ = false; + int max_cost_ = 0; + tripoint_rel_ms padding_ = tripoint_rel_ms( 16, 16, 1 ); }; class RealityBubblePathfinder { -public: - // The class uses a lot of memory, and is safe to reuse as long as none of the provided - // functors to find_path make use of it. - explicit RealityBubblePathfinder(RealityBubblePathfindingCache* cache) : cache_(cache) {} - - template - std::vector find_path(const RealityBubblePathfindingSettings& settings, - const tripoint_bub_ms& from, const tripoint_bub_ms& to, - PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, HeuristicFn&& heuristic_fn); - -private: - // Minimum implementation of std::unordered_set interface that is used in the pathfinder. - class FastTripointSet - { public: - // Empty dummy type to return in place of an iterator. It isn't possible to give a nice - // iterator implementation, and the pathfinder doesn't use it anyways. - struct NotIterator {}; + // The class uses a lot of memory, and is safe to reuse as long as none of the provided + // functors to find_path make use of it. + explicit RealityBubblePathfinder( RealityBubblePathfindingCache *cache ) : cache_( cache ) {} - std::pair emplace(const tripoint_bub_ms& p); + template + std::vector find_path( const RealityBubblePathfindingSettings &settings, + const tripoint_bub_ms &from, const tripoint_bub_ms &to, + PositionCostFn &&p_cost_fn, MoveCostFn &&m_cost_fn, HeuristicFn &&heuristic_fn ); - void clear(); + private: + // Minimum implementation of std::unordered_set interface that is used in the pathfinder. + class FastTripointSet + { + public: + // Empty dummy type to return in place of an iterator. It isn't possible to give a nice + // iterator implementation, and the pathfinder doesn't use it anyways. + struct NotIterator {}; - std::size_t count(const tripoint_bub_ms& p) const { - return set_[p.z() + OVERMAP_DEPTH][p.y() * MAPSIZE_X + p.x()]; - } + std::pair emplace( const tripoint_bub_ms &p ); - private: - std::array dirty_ = {}; - std::array, OVERMAP_LAYERS> set_ = {}; - }; + void clear(); - // Minimum implementation of std::unordered_map interface that is used in the pathfinder. - class FastBestPathMap - { - public: - std::pair*, bool> try_emplace(const tripoint_bub_ms& child, - int cost, const tripoint_bub_ms& parent); + std::size_t count( const tripoint_bub_ms &p ) const { + return set_[p.z() + OVERMAP_DEPTH][p.y() * MAPSIZE_X + p.x()]; + } - std::size_t count(const tripoint_bub_ms& p) const { - return in_.count(p); - } + private: + std::array dirty_ = {}; + std::array, OVERMAP_LAYERS> set_ = {}; + }; - void clear() { - in_.clear(); - } + // Minimum implementation of std::unordered_map interface that is used in the pathfinder. + class FastBestPathMap + { + public: + std::pair*, bool> try_emplace( const tripoint_bub_ms &child, + int cost, const tripoint_bub_ms &parent ); - const std::pair& at(const tripoint_bub_ms& child) const { - return best_states_[child.z() + OVERMAP_DEPTH][child.y()][child.x()]; - } + std::size_t count( const tripoint_bub_ms &p ) const { + return in_.count( p ); + } - std::pair& operator[](const tripoint_bub_ms& child) { - return *try_emplace(child, 0, child).first; - } + void clear() { + in_.clear(); + } - private: - FastTripointSet in_; - RealityBubbleArray> best_states_; - }; - - template - std::vector find_path_impl(AStar& impl, - const RealityBubblePathfindingSettings& settings, - const tripoint_bub_ms& from, const tripoint_bub_ms& to, - PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, HeuristicFn&& heuristic_fn); - - RealityBubblePathfindingCache* cache_; - AStarPathfinder astar_; - BidirectionalAStarPathfinder + const std::pair &at( const tripoint_bub_ms &child ) const { + return best_states_[child.z() + OVERMAP_DEPTH][child.y()][child.x()]; + } + + std::pair &operator[]( const tripoint_bub_ms &child ) { + return *try_emplace( child, 0, child ).first; + } + + private: + FastTripointSet in_; + RealityBubbleArray> best_states_; + }; + + template + std::vector find_path_impl( AStar &impl, + const RealityBubblePathfindingSettings &settings, + const tripoint_bub_ms &from, const tripoint_bub_ms &to, + PositionCostFn &&p_cost_fn, MoveCostFn &&m_cost_fn, HeuristicFn &&heuristic_fn ); + + RealityBubblePathfindingCache *cache_; + AStarPathfinder astar_; + BidirectionalAStarPathfinder bidirectional_astar_; }; class PathfindingSettings { -public: - static constexpr PathfindingFlags RoughTerrain = PathfindingFlag::Slow | PathfindingFlag::Obstacle | - PathfindingFlag::Vehicle | PathfindingFlag::Sharp | PathfindingFlag::DangerousTrap; - static constexpr PathfindingFlags AnySizeRestriction = PathfindingFlag::RestrictTiny | - PathfindingFlag::RestrictSmall | - PathfindingFlag::RestrictMedium | PathfindingFlag::RestrictLarge | PathfindingFlag::RestrictHuge; - - bool avoid_falling() const { - return !rb_settings_.allow_falling(); - } - void set_avoid_falling(bool v = true) { - rb_settings_.set_allow_falling(!v); - } + public: + static constexpr PathfindingFlags RoughTerrain = PathfindingFlag::Slow | PathfindingFlag::Obstacle | + PathfindingFlag::Vehicle | PathfindingFlag::Sharp | PathfindingFlag::DangerousTrap; + static constexpr PathfindingFlags AnySizeRestriction = PathfindingFlag::RestrictTiny | + PathfindingFlag::RestrictSmall | + PathfindingFlag::RestrictMedium | PathfindingFlag::RestrictLarge | PathfindingFlag::RestrictHuge; + + bool avoid_falling() const { + return !rb_settings_.allow_falling(); + } + void set_avoid_falling( bool v = true ) { + rb_settings_.set_allow_falling( !v ); + } - bool avoid_unsheltered() const { - return is_set(PathfindingFlag::Unsheltered); - } - void set_avoid_unsheltered(bool v = true) { - set(PathfindingFlag::Unsheltered, v); - } + bool avoid_unsheltered() const { + return is_set( PathfindingFlag::Unsheltered ); + } + void set_avoid_unsheltered( bool v = true ) { + set( PathfindingFlag::Unsheltered, v ); + } - bool avoid_swimming() const { - return is_set(PathfindingFlag::Swimmable); - } - void set_avoid_swimming(bool v = true) { - set(PathfindingFlag::Swimmable, v); - } + bool avoid_swimming() const { + return is_set( PathfindingFlag::Swimmable ); + } + void set_avoid_swimming( bool v = true ) { + set( PathfindingFlag::Swimmable, v ); + } - bool avoid_ground() const { - return is_set(PathfindingFlag::Ground); - } - void set_avoid_ground(bool v = true) { - set(PathfindingFlag::Ground, v); - } + bool avoid_ground() const { + return is_set( PathfindingFlag::Ground ); + } + void set_avoid_ground( bool v = true ) { + set( PathfindingFlag::Ground, v ); + } - bool avoid_vehicle() const { - return is_set(PathfindingFlag::Vehicle); - } - void set_avoid_vehicle(bool v = true) { - set(PathfindingFlag::Vehicle, v); - } + bool avoid_vehicle() const { + return is_set( PathfindingFlag::Vehicle ); + } + void set_avoid_vehicle( bool v = true ) { + set( PathfindingFlag::Vehicle, v ); + } - bool avoid_climbing() const { - return avoid_climbing_; - } - void set_avoid_climbing(bool v = true) { - avoid_climbing_ = v; - maybe_set_avoid_obstacle(); - } + bool avoid_climbing() const { + return avoid_climbing_; + } + void set_avoid_climbing( bool v = true ) { + avoid_climbing_ = v; + maybe_set_avoid_obstacle(); + } - bool avoid_climb_stairway() const { - return !rb_settings_.allow_stairways(); - } - void set_avoid_climb_stairway(bool v = true) { - rb_settings_.set_allow_stairways(!v); - } + bool avoid_climb_stairway() const { + return !rb_settings_.allow_stairways(); + } + void set_avoid_climb_stairway( bool v = true ) { + rb_settings_.set_allow_stairways( !v ); + } - bool avoid_deep_water() const { - return is_set(PathfindingFlag::DeepWater); - } - void set_avoid_deep_water(bool v = true) { - set(PathfindingFlag::DeepWater, v); - } + bool avoid_deep_water() const { + return is_set( PathfindingFlag::DeepWater ); + } + void set_avoid_deep_water( bool v = true ) { + set( PathfindingFlag::DeepWater, v ); + } - std::optional size_restriction() const { - return size_restriction_; - } - PathfindingFlags size_restriction_mask() const { - return size_restriction_mask_; - } - void set_size_restriction(std::optional size_restriction = std::nullopt); + std::optional size_restriction() const { + return size_restriction_; + } + PathfindingFlags size_restriction_mask() const { + return size_restriction_mask_; + } + void set_size_restriction( std::optional size_restriction = std::nullopt ); - bool avoid_pits() const { - return is_set(PathfindingFlag::Pit); - } - void set_avoid_pits(bool v = true) { - set(PathfindingFlag::Pit, v); - } + bool avoid_pits() const { + return is_set( PathfindingFlag::Pit ); + } + void set_avoid_pits( bool v = true ) { + set( PathfindingFlag::Pit, v ); + } - bool avoid_opening_doors() const { - return avoid_opening_doors_; - } - void set_avoid_opening_doors(bool v = true) { - avoid_opening_doors_ = v; - maybe_set_avoid_obstacle(); - } + bool avoid_opening_doors() const { + return avoid_opening_doors_; + } + void set_avoid_opening_doors( bool v = true ) { + avoid_opening_doors_ = v; + maybe_set_avoid_obstacle(); + } - bool avoid_unlocking_doors() const { - return avoid_unlocking_doors_; - } - void set_avoid_unlocking_doors(bool v = true) { - avoid_unlocking_doors_ = v; - } + bool avoid_unlocking_doors() const { + return avoid_unlocking_doors_; + } + void set_avoid_unlocking_doors( bool v = true ) { + avoid_unlocking_doors_ = v; + } - bool avoid_rough_terrain() const { - return is_set(RoughTerrain); - } - void set_avoid_rough_terrain(bool v = true) { - set(RoughTerrain, v); - } + bool avoid_rough_terrain() const { + return is_set( RoughTerrain ); + } + void set_avoid_rough_terrain( bool v = true ) { + set( RoughTerrain, v ); + } - bool avoid_dangerous_traps() const { - return is_set(PathfindingFlag::DangerousTrap); - } - void set_avoid_dangerous_traps(bool v) { - set(PathfindingFlag::DangerousTrap, v); - } + bool avoid_dangerous_traps() const { + return is_set( PathfindingFlag::DangerousTrap ); + } + void set_avoid_dangerous_traps( bool v ) { + set( PathfindingFlag::DangerousTrap, v ); + } - bool avoid_sharp() const { - return is_set(PathfindingFlag::Sharp); - } - void set_avoid_sharp(bool v = true) { - set(PathfindingFlag::Sharp, v); - } + bool avoid_sharp() const { + return is_set( PathfindingFlag::Sharp ); + } + void set_avoid_sharp( bool v = true ) { + set( PathfindingFlag::Sharp, v ); + } - bool avoid_lava() const { - return is_set(PathfindingFlag::Lava); - } - void set_avoid_lava(bool v = true) { - set(PathfindingFlag::Lava, v); - } + bool avoid_lava() const { + return is_set( PathfindingFlag::Lava ); + } + void set_avoid_lava( bool v = true ) { + set( PathfindingFlag::Lava, v ); + } - bool avoid_hard_ground() const { - return is_set(PathfindingFlag::HardGround); - } - void set_avoid_hard_ground(bool v = true) { - set(PathfindingFlag::HardGround, v); - } + bool avoid_hard_ground() const { + return is_set( PathfindingFlag::HardGround ); + } + void set_avoid_hard_ground( bool v = true ) { + set( PathfindingFlag::HardGround, v ); + } - const std::function& maybe_avoid_dangerous_fields_fn() const { - return maybe_avoid_dangerous_fields_fn_; - } - void set_maybe_avoid_dangerous_fields_fn(std::function fn = - nullptr) { - maybe_avoid_dangerous_fields_fn_ = std::move(fn); - } + const std::function &maybe_avoid_dangerous_fields_fn() const { + return maybe_avoid_dangerous_fields_fn_; + } + void set_maybe_avoid_dangerous_fields_fn( std::function fn = + nullptr ) { + maybe_avoid_dangerous_fields_fn_ = std::move( fn ); + } - const std::function& maybe_avoid_fn() const { - return maybe_avoid_fn_; - } - void set_maybe_avoid_fn(std::function fn = nullptr) { - maybe_avoid_fn_ = std::move(fn); - } + const std::function &maybe_avoid_fn() const { + return maybe_avoid_fn_; + } + void set_maybe_avoid_fn( std::function fn = nullptr ) { + maybe_avoid_fn_ = std::move( fn ); + } - int max_distance() const { - return max_distance_; - } - void set_max_distance(int v = 0) { - max_distance_ = v; - } + int max_distance() const { + return max_distance_; + } + void set_max_distance( int v = 0 ) { + max_distance_ = v; + } - int max_cost() const { - return rb_settings_.max_cost(); - } - void set_max_cost(int v = 0) { - rb_settings_.set_max_cost(v); - } + int max_cost() const { + return rb_settings_.max_cost(); + } + void set_max_cost( int v = 0 ) { + rb_settings_.set_max_cost( v ); + } - int climb_cost() const { - return climb_cost_; - } - void set_climb_cost(int v = 0) { - climb_cost_ = v; - maybe_set_avoid_obstacle(); - } + int climb_cost() const { + return climb_cost_; + } + void set_climb_cost( int v = 0 ) { + climb_cost_ = v; + maybe_set_avoid_obstacle(); + } - bool is_digging() const { - return is_digging_; - } - void set_is_digging(bool v = true) { - is_digging_ = v; - maybe_set_avoid_obstacle(); - } + bool is_digging() const { + return is_digging_; + } + void set_is_digging( bool v = true ) { + is_digging_ = v; + maybe_set_avoid_obstacle(); + } - bool is_flying() const { - return rb_settings_.allow_flying(); - } - void set_is_flying(bool v = true) { - rb_settings_.set_allow_flying(v); - } + bool is_flying() const { + return rb_settings_.allow_flying(); + } + void set_is_flying( bool v = true ) { + rb_settings_.set_allow_flying( v ); + } - PathfindingFlags avoid_mask() const { - return avoid_mask_; - } + PathfindingFlags avoid_mask() const { + return avoid_mask_; + } - bool avoid_bashing() const { - return avoid_bashing_; - } - void set_avoid_bashing(bool v = true) { - avoid_bashing_ = v; - maybe_set_avoid_obstacle(); - } + bool avoid_bashing() const { + return avoid_bashing_; + } + void set_avoid_bashing( bool v = true ) { + avoid_bashing_ = v; + maybe_set_avoid_obstacle(); + } - int bash_strength() const { - return bash_strength_; - } - void set_bash_strength(int v = 0) { - bash_strength_ = v; - maybe_set_avoid_obstacle(); - } - int bash_rating_from_range(int min, int max) const; + int bash_strength() const { + return bash_strength_; + } + void set_bash_strength( int v = 0 ) { + bash_strength_ = v; + maybe_set_avoid_obstacle(); + } + int bash_rating_from_range( int min, int max ) const; -protected: - const RealityBubblePathfindingSettings& rb_settings() const { - return rb_settings_; - } + protected: + const RealityBubblePathfindingSettings &rb_settings() const { + return rb_settings_; + } - friend class map; + friend class map; -private: - bool is_set(PathfindingFlags flag) const { - return avoid_mask_.is_set(flag); - } - void set(PathfindingFlags flags, bool v) { - if (v) { - avoid_mask_.set_union(flags); + private: + bool is_set( PathfindingFlags flag ) const { + return avoid_mask_.is_set( flag ); } - else { - avoid_mask_.set_clear(flags); + void set( PathfindingFlags flags, bool v ) { + if( v ) { + avoid_mask_.set_union( flags ); + } else { + avoid_mask_.set_clear( flags ); + } } - } - void maybe_set_avoid_obstacle() { - // Check if we can short circuit checking obstacles. Significantly faster if so. - set(PathfindingFlag::Obstacle, !is_digging() && (climb_cost() <= 0 || avoid_climbing()) && - avoid_opening_doors() && (avoid_bashing() || bash_strength() <= 0)); - } + void maybe_set_avoid_obstacle() { + // Check if we can short circuit checking obstacles. Significantly faster if so. + set( PathfindingFlag::Obstacle, !is_digging() && ( climb_cost() <= 0 || avoid_climbing() ) && + avoid_opening_doors() && ( avoid_bashing() || bash_strength() <= 0 ) ); + } - PathfindingFlags avoid_mask_ = PathfindingFlag::Impassable; + PathfindingFlags avoid_mask_ = PathfindingFlag::Impassable; - std::optional size_restriction_; - PathfindingFlags size_restriction_mask_; - int max_distance_ = 0; + std::optional size_restriction_; + PathfindingFlags size_restriction_mask_; + int max_distance_ = 0; - bool avoid_bashing_ = false; - int bash_strength_ = 0; + bool avoid_bashing_ = false; + int bash_strength_ = 0; - // Expected terrain cost (2 is flat ground) of climbing a wire fence, 0 means no climbing - bool avoid_climbing_ = false; - int climb_cost_ = 0; + // Expected terrain cost (2 is flat ground) of climbing a wire fence, 0 means no climbing + bool avoid_climbing_ = false; + int climb_cost_ = 0; - bool is_digging_ = false; - RealityBubblePathfindingSettings rb_settings_; + bool is_digging_ = false; + RealityBubblePathfindingSettings rb_settings_; - bool avoid_opening_doors_ = false; - bool avoid_unlocking_doors_ = false; - std::function maybe_avoid_dangerous_fields_fn_; - std::function maybe_avoid_fn_; + bool avoid_opening_doors_ = false; + bool avoid_unlocking_doors_ = false; + std::function maybe_avoid_dangerous_fields_fn_; + std::function maybe_avoid_fn_; }; // Implementation Details template -std::vector RealityBubblePathfinder::find_path(const - RealityBubblePathfindingSettings& settings, const tripoint_bub_ms& from, - const tripoint_bub_ms& to, PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, - HeuristicFn&& heuristic_fn) +std::vector RealityBubblePathfinder::find_path( const + RealityBubblePathfindingSettings &settings, const tripoint_bub_ms &from, + const tripoint_bub_ms &to, PositionCostFn &&p_cost_fn, MoveCostFn &&m_cost_fn, + HeuristicFn &&heuristic_fn ) { // The bidirectional pathfinder doesn't handle paths generated by falling, so we have to fall // back to normal A* in this case. const bool might_fall = !settings.allow_flying() && settings.allow_falling(); - if (might_fall) { - return find_path_impl(astar_, settings, from, to, std::forward(p_cost_fn), - std::forward(m_cost_fn), std::forward(heuristic_fn)); - } - else { - return find_path_impl(bidirectional_astar_, settings, from, to, - std::forward(p_cost_fn), std::forward(m_cost_fn), - std::forward(heuristic_fn)); + if( might_fall ) { + return find_path_impl( astar_, settings, from, to, std::forward( p_cost_fn ), + std::forward( m_cost_fn ), std::forward( heuristic_fn ) ); + } else { + return find_path_impl( bidirectional_astar_, settings, from, to, + std::forward( p_cost_fn ), std::forward( m_cost_fn ), + std::forward( heuristic_fn ) ); } } template -std::vector RealityBubblePathfinder::find_path_impl(AStar& impl, const - RealityBubblePathfindingSettings& settings, const tripoint_bub_ms& from, - const tripoint_bub_ms& to, PositionCostFn&& p_cost_fn, MoveCostFn&& m_cost_fn, - HeuristicFn&& heuristic_fn) +std::vector RealityBubblePathfinder::find_path_impl( AStar &impl, const + RealityBubblePathfindingSettings &settings, const tripoint_bub_ms &from, + const tripoint_bub_ms &to, PositionCostFn &&p_cost_fn, MoveCostFn &&m_cost_fn, + HeuristicFn &&heuristic_fn ) { - const tripoint_bub_ms min = coord_max(coord_min(from, to) - settings.padding(), - tripoint_bub_ms(0, 0, -OVERMAP_DEPTH)); - const tripoint_bub_ms max = coord_min(coord_max(from, to) + settings.padding(), - tripoint_bub_ms(MAPSIZE_X - 1, MAPSIZE_Y - 1, OVERMAP_HEIGHT)); - const inclusive_cuboid bounds(min, max); - - return impl.find_path(settings.max_cost(), from, to, std::forward(p_cost_fn), - std::forward(m_cost_fn), std::forward(heuristic_fn), [this, &settings, - bounds](tripoint_bub_ms p, tripoint_bub_ms c, auto&& emit_fn) { - const tripoint_rel_ms offset(1, 1, 1); - const tripoint_bub_ms min = clamp(c - offset, bounds); - const tripoint_bub_ms max = clamp(c + offset, bounds); - if (settings.allow_flying()) { - for (int z = min.z(); z <= max.z(); ++z) { - for (int y = min.y(); y <= max.y(); ++y) { - for (int x = min.x(); x <= max.x(); ++x) { - if (x == c.x() && y == c.y() && z == c.z()) { - continue; - } - const tripoint_bub_ms next(x, y, z); - emit_fn(next); + const tripoint_bub_ms min = coord_max( coord_min( from, to ) - settings.padding(), + tripoint_bub_ms( 0, 0, -OVERMAP_DEPTH ) ); + const tripoint_bub_ms max = coord_min( coord_max( from, to ) + settings.padding(), + tripoint_bub_ms( MAPSIZE_X - 1, MAPSIZE_Y - 1, OVERMAP_HEIGHT ) ); + const inclusive_cuboid bounds( min, max ); + + return impl.find_path( settings.max_cost(), from, to, std::forward( p_cost_fn ), + std::forward( m_cost_fn ), std::forward( heuristic_fn ), [this, &settings, + bounds]( tripoint_bub_ms p, tripoint_bub_ms c, auto &&emit_fn ) { + const tripoint_rel_ms offset( 1, 1, 1 ); + const tripoint_bub_ms min = clamp( c - offset, bounds ); + const tripoint_bub_ms max = clamp( c + offset, bounds ); + if( settings.allow_flying() ) { + for( int z = min.z(); z <= max.z(); ++z ) { + for( int y = min.y(); y <= max.y(); ++y ) { + for( int x = min.x(); x <= max.x(); ++x ) { + if( x == c.x() && y == c.y() && z == c.z() ) { + continue; } + const tripoint_bub_ms next( x, y, z ); + emit_fn( next ); } } - return; } + return; + } - const PathfindingFlags flags = cache_->flags(c); + const PathfindingFlags flags = cache_->flags( c ); - // If we're falling, we can only continue falling. - if (c.z() > -OVERMAP_DEPTH && flags.is_set(PathfindingFlag::Air)) { - const tripoint_bub_ms down(c.xy(), c.z() - 1); - emit_fn(down); - return; - } + // If we're falling, we can only continue falling. + if( c.z() > -OVERMAP_DEPTH && flags.is_set( PathfindingFlag::Air ) ) { + const tripoint_bub_ms down( c.xy(), c.z() - 1 ); + emit_fn( down ); + return; + } - const point_rel_ms d((c.x() > p.x()) - (c.x() < p.x()), - (c.y() > p.y()) - (c.y() < p.y())); - const point_bub_ms n = c.xy() + d; - - if (d.x() != 0 && d.y() != 0) { - // Diagonal movement. Visit the following states: - // - // * * n - // . c * - // p . * - // - // where * is a state to be visited - // n is the state in movement direction, visited - // p is the parent state (not visited) - // c is the current state (not visited) - // . is not visited - if (min.x() <= n.x() && n.x() <= max.x()) { - for (int y = min.y(); y <= max.y(); ++y) { - const tripoint_bub_ms next(n.x(), y, c.z()); - emit_fn(next); - } + const point_rel_ms d( ( c.x() > p.x() ) - ( c.x() < p.x() ), + ( c.y() > p.y() ) - ( c.y() < p.y() ) ); + const point_bub_ms n = c.xy() + d; + + if( d.x() != 0 && d.y() != 0 ) { + // Diagonal movement. Visit the following states: + // + // * * n + // . c * + // p . * + // + // where * is a state to be visited + // n is the state in movement direction, visited + // p is the parent state (not visited) + // c is the current state (not visited) + // . is not visited + if( min.x() <= n.x() && n.x() <= max.x() ) { + for( int y = min.y(); y <= max.y(); ++y ) { + const tripoint_bub_ms next( n.x(), y, c.z() ); + emit_fn( next ); } - if (min.y() <= n.y() && n.y() <= max.y()) { - for (int x = min.x(); x <= max.x(); ++x) { - if (x == n.x()) { - continue; - } - const tripoint_bub_ms next(x, n.y(), c.z()); - emit_fn(next); + } + if( min.y() <= n.y() && n.y() <= max.y() ) { + for( int x = min.x(); x <= max.x(); ++x ) { + if( x == n.x() ) { + continue; } + const tripoint_bub_ms next( x, n.y(), c.z() ); + emit_fn( next ); } } - else if (d.x() != 0) { - // Horizontal movement. Visit the following states: - // - // . . * - // p c n - // . . * - // - // where * is a state to be visited - // n is the state in movement direction, visited - // p is the parent state (not visited) - // c is the current state (not visited) - // . is not visited - if (min.x() <= n.x() && n.x() <= max.x()) { - for (int y = min.y(); y <= max.y(); ++y) { - const tripoint_bub_ms next(n.x(), y, c.z()); - emit_fn(next); - } + } else if( d.x() != 0 ) { + // Horizontal movement. Visit the following states: + // + // . . * + // p c n + // . . * + // + // where * is a state to be visited + // n is the state in movement direction, visited + // p is the parent state (not visited) + // c is the current state (not visited) + // . is not visited + if( min.x() <= n.x() && n.x() <= max.x() ) { + for( int y = min.y(); y <= max.y(); ++y ) { + const tripoint_bub_ms next( n.x(), y, c.z() ); + emit_fn( next ); } } - else if (d.y() != 0) { - // Vertical movement. Visit the following states: - // - // * n * - // . c . - // . p . - // - // where * is a state to be visited - // n is the state in movement direction, visited - // p is the parent state (not visited) - // c is the current state (not visited) - // . is not visited - if (min.y() <= n.y() && n.y() <= max.y()) { - for (int x = min.x(); x <= max.x(); ++x) { - const tripoint_bub_ms next(x, n.y(), c.z()); - emit_fn(next); - } + } else if( d.y() != 0 ) { + // Vertical movement. Visit the following states: + // + // * n * + // . c . + // . p . + // + // where * is a state to be visited + // n is the state in movement direction, visited + // p is the parent state (not visited) + // c is the current state (not visited) + // . is not visited + if( min.y() <= n.y() && n.y() <= max.y() ) { + for( int x = min.x(); x <= max.x(); ++x ) { + const tripoint_bub_ms next( x, n.y(), c.z() ); + emit_fn( next ); } } - else { - // We arrived in this state with same x/y, which means - // we got here by traversing a staircase or similar. Need to - // visit all directions. - for (int y = min.y(); y <= max.y(); ++y) { - for (int x = min.x(); x <= max.x(); ++x) { - if (x == c.x() && y == c.y()) { - continue; - } - const tripoint_bub_ms next(x, y, c.z()); - emit_fn(next); + } else { + // We arrived in this state with same x/y, which means + // we got here by traversing a staircase or similar. Need to + // visit all directions. + for( int y = min.y(); y <= max.y(); ++y ) { + for( int x = min.x(); x <= max.x(); ++x ) { + if( x == c.x() && y == c.y() ) { + continue; } + const tripoint_bub_ms next( x, y, c.z() ); + emit_fn( next ); } } + } - if (settings.allow_stairways()) { - if (flags.is_set(PathfindingFlag::GoesDown)) { - emit_fn(cache_->down_destination(c)); - } - if (flags.is_set(PathfindingFlag::GoesUp)) { - emit_fn(cache_->up_destination(c)); - } - } - if (flags.is_set(PathfindingFlag::RampUp)) { - const tripoint_bub_ms above(c.xy(), c.z() + 1); - emit_fn(above); + if( settings.allow_stairways() ) { + if( flags.is_set( PathfindingFlag::GoesDown ) ) { + emit_fn( cache_->down_destination( c ) ); } - if (flags.is_set(PathfindingFlag::RampDown)) { - const tripoint_bub_ms below(c.xy(), c.z() - 1); - emit_fn(below); + if( flags.is_set( PathfindingFlag::GoesUp ) ) { + emit_fn( cache_->up_destination( c ) ); } - }); + } + if( flags.is_set( PathfindingFlag::RampUp ) ) { + const tripoint_bub_ms above( c.xy(), c.z() + 1 ); + emit_fn( above ); + } + if( flags.is_set( PathfindingFlag::RampDown ) ) { + const tripoint_bub_ms below( c.xy(), c.z() - 1 ); + emit_fn( below ); + } + } ); } diff --git a/src/point.cpp b/src/point.cpp index 400b811af0dc6..8008f7385db0f 100644 --- a/src/point.cpp +++ b/src/point.cpp @@ -159,10 +159,10 @@ std::vector closest_points_first( const point ¢er, int min_dist, int std::vector result; result.reserve( n + ( is_center_included ? 1 : 0 ) ); - find_point_closest_first(center, min_dist, max_dist, [&result](const point& p) { - result.push_back(p); + find_point_closest_first( center, min_dist, max_dist, [&result]( const point & p ) { + result.push_back( p ); return false; - } ); + } ); return result; } diff --git a/src/point.h b/src/point.h index b2708dc3c0ac4..d1a2c6930f6fd 100644 --- a/src/point.h +++ b/src/point.h @@ -293,8 +293,8 @@ std::vector closest_points_first( const point ¢er, int min_dist, int // TODO: Make this into an Input Iterator. template -std::optional find_point_closest_first(const point& center, int min_dist, int max_dist, - PredicateFn&& fn); +std::optional find_point_closest_first( const point ¢er, int min_dist, int max_dist, + PredicateFn &&fn ); inline constexpr tripoint tripoint_min { INT_MIN, INT_MIN, INT_MIN }; inline constexpr tripoint tripoint_max{ INT_MAX, INT_MAX, INT_MAX }; @@ -370,41 +370,41 @@ inline const std::array eight_horizontal_neighbors = { { }; template -std::optional find_point_closest_first(const point& center, int min_dist, int max_dist, - PredicateFn&& predicate_fn) +std::optional find_point_closest_first( const point ¢er, int min_dist, int max_dist, + PredicateFn &&predicate_fn ) { - min_dist = std::max(min_dist, 0); - max_dist = std::max(max_dist, 0); + min_dist = std::max( min_dist, 0 ); + max_dist = std::max( max_dist, 0 ); - if (min_dist > max_dist) { + if( min_dist > max_dist ) { return std::nullopt; } const int min_edge = min_dist * 2 + 1; const int max_edge = max_dist * 2 + 1; - const int n = max_edge * max_edge - (min_edge - 2) * (min_edge - 2); + const int n = max_edge * max_edge - ( min_edge - 2 ) * ( min_edge - 2 ); const bool is_center_included = min_dist == 0; - if (is_center_included) { - if (predicate_fn(center)) { + if( is_center_included ) { + if( predicate_fn( center ) ) { return center; } } - int x_init = std::max(min_dist, 1); - point p(x_init, 1 - x_init); + int x_init = std::max( min_dist, 1 ); + point p( x_init, 1 - x_init ); - point d(point_east); + point d( point_east ); - for (int i = 0; i < n; i++) { + for( int i = 0; i < n; i++ ) { const point next = center + p; - if (predicate_fn(next)) { + if( predicate_fn( next ) ) { return next; } - if (p.x == p.y || (p.x < 0 && p.x == -p.y) || (p.x > 0 && p.x == 1 - p.y)) { - std::swap(d.x, d.y); + if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) { + std::swap( d.x, d.y ); d.x = -d.x; } From 7faab86d4364d01e8e1d45fb1694f0eda1def55b Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Mon, 26 Aug 2024 01:19:36 +1000 Subject: [PATCH 4/5] remove comment --- src/map.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index fa9b6dde31dea..c8c60ff15270c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -10619,7 +10619,6 @@ void map::invalidate_max_populated_zlev( int zlev ) } } -// PERF: 40.66% (Part of npc::regen_ai_cache) bool map::has_potential_los( const tripoint &from, const tripoint &to ) const { const point key = sees_cache_key( from, to ); From 8cd63398b54b32baaca1eb3597cd2ec1fa3c36ec Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:59:40 +1000 Subject: [PATCH 5/5] Remove comment. --- src/map.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/map.h b/src/map.h index 94821bd49e5c7..060a63bb98b69 100644 --- a/src/map.h +++ b/src/map.h @@ -743,17 +743,6 @@ class map * @param pre_closed Never path through those points. They can still be the source or the destination. */ // TODO: fix point types (remove the first overload) - //std::vector route( const tripoint &f, const tripoint &t, - // const pathfinding_settings &settings, - //const std::function &avoid = []( const tripoint & ) { - // return false; - //} ) const; - //std::vector route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, - // const pathfinding_settings &settings, - //const std::function &avoid = []( const tripoint & ) { - // return false; - //} ) const; - bool can_teleport( const tripoint_bub_ms &t, const PathfindingSettings &settings ) const; bool can_move( const tripoint_bub_ms &f, const tripoint_bub_ms &t, const PathfindingSettings &settings ) const;