diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index 339cf79c9accf..ead888e976ae7 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -671,7 +671,7 @@ std::vector route_adjacent( const Character &you, const tripoin const std::vector &sorted = get_sorted_tiles_by_distance( you.pos_bub(), passable_tiles ); - const std::unordered_set &avoid = you.get_path_avoid(); + 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 ); @@ -745,7 +745,7 @@ static std::vector route_best_workbench( return best_bench_multi_a > best_bench_multi_b; }; std::stable_sort( sorted.begin(), sorted.end(), cmp ); - const std::unordered_set &avoid = you.get_path_avoid(); + const auto &avoid = you.get_path_avoid(); if( sorted.front() == you.pos_bub() ) { // We are on the best tile return {}; diff --git a/src/character.cpp b/src/character.cpp index 55bf924cb8916..accaf32d02879 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10072,18 +10072,14 @@ float Character::adjust_for_focus( float amount ) const return amount * ( effective_focus / 100.0f ); } -std::unordered_set Character::get_path_avoid() const +std::function Character::get_path_avoid() const { - std::unordered_set ret; - for( npc &guy : g->all_npcs() ) { - if( sees( guy ) ) { - ret.insert( guy.pos() ); - } - } - // TODO: Add known traps in a way that doesn't destroy performance - return ret; + return [this]( const tripoint & p ) { + Creature *critter = get_creature_tracker().creature_at( p, true ); + return critter && critter->is_npc() && this->sees( *critter ); + }; } const pathfinding_settings &Character::get_pathfinding_settings() const diff --git a/src/character.h b/src/character.h index becc00574f9a1..d5decfc01cebd 100644 --- a/src/character.h +++ b/src/character.h @@ -3265,7 +3265,7 @@ class Character : public Creature, public visitable int run_cost( int base_cost, bool diag = false ) const; const pathfinding_settings &get_pathfinding_settings() const override; - std::unordered_set get_path_avoid() const override; + std::function get_path_avoid() const override; /** * Get all hostile creatures currently visible to this player. */ diff --git a/src/creature.h b/src/creature.h index 00f731f421498..afae45e5266a5 100644 --- a/src/creature.h +++ b/src/creature.h @@ -943,7 +943,7 @@ class Creature : public viewer /** 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::unordered_set get_path_avoid() const = 0; + virtual std::function get_path_avoid() const = 0; bool underwater; void draw( const catacurses::window &w, const point_bub_ms &origin, bool inverted ) const; diff --git a/src/game.cpp b/src/game.cpp index 1ca52e5da2dbf..73b374381d623 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4528,9 +4528,8 @@ Creature *game::is_hostile_within( int distance, bool dangerous ) } const pathfinding_settings pf_settings = pathfinding_settings{ 8, distance, distance * 2, 4, true, true, false, true, false, false }; - static const std::unordered_set path_avoid = {}; - if( !get_map().route( u.pos(), critter->pos(), pf_settings, path_avoid ).empty() ) { + if( !get_map().route( u.pos(), critter->pos(), pf_settings ).empty() ) { return critter; } continue; @@ -7649,17 +7648,14 @@ std::optional> game::safe_route_to( Character &who, } }; route_t shortest_route; - std::unordered_set path_avoid; - for( const tripoint_bub_ms &p : points_in_radius( who.pos_bub(), 60 ) ) { - if( is_dangerous_tile( p.raw() ) ) { - path_avoid.insert( p.raw() ); - } - } for( const tripoint_bub_ms &p : here.points_in_radius( target, threshold, 0 ) ) { - if( path_avoid.count( p.raw() ) > 0 ) { - continue; // dont route to dangerous tiles + if( is_dangerous_tile( p.raw() ) ) { + continue; } - const route_t route = here.route( who.pos_bub(), p, who.get_pathfinding_settings(), path_avoid ); + const route_t route = here.route( who.pos_bub(), p, + who.get_pathfinding_settings(), [this]( const tripoint & p ) { + return is_dangerous_tile( p ); + } ); if( route.empty() ) { continue; // no route } diff --git a/src/map.h b/src/map.h index 0ec63e33cf12a..73ea8a9e8bc22 100644 --- a/src/map.h +++ b/src/map.h @@ -36,6 +36,7 @@ #include "lightmap.h" #include "line.h" #include "lru_cache.h" +#include "map_iterator.h" #include "map_selector.h" #include "mapdata.h" #include "maptile_fwd.h" @@ -731,10 +732,14 @@ class map // TODO: fix point types (remove the first overload) std::vector route( const tripoint &f, const tripoint &t, const pathfinding_settings &settings, - const std::unordered_set &pre_closed = {{ }} ) const; + 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::unordered_set &pre_closed = {{ }} ) const; + const std::function &avoid = []( const tripoint & ) { + return false; + } ) const; // Get a straight route from f to t, only along non-rough terrain. Returns an empty vector // if that is not possible. diff --git a/src/monmove.cpp b/src/monmove.cpp index 9d611a84234cc..dc7a9828e126e 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1011,10 +1011,7 @@ void monster::move() } else { path = here.straight_route( pos(), local_dest ); if( !path.empty() ) { - std::unordered_set closed = get_path_avoid(); - if( std::any_of( path.begin(), path.end(), [&closed]( const tripoint & p ) { - return closed.count( p ); - } ) ) { + if( std::any_of( path.begin(), path.end(), get_path_avoid() ) ) { path.clear(); } } diff --git a/src/monster.cpp b/src/monster.cpp index 2112989db4da5..7cc9dccef194c 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -3948,33 +3948,29 @@ const pathfinding_settings &monster::get_pathfinding_settings() const return type->path_settings; } -std::unordered_set monster::get_path_avoid() const +std::function monster::get_path_avoid() const { - std::unordered_set ret; - - map &here = get_map(); - int radius = std::min( sight_range( here.ambient_light_at( pos_bub() ) ), 5 ); - - for( const tripoint &p : here.points_in_radius( pos(), radius ) ) { - if( !can_move_to( p ) ) { - if( bash_skill() <= 0 || !here.is_bashable( p ) ) { - ret.insert( p ); - } + 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; } - } - - if( has_flag( mon_flag_PRIORITIZE_TARGETS ) ) { - radius = 2; - } else if( has_flag( mon_flag_PATH_AVOID_DANGER ) ) { - radius = 1; - } else { - return ret; - } - for( Creature *critter : here.get_creatures_in_radius( pos(), radius ) ) { - ret.insert( critter->pos() ); - } - return ret; + // 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, diff --git a/src/monster.h b/src/monster.h index a4c334b18496f..fe096e1577c02 100644 --- a/src/monster.h +++ b/src/monster.h @@ -604,7 +604,7 @@ class monster : public Creature void on_load(); const pathfinding_settings &get_pathfinding_settings() const override; - std::unordered_set get_path_avoid() 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/npc.cpp b/src/npc.cpp index 15152a006fa0f..79fddacf97d07 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -3337,50 +3337,32 @@ const pathfinding_settings &npc::get_pathfinding_settings( bool no_bashing ) con return *path_settings; } -std::unordered_set npc::get_path_avoid() const +std::function npc::get_path_avoid() const { - std::unordered_set ret; - for( Creature &critter : g->all_creatures() ) { - // TODO: Cache this somewhere - ret.insert( critter.pos() ); - } - map &here = get_map(); - if( rules.has_flag( ally_rule::avoid_doors ) ) { - for( const tripoint &p : here.points_in_radius( pos(), 30 ) ) { - if( here.open_door( *this, p, true, true ) ) { - ret.insert( p ); - } + return [this]( const tripoint & p ) { + if( get_creature_tracker().creature_at( p ) ) { + return true; } - } - if( rules.has_flag( ally_rule::avoid_locks ) ) { - for( const tripoint &p : here.points_in_radius( pos(), 30 ) ) { - if( doors::can_unlock_door( here, *this, tripoint_bub_ms( p ) ) ) { - ret.insert( p ); - } + map &here = get_map(); + if( rules.has_flag( ally_rule::avoid_doors ) && here.open_door( *this, p, true, true ) ) { + return true; } - } - if( rules.has_flag( ally_rule::hold_the_line ) ) { - for( const tripoint &p : here.points_in_radius( get_player_character().pos(), 1 ) ) { - if( here.close_door( p, true, true ) || here.move_cost( p ) > 2 ) { - ret.insert( p ); - } + if( rules.has_flag( ally_rule::avoid_locks ) && + doors::can_unlock_door( here, *this, tripoint_bub_ms( p ) ) ) { + return true; + } + if( rules.has_flag( ally_rule::hold_the_line ) && ( here.close_door( p, true, true ) || + here.move_cost( p ) > 2 ) ) { + return true; } - } - - for( const tripoint &p : here.points_in_radius( pos(), 6 ) ) { if( sees_dangerous_field( p ) ) { - ret.insert( p ); + return true; } - } - - // Why is this in path avoid if they can't move there at all? - for( const tripoint &p : here.points_in_radius( pos(), 6 ) ) { if( !can_move_to_vehicle_tile( here.getglobal( p ) ) ) { - ret.insert( p ); + return true; } - } - - return ret; + return false; + }; } mfaction_id npc::get_monster_faction() const diff --git a/src/npc.h b/src/npc.h index b8a35e9961128..c087293756f31 100644 --- a/src/npc.h +++ b/src/npc.h @@ -1217,7 +1217,7 @@ class npc : public Character const pathfinding_settings &get_pathfinding_settings() const override; const pathfinding_settings &get_pathfinding_settings( bool no_bashing ) const; - std::unordered_set get_path_avoid() const override; + std::function get_path_avoid() const override; // Item discovery and fetching diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index dc1efcffc0382..72dfc31d3c4ec 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -351,7 +351,7 @@ int map::extra_cost( const tripoint &cur, const tripoint &p, const pathfinding_s std::vector map::route( const tripoint &f, const tripoint &t, const pathfinding_settings &settings, - const std::unordered_set &pre_closed ) const + 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. @@ -365,16 +365,14 @@ std::vector map::route( const tripoint &f, const tripoint &t, if( !inbounds( t ) ) { tripoint clipped = t; clip_to_bounds( clipped ); - return route( f, clipped, settings, pre_closed ); + 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(), [&pre_closed]( const tripoint & p ) { - return pre_closed.count( p ); - } ) ) { + if( std::none_of( line_path.begin(), line_path.end(), avoid ) ) { return line_path; } } @@ -394,17 +392,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, clip_to_bounds( max.x, max.y, max.z ); pf.reset( min.z, max.z ); - // Make NPCs not want to path through player - // But don't make player pathing stop working - for( const tripoint &p : pre_closed ) { - if( p.x >= min.x && p.x < max.x && p.y >= min.y && p.y < max.y ) { - pf.close_point( p ); - } - } - // Start and end must not be closed - pf.unclose_point( f ); - pf.unclose_point( t ); pf.add_point( 0, 0, f, f ); bool done = false; @@ -447,6 +435,11 @@ std::vector map::route( const tripoint &f, const tripoint &t, continue; } + if( p != t && avoid( p ) ) { + layer.closed[index] = true; + continue; + } + if( layer.closed[index] ) { continue; } @@ -613,9 +606,9 @@ std::vector map::route( const tripoint &f, const tripoint &t, std::vector map::route( const tripoint_bub_ms &f, const tripoint_bub_ms &t, const pathfinding_settings &settings, - const std::unordered_set &pre_closed ) const + const std::function &avoid ) const { - std::vector raw_result = route( f.raw(), t.raw(), settings, pre_closed ); + 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 ) { diff --git a/tests/vehicle_fake_part_test.cpp b/tests/vehicle_fake_part_test.cpp index 74fc761d135d6..023c0cbaba488 100644 --- a/tests/vehicle_fake_part_test.cpp +++ b/tests/vehicle_fake_part_test.cpp @@ -340,8 +340,7 @@ TEST_CASE( "vehicle_with_fake_obstacle_parts_block_movement", "[vehicle][vehicle std::vector route = here.route( tripoint_bub_ms( test_origin - point( 2, 0 ) ), tripoint_bub_ms( test_origin + point( 2, 0 ) ), - you.get_pathfinding_settings(), - {} ); + you.get_pathfinding_settings() ); REQUIRE( !route.empty() ); CAPTURE( route ); REQUIRE( route.size() == 7 );