Skip to content

Commit

Permalink
Merge pull request #70639 from prharvey/pathfinding_backport
Browse files Browse the repository at this point in the history
Backport optimizations from new pathfinder into old one.
  • Loading branch information
Maleclypse authored Jan 5, 2024
2 parents 044eaca + 4805af9 commit 297c0f0
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 142 deletions.
4 changes: 2 additions & 2 deletions src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ std::vector<tripoint_bub_ms> route_adjacent( const Character &you, const tripoin
const std::vector<tripoint_bub_ms> &sorted =
get_sorted_tiles_by_distance( you.pos_bub(), passable_tiles );

const std::set<tripoint> &avoid = you.get_path_avoid();
const std::unordered_set<tripoint> &avoid = you.get_path_avoid();
for( const tripoint_bub_ms &tp : sorted ) {
std::vector<tripoint_bub_ms> route =
here.route( you.pos_bub(), tp, you.get_pathfinding_settings(), avoid );
Expand Down Expand Up @@ -736,7 +736,7 @@ static std::vector<tripoint_bub_ms> route_best_workbench(
return best_bench_multi_a > best_bench_multi_b;
};
std::stable_sort( sorted.begin(), sorted.end(), cmp );
const std::set<tripoint> &avoid = you.get_path_avoid();
const std::unordered_set<tripoint> &avoid = you.get_path_avoid();
if( sorted.front() == you.pos_bub() ) {
// We are on the best tile
return {};
Expand Down
4 changes: 2 additions & 2 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10039,9 +10039,9 @@ float Character::adjust_for_focus( float amount ) const
return amount * ( effective_focus / 100.0f );
}

std::set<tripoint> Character::get_path_avoid() const
std::unordered_set<tripoint> Character::get_path_avoid() const
{
std::set<tripoint> ret;
std::unordered_set<tripoint> ret;
for( npc &guy : g->all_npcs() ) {
if( sees( guy ) ) {
ret.insert( guy.pos() );
Expand Down
2 changes: 1 addition & 1 deletion src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -3184,7 +3184,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::set<tripoint> get_path_avoid() const override;
std::unordered_set<tripoint> get_path_avoid() const override;
/**
* Get all hostile creatures currently visible to this player.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,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::set<tripoint> get_path_avoid() const = 0;
virtual std::unordered_set<tripoint> get_path_avoid() const = 0;

int moves;
bool underwater;
Expand Down
4 changes: 2 additions & 2 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4403,7 +4403,7 @@ 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::set<tripoint> path_avoid = {};
static const std::unordered_set<tripoint> path_avoid = {};

if( !get_map().route( u.pos(), critter->pos(), pf_settings, path_avoid ).empty() ) {
return critter;
Expand Down Expand Up @@ -7477,7 +7477,7 @@ std::optional<std::vector<tripoint_bub_ms>> game::safe_route_to( Character &who,
}
};
route_t shortest_route;
std::set<tripoint> path_avoid;
std::unordered_set<tripoint> 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() );
Expand Down
134 changes: 69 additions & 65 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1794,7 +1794,7 @@ bool map::furn_set( const tripoint &p, const furn_id &new_furniture, const bool
get_creature_tracker().invalidate_reachability_cache();
}
// TODO: Limit to changes that affect move cost, traps and stairs
set_pathfinding_cache_dirty( p.z );
set_pathfinding_cache_dirty( p );

// Make sure the furniture falls if it needs to
support_dirty( p );
Expand Down Expand Up @@ -2272,7 +2272,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.z );
set_pathfinding_cache_dirty( p );

tripoint above( p.xy(), p.z + 1 );
// Make sure that if we supported something and no longer do so, it falls down
Expand Down Expand Up @@ -6515,7 +6515,7 @@ void map::on_field_modified( const tripoint &p, const field_type &fd_type )
}

if( fd_type.is_dangerous() ) {
set_pathfinding_cache_dirty( p.z );
set_pathfinding_cache_dirty( p );
}

// Ensure blood type fields don't hang in the air
Expand Down Expand Up @@ -10093,6 +10093,13 @@ void map::set_pathfinding_cache_dirty( const int zlev )
}
}

void map::set_pathfinding_cache_dirty( const tripoint &p )
{
if( inbounds( p ) ) {
get_pathfinding_cache( p.z ).dirty_points.insert( p.xy() );
}
}

void map::queue_main_cleanup()
{
if( this != &get_map() ) {
Expand All @@ -10117,92 +10124,89 @@ const pathfinding_cache &map::get_pathfinding_cache_ref( int zlev ) const
return *pathfinding_caches[ OVERMAP_DEPTH ];
}
pathfinding_cache &cache = get_pathfinding_cache( zlev );
if( cache.dirty ) {
if( cache.dirty || !cache.dirty_points.empty() ) {
update_pathfinding_cache( zlev );
}

return cache;
}

void map::update_pathfinding_cache( int zlev ) const
void map::update_pathfinding_cache( const tripoint &p ) const
{
pathfinding_cache &cache = get_pathfinding_cache( zlev );
if( !cache.dirty ) {
if( !inbounds( p ) ) {
return;
}
pathfinding_cache &cache = get_pathfinding_cache( p.z );
pf_special cur_value = PF_NORMAL;

std::uninitialized_fill_n( &cache.special[0][0], MAPSIZE_X * MAPSIZE_Y, PF_NORMAL );
const_maptile tile = maptile_at_internal( p );

for( int smx = 0; smx < my_MAPSIZE; ++smx ) {
for( int smy = 0; smy < my_MAPSIZE; ++smy ) {
const submap *cur_submap = get_submap_at_grid( { smx, smy, zlev } );
if( !cur_submap ) {
return;
}

tripoint p( 0, 0, zlev );

for( int sx = 0; sx < SEEX; ++sx ) {
p.x = sx + smx * SEEX;
for( int sy = 0; sy < SEEY; ++sy ) {
p.y = sy + smy * SEEY;
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 );

pf_special cur_value = PF_NORMAL;
const int cost = move_cost_internal( furniture, terrain, field, veh, part );

const_maptile tile( cur_submap, point( sx, sy ) );

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 );
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;
}
}

const int cost = move_cost_internal( furniture, terrain, field, veh, part );
if( veh != nullptr ) {
cur_value |= PF_VEHICLE;
}

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;
}
}
for( const auto &fld : tile.get_field() ) {
const field_entry &cur = fld.second;
if( cur.is_dangerous() ) {
cur_value |= PF_FIELD;
}
}

if( veh != nullptr ) {
cur_value |= PF_VEHICLE;
}
if( ( !tile.get_trap_t().is_benign() || !terrain.trap.obj().is_benign() ) &&
!here.has_vehicle_floor( p ) ) {
cur_value |= PF_TRAP;
}

for( const auto &fld : tile.get_field() ) {
const field_entry &cur = fld.second;
if( cur.is_dangerous() ) {
cur_value |= PF_FIELD;
}
}
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( ( !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_SHARP ) && !here.has_vehicle_floor( p ) ) {
cur_value |= PF_SHARP;
}

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;
}
cache.special[p.x][p.y] = cur_value;
}

if( terrain.has_flag( ter_furn_flag::TFLAG_SHARP ) && !here.has_vehicle_floor( p ) ) {
cur_value |= PF_SHARP;
}
void map::update_pathfinding_cache( int zlev ) const
{
pathfinding_cache &cache = get_pathfinding_cache( zlev );

cache.special[p.x][p.y] = cur_value;
}
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 = false;
cache.dirty_points.clear();
}

void map::clip_to_bounds( tripoint &p ) const
Expand Down
10 changes: 8 additions & 2 deletions src/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ 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 invalidate_map_cache( int zlev );
Expand Down Expand Up @@ -693,10 +694,14 @@ class map
// TODO: fix point types (remove the first overload)
std::vector<tripoint> route( const tripoint &f, const tripoint &t,
const pathfinding_settings &settings,
const std::set<tripoint> &pre_closed = {{ }} ) const;
const std::unordered_set<tripoint> &pre_closed = {{ }} ) const;
std::vector<tripoint_bub_ms> route( const tripoint_bub_ms &f, const tripoint_bub_ms &t,
const pathfinding_settings &settings,
const std::set<tripoint> &pre_closed = {{ }} ) const;
const std::unordered_set<tripoint> &pre_closed = {{ }} ) const;

// Get a straight route from f to t, only along non-rough terrain. Returns an empty vector
// if that is not possible.
std::vector<tripoint> straight_route( const tripoint &f, const tripoint &t ) const;

// Vehicles: Common to 2D and 3D
VehicleList get_vehicles();
Expand Down Expand Up @@ -2286,6 +2291,7 @@ class map

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 );
Expand Down
23 changes: 21 additions & 2 deletions src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ bool monster::know_danger_at( const tripoint &p ) const
}

// Some things are only avoided if we're not attacking
if( 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
if( avoid_sharp && here.has_flag( ter_furn_flag::TFLAG_SHARP, p ) &&
!( type->size == creature_size::tiny || flies() ||
Expand Down Expand Up @@ -1068,7 +1069,25 @@ void monster::move()
if( pf_settings.max_dist >= rl_dist( get_location(), get_dest() ) &&
( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != local_dest ) ) {
// We need a new path
path = here.route( pos(), local_dest, pf_settings, get_path_avoid() );
if( can_pathfind() ) {
path = here.route( pos(), local_dest, pf_settings, get_path_avoid() );
if( path.empty() ) {
increment_pathfinding_cd();
}
} else {
path = here.straight_route( pos(), local_dest );
if( !path.empty() ) {
std::unordered_set<tripoint> closed = get_path_avoid();
if( std::any_of( path.begin(), path.end(), [&closed]( const tripoint & p ) {
return closed.count( p );
} ) ) {
path.clear();
}
}
}
if( !path.empty() ) {
reset_pathfinding_cd();
}
}

// Try to respect old paths, even if we can't pathfind at the moment
Expand Down
9 changes: 7 additions & 2 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,9 @@ tripoint_abs_ms monster::get_dest() const

void monster::set_dest( const tripoint_abs_ms &p )
{
if( !goal || rl_dist( p, *goal ) > 2 ) {
reset_pathfinding_cd();
}
goal = p;
}

Expand Down Expand Up @@ -2119,6 +2122,8 @@ void monster::apply_damage( Creature *source, bodypart_id /*bp*/, int dam,
if( is_dead_state() ) {
return;
}
// Ensure we can try to get at what hit us.
reset_pathfinding_cd();
hp -= dam;
if( hp < 1 ) {
set_killer( source );
Expand Down Expand Up @@ -3912,9 +3917,9 @@ const pathfinding_settings &monster::get_pathfinding_settings() const
return type->path_settings;
}

std::set<tripoint> monster::get_path_avoid() const
std::unordered_set<tripoint> monster::get_path_avoid() const
{
std::set<tripoint> ret;
std::unordered_set<tripoint> ret;

map &here = get_map();
int radius = std::min( sight_range( here.ambient_light_at( pos() ) ), 5 );
Expand Down
19 changes: 18 additions & 1 deletion src/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ class monster : public Creature
void on_load();

const pathfinding_settings &get_pathfinding_settings() const override;
std::set<tripoint> get_path_avoid() const override;
std::unordered_set<tripoint> get_path_avoid() const override;
private:
void process_trigger( mon_trigger trig, int amount );
void process_trigger( mon_trigger trig, const std::function<int()> &amount_func );
Expand All @@ -629,6 +629,23 @@ class monster : public Creature
monster_horde_attraction horde_attraction = MHA_NULL;
/** Found path. Note: Not used by monsters that don't pathfind! **/
std::vector<tripoint> path;

// Exponential backoff for stuck monsters. Massively reduces pathfinding CPU.
time_point pathfinding_cd = calendar::turn;
time_duration pathfinding_backoff = 2_seconds;

bool can_pathfind() const {
return pathfinding_cd <= calendar::turn;
}
void reset_pathfinding_cd() {
pathfinding_cd = calendar::turn;
pathfinding_backoff = 2_seconds;
}
void increment_pathfinding_cd() {
pathfinding_cd = calendar::turn + pathfinding_backoff;
pathfinding_backoff = std::min( pathfinding_backoff * 2, 10_seconds );
}

/** patrol points for monsters that can pathfind and have a patrol route! **/
std::vector<tripoint_abs_ms> patrol_route;
int next_patrol_point = -1;
Expand Down
Loading

0 comments on commit 297c0f0

Please sign in to comment.