From c2873639a97dfad0f0310538b32fab005572eb93 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Mon, 3 Jun 2024 17:07:24 -0700 Subject: [PATCH 1/8] refactor: make route() take a predicate for path_avoid --- src/activity_item_handling.cpp | 4 +-- src/character.cpp | 14 ++++----- src/character.h | 2 +- src/creature.h | 2 +- src/game.cpp | 18 +++++------- src/map.h | 9 ++++-- src/monmove.cpp | 6 ++-- src/monster.cpp | 48 ++++++++++++++---------------- src/monster.h | 2 +- src/npc.cpp | 54 ++++++++++++---------------------- src/npc.h | 2 +- src/pathfinding.cpp | 25 ++++++++-------- 12 files changed, 80 insertions(+), 106 deletions(-) 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 a372590232e03..38d25cb83e6c3 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10044,18 +10044,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->is_npc() && this->sees( *critter ); + }; } const pathfinding_settings &Character::get_pathfinding_settings() const diff --git a/src/character.h b/src/character.h index cdc37dcd3a37e..c5847b18cf2e9 100644 --- a/src/character.h +++ b/src/character.h @@ -3258,7 +3258,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 d241b3a7289bc..a8529566f0565 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4495,9 +4495,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; @@ -7616,17 +7615,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 c03f4e5bf2825..b16b837a7b5ca 100644 --- a/src/map.h +++ b/src/map.h @@ -1,4 +1,5 @@ #pragma once +#include "map_iterator.h" #ifndef CATA_SRC_MAP_H #define CATA_SRC_MAP_H @@ -729,10 +730,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; + 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; + 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 52135afd9afa5..1363d27bd0cfb 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1027,9 +1027,9 @@ 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 ); + const auto &avoid = get_path_avoid(); + if( std::any_of( path.begin(), path.end(), [&avoid]( const tripoint & p ) { + return avoid( p ); } ) ) { path.clear(); } diff --git a/src/monster.cpp b/src/monster.cpp index 984e921ccdeb5..c4a688d8f967b 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -3945,34 +3945,30 @@ 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; + } + + // 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_1 ) || + has_flag( mon_flag_PATH_AVOID_DANGER_2 ) ) { + radius = 1; + } else { + return false; } - } - - if( has_flag( mon_flag_PRIORITIZE_TARGETS ) ) { - radius = 2; - } else if( has_flag( mon_flag_PATH_AVOID_DANGER_1 ) || - has_flag( mon_flag_PATH_AVOID_DANGER_2 ) ) { - radius = 1; - } else { - return ret; - } - for( Creature *critter : here.get_creatures_in_radius( pos(), radius ) ) { - ret.insert( critter->pos() ); - } - - return ret; + 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 1cc75e0d45673..8537309fbf193 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -3332,50 +3332,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 d262caedb0a7e..09607bb897cdd 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -191,7 +191,7 @@ std::vector map::straight_route( const tripoint &f, const tripoint &t std::vector map::route( const tripoint &f, const tripoint &t, const pathfinding_settings &settings, - const std::unordered_set &pre_closed ) 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. @@ -205,15 +205,15 @@ 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]( const tripoint & p ) { + return avoid( p ); } ) ) { return line_path; } @@ -241,13 +241,6 @@ 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 ); @@ -266,6 +259,12 @@ std::vector map::route( const tripoint &f, const tripoint &t, continue; } + if( cur != f && cur != t && avoid( cur ) ) { + layer.closed[parent_index] = true; + continue; + } + + if( layer.gscore[parent_index] > max_length ) { // Shortest path would be too long, return empty vector return std::vector(); @@ -552,9 +551,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 + 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, std::move( avoid ) ); std::vector result; std::transform( raw_result.begin(), raw_result.end(), std::back_inserter( result ), []( const tripoint & p ) { From 25083c10fe19e72e3ca66864a745cec0a64eba3a Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Mon, 3 Jun 2024 17:37:31 -0700 Subject: [PATCH 2/8] no need to unclose start/end any more --- src/pathfinding.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 09607bb897cdd..1b7df181204d3 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -242,9 +242,6 @@ std::vector map::route( const tripoint &f, const tripoint &t, pf.reset( min.z, max.z ); - // 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; From e57064bd2a9e85f9191bb9283f8aeb209c562b0b Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Mon, 3 Jun 2024 17:44:38 -0700 Subject: [PATCH 3/8] move check to child tile --- src/pathfinding.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 1b7df181204d3..de34667050c18 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -256,12 +256,6 @@ std::vector map::route( const tripoint &f, const tripoint &t, continue; } - if( cur != f && cur != t && avoid( cur ) ) { - layer.closed[parent_index] = true; - continue; - } - - if( layer.gscore[parent_index] > max_length ) { // Shortest path would be too long, return empty vector return std::vector(); @@ -291,6 +285,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; } From 341d31b7274ef7b5f3f1e6e61b758841fea80592 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Wed, 5 Jun 2024 15:52:09 -0700 Subject: [PATCH 4/8] fix include order --- src/map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.h b/src/map.h index b16b837a7b5ca..19a409da364f9 100644 --- a/src/map.h +++ b/src/map.h @@ -1,5 +1,4 @@ #pragma once -#include "map_iterator.h" #ifndef CATA_SRC_MAP_H #define CATA_SRC_MAP_H @@ -37,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" From 6649027d5aab81199c9ea202d16b99b2053b3a4d Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Wed, 5 Jun 2024 15:56:32 -0700 Subject: [PATCH 5/8] const function& --- src/map.h | 4 ++-- src/monmove.cpp | 4 +--- src/pathfinding.cpp | 10 ++++------ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/map.h b/src/map.h index 19a409da364f9..6795f8079d602 100644 --- a/src/map.h +++ b/src/map.h @@ -730,12 +730,12 @@ class map // TODO: fix point types (remove the first overload) std::vector route( const tripoint &f, const tripoint &t, const pathfinding_settings &settings, - std::function avoid = []( const tripoint & ) { + 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, - std::function avoid = []( const tripoint & ) { + const std::function &avoid = []( const tripoint & ) { return false; } ) const; diff --git a/src/monmove.cpp b/src/monmove.cpp index 1363d27bd0cfb..11e7e1f4a0903 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1028,9 +1028,7 @@ void monster::move() path = here.straight_route( pos(), local_dest ); if( !path.empty() ) { const auto &avoid = get_path_avoid(); - if( std::any_of( path.begin(), path.end(), [&avoid]( const tripoint & p ) { - return avoid( p ); - } ) ) { + if( std::any_of( path.begin(), path.end(), avoid ) ) { path.clear(); } } diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index de34667050c18..f15fb57213404 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -191,7 +191,7 @@ std::vector map::straight_route( const tripoint &f, const tripoint &t std::vector map::route( const tripoint &f, const tripoint &t, const pathfinding_settings &settings, - std::function avoid ) 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. @@ -212,9 +212,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, 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]( const tripoint & p ) { - return avoid( p ); - } ) ) { + if( std::none_of( line_path.begin(), line_path.end(), avoid ) ) { return line_path; } } @@ -547,9 +545,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, - std::function avoid ) const + const std::function &avoid ) const { - std::vector raw_result = route( f.raw(), t.raw(), settings, std::move( avoid ) ); + 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 ) { From 35baf0882b36d511cbff068bb3588593cda730e6 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Wed, 5 Jun 2024 16:01:17 -0700 Subject: [PATCH 6/8] inline --- src/monmove.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/monmove.cpp b/src/monmove.cpp index 11e7e1f4a0903..a95013b4f1b90 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1027,8 +1027,7 @@ void monster::move() } else { path = here.straight_route( pos(), local_dest ); if( !path.empty() ) { - const auto &avoid = get_path_avoid(); - if( std::any_of( path.begin(), path.end(), avoid ) ) { + if( std::any_of( path.begin(), path.end(), get_path_avoid() ) ) { path.clear(); } } From c311e0629b8b79e9d353a7028bedca95b22d18ef Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Thu, 6 Jun 2024 15:57:24 -0700 Subject: [PATCH 7/8] null check --- src/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/character.cpp b/src/character.cpp index 38d25cb83e6c3..7d74509995cc1 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10050,7 +10050,7 @@ std::function Character::get_path_avoid() const return [this]( const tripoint & p ) { Creature *critter = get_creature_tracker().creature_at( p, true ); - return critter->is_npc() && this->sees( *critter ); + return critter && critter->is_npc() && this->sees( *critter ); }; } From ac30ebb90c84849551b96b4e1e4dd2564a4f773a Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Fri, 7 Jun 2024 09:44:43 -0700 Subject: [PATCH 8/8] fix vehicle_fake_part_test --- tests/vehicle_fake_part_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 );