From 6b708f197d4c95a1d0f1dee3f3e3a67bcb0d132f Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Thu, 10 Sep 2020 03:17:38 +0000 Subject: [PATCH 1/4] Split up monster_faction_map by zlevel --- src/creature_tracker.cpp | 11 ++--- src/creature_tracker.h | 5 ++- src/monmove.cpp | 87 +++++++++++++++++++++------------------- 3 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/creature_tracker.cpp b/src/creature_tracker.cpp index 037141c494552..3c2b9cd35efc4 100644 --- a/src/creature_tracker.cpp +++ b/src/creature_tracker.cpp @@ -97,10 +97,10 @@ void Creature_tracker::add_to_faction_map( const shared_ptr_fast &critt // Only 1 faction per mon at the moment. if( critter.friendly == 0 ) { - monster_faction_map_[ critter.faction ].insert( critter_ptr ); + monster_faction_map_[ critter.faction ][critter_ptr->pos().z].insert( critter_ptr ); } else { static const mfaction_str_id playerfaction( "player" ); - monster_faction_map_[ playerfaction ].insert( critter_ptr ); + monster_faction_map_[ playerfaction ][critter_ptr->pos().z].insert( critter_ptr ); } } @@ -182,11 +182,12 @@ void Creature_tracker::remove( const monster &critter ) } for( auto &pair : monster_faction_map_ ) { - const auto fac_iter = pair.second.find( *iter ); - if( fac_iter != pair.second.end() ) { + const int zpos = critter.pos().z; + const auto fac_iter = pair.second[zpos].find( *iter ); + if( fac_iter != pair.second[zpos].end() ) { // Need to do this manually because the shared pointer containing critter is kept valid // within removed_ and so the weak pointer in monster_faction_map_ is also valid. - pair.second.erase( fac_iter ); + pair.second[zpos].erase( fac_iter ); break; } } diff --git a/src/creature_tracker.h b/src/creature_tracker.h index 3057005bead0f..5b7f98c05baf3 100644 --- a/src/creature_tracker.h +++ b/src/creature_tracker.h @@ -3,6 +3,7 @@ #define CATA_SRC_CREATURE_TRACKER_H #include +#include #include #include #include @@ -32,8 +33,8 @@ class Creature_tracker } }; - std::unordered_map, weak_ptr_comparator>> - monster_faction_map_; + std::unordered_map, weak_ptr_comparator>>> + monster_faction_map_; /** * Creatures that get removed via @ref remove are stored here until the end of the turn. diff --git a/src/monmove.cpp b/src/monmove.cpp index bde10f1e2fab4..c22ea638d3902 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -435,33 +435,36 @@ void monster::plan() fleeing = fleeing || ( mood == MATT_FLEE ); if( friendly == 0 ) { - for( const auto &fac : factions ) { - mf_attitude faction_att = faction.obj().attitude( fac.first ); + for( const auto &fac_list : factions ) { + mf_attitude faction_att = faction.obj().attitude( fac_list.first ); if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) { continue; } - for( const weak_ptr_fast &weak : fac.second ) { - const shared_ptr_fast shared = weak.lock(); - if( !shared ) { - continue; - } - monster &mon = *shared; - float rating = rate_target( mon, dist, smart_planning ); - if( rating == dist ) { - ++valid_targets; - if( one_in( valid_targets ) ) { + // TODO: Limit this to levels the monster can see. + for( auto &fac : fac_list.second ) { + for( const weak_ptr_fast &weak : fac.second ) { + const shared_ptr_fast shared = weak.lock(); + if( !shared ) { + continue; + } + monster &mon = *shared; + float rating = rate_target( mon, dist, smart_planning ); + if( rating == dist ) { + ++valid_targets; + if( one_in( valid_targets ) ) { + target = &mon; + } + } + if( rating < dist ) { target = &mon; + dist = rating; + valid_targets = 1; + } + if( rating <= 5 ) { + anger += angers_hostile_near; + morale -= fears_hostile_near; } - } - if( rating < dist ) { - target = &mon; - dist = rating; - valid_targets = 1; - } - if( rating <= 5 ) { - anger += angers_hostile_near; - morale -= fears_hostile_near; } } } @@ -480,26 +483,28 @@ void monster::plan() } swarms = swarms && target == nullptr; // Only swarm if we have no target if( group_morale || swarms ) { - for( const weak_ptr_fast &weak : myfaction_iter->second ) { - const shared_ptr_fast shared = weak.lock(); - if( !shared ) { - continue; - } - monster &mon = *shared; - float rating = rate_target( mon, dist, smart_planning ); - if( group_morale && rating <= 10 ) { - morale += 10 - rating; - } - if( swarms ) { - if( rating < 5 ) { // Too crowded here - wander_pos.x = posx() * rng( 1, 3 ) - mon.posx(); - wander_pos.y = posy() * rng( 1, 3 ) - mon.posy(); - wandf = 2; - target = nullptr; - // Swarm to the furthest ally you can see - } else if( rating < FLT_MAX && rating > dist && wandf <= 0 ) { - target = &mon; - dist = rating; + for( auto &fac : myfaction_iter->second ) { + for( const weak_ptr_fast &weak : fac.second ) { + const shared_ptr_fast shared = weak.lock(); + if( !shared ) { + continue; + } + monster &mon = *shared; + float rating = rate_target( mon, dist, smart_planning ); + if( group_morale && rating <= 10 ) { + morale += 10 - rating; + } + if( swarms ) { + if( rating < 5 ) { // Too crowded here + wander_pos.x = posx() * rng( 1, 3 ) - mon.posx(); + wander_pos.y = posy() * rng( 1, 3 ) - mon.posy(); + wandf = 2; + target = nullptr; + // Swarm to the furthest ally you can see + } else if( rating < FLT_MAX && rating > dist && wandf <= 0 ) { + target = &mon; + dist = rating; + } } } } From c795a7cb39d24907c47970cfeb18522ea47fbb47 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Fri, 11 Sep 2020 02:10:45 +0000 Subject: [PATCH 2/4] Track whole-level floor opacity and expose a getter for it --- src/map.cpp | 26 ++++++++++++++++++++++++++ src/map.h | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/src/map.cpp b/src/map.cpp index edd59822dcace..d1664c88617fc 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -8067,6 +8067,29 @@ void map::build_obstacle_cache( const tripoint &start, const tripoint &end, } } +// If this ever shows up on profiling, maybe prepopulate one of these in the map cache for each level. +std::bitset map::get_inter_level_visibility( const int origin_zlevel ) const +{ + std::bitset seen_levels; + seen_levels.set( origin_zlevel + OVERMAP_DEPTH ); + for( int z = origin_zlevel + 1; z <= OVERMAP_HEIGHT; ++z ) { + if( get_cache_ref( z ).no_floor_gaps ) { + break; + } else { + seen_levels.set( z + OVERMAP_DEPTH ); + } + } + for( int z = origin_zlevel; z > -OVERMAP_DEPTH; --z ) { + if( get_cache_ref( z ).no_floor_gaps ) { + break; + } else { + // No floor means we can see the *lower* level. + seen_levels.set( z - 1 + OVERMAP_DEPTH ); + } + } + return seen_levels; +} + bool map::build_floor_cache( const int zlev ) { auto &ch = get_cache( zlev ); @@ -8077,6 +8100,8 @@ bool map::build_floor_cache( const int zlev ) auto &floor_cache = ch.floor_cache; std::uninitialized_fill_n( &floor_cache[0][0], ( MAPSIZE_X ) * ( MAPSIZE_Y ), true ); + bool &no_floor_gaps = ch.no_floor_gaps; + no_floor_gaps = true; for( int smx = 0; smx < my_MAPSIZE; ++smx ) { for( int smy = 0; smy < my_MAPSIZE; ++smy ) { @@ -8094,6 +8119,7 @@ bool map::build_floor_cache( const int zlev ) const int x = sx + smx * SEEX; const int y = sy + smy * SEEY; floor_cache[x][y] = false; + no_floor_gaps = false; } } } diff --git a/src/map.h b/src/map.h index bed529c384345..121ad27b1caaf 100644 --- a/src/map.h +++ b/src/map.h @@ -167,6 +167,8 @@ struct level_cache { bool outside_cache_dirty = false; bool floor_cache_dirty = false; bool seen_cache_dirty = false; + // This is a single value indicating that the entire level is floored. + bool no_floor_gaps = false; four_quadrants lm[MAPSIZE_X][MAPSIZE_Y]; float sm[MAPSIZE_X][MAPSIZE_Y]; @@ -1614,6 +1616,8 @@ class map void build_sunlight_cache( int pzlev ); public: void build_outside_cache( int zlev ); + // Get a bitmap indicating which layers are potentially visible from the target layer. + std::bitset get_inter_level_visibility( const int origin_zlevel )const ; // Builds a floor cache and returns true if the cache was invalidated. // Used to determine if seen cache should be rebuilt. bool build_floor_cache( int zlev ); From 3ec366f43c8335e6fca357631cd3278a922d701a Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Fri, 11 Sep 2020 02:11:38 +0000 Subject: [PATCH 3/4] Use whole-level floor opacity to streamline inter-faction agression checks --- src/monmove.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/monmove.cpp b/src/monmove.cpp index c22ea638d3902..39f9a95219d07 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -324,12 +324,15 @@ void monster::plan() const int angers_cub_threatened = type->has_anger_trigger( mon_trigger::PLAYER_NEAR_BABY ) ? 8 : 0; const int fears_hostile_near = type->has_fear_trigger( mon_trigger::HOSTILE_CLOSE ) ? 5 : 0; + map &here = get_map(); + std::bitset seen_levels = here.get_inter_level_visibility( pos().z ); bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale; bool swarms = has_flag( MF_SWARMS ); monster_attitude mood = attitude(); Character &player_character = get_player_character(); // If we can see the player, move toward them or flee, simpleminded animals are too dumb to follow the player. - if( friendly == 0 && sees( player_character ) && !has_flag( MF_PET_WONT_FOLLOW ) ) { + if( friendly == 0 && seen_levels.test( player_character.pos().z + OVERMAP_DEPTH ) && + sees( player_character ) && !has_flag( MF_PET_WONT_FOLLOW ) ) { dist = rate_target( player_character, dist, smart_planning ); fleeing = fleeing || is_fleeing( player_character ); target = &player_character; @@ -368,7 +371,7 @@ void monster::plan() } } else if( friendly != 0 && !docile ) { for( monster &tmp : g->all_monsters() ) { - if( tmp.friendly == 0 ) { + if( tmp.friendly == 0 && seen_levels.test( tmp.pos().z + OVERMAP_DEPTH ) ) { float rating = rate_target( tmp, dist, smart_planning ); if( rating < dist ) { target = &tmp; @@ -392,6 +395,9 @@ void monster::plan() if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) { continue; } + if( !seen_levels.test( who.pos().z + OVERMAP_DEPTH ) ) { + continue; + } float rating = rate_target( who, dist, smart_planning ); bool fleeing_from = is_fleeing( who ); @@ -441,8 +447,10 @@ void monster::plan() continue; } - // TODO: Limit this to levels the monster can see. for( auto &fac : fac_list.second ) { + if( !seen_levels.test( fac.first + OVERMAP_DEPTH ) ) { + continue; + } for( const weak_ptr_fast &weak : fac.second ) { const shared_ptr_fast shared = weak.lock(); if( !shared ) { @@ -484,6 +492,9 @@ void monster::plan() swarms = swarms && target == nullptr; // Only swarm if we have no target if( group_morale || swarms ) { for( auto &fac : myfaction_iter->second ) { + if( !seen_levels.test( fac.first + OVERMAP_DEPTH ) ) { + continue; + } for( const weak_ptr_fast &weak : fac.second ) { const shared_ptr_fast shared = weak.lock(); if( !shared ) { @@ -510,7 +521,6 @@ void monster::plan() } } - map &here = get_map(); // Operating monster keep you safe while they operate, how nice.... if( type->has_special_attack( "OPERATE" ) ) { if( has_effect( effect_operating ) ) { From e714a8e96c179788a724ed1d1c524dae0fd42603 Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Tue, 15 Sep 2020 10:33:40 +0300 Subject: [PATCH 4/4] Apply suggestions from code review --- src/map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.h b/src/map.h index 121ad27b1caaf..5702a2c62e5e1 100644 --- a/src/map.h +++ b/src/map.h @@ -1617,7 +1617,7 @@ class map public: void build_outside_cache( int zlev ); // Get a bitmap indicating which layers are potentially visible from the target layer. - std::bitset get_inter_level_visibility( const int origin_zlevel )const ; + std::bitset get_inter_level_visibility( int origin_zlevel )const ; // Builds a floor cache and returns true if the cache was invalidated. // Used to determine if seen cache should be rebuilt. bool build_floor_cache( int zlev );