Skip to content

Commit

Permalink
Merge pull request #44107 from kevingranade/segregate-inter-level-hos…
Browse files Browse the repository at this point in the history
…tility

Segregate inter level hostility
  • Loading branch information
ZhilkinSerg authored Sep 16, 2020
2 parents 007b298 + e714a8e commit 65a8b8a
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 47 deletions.
11 changes: 6 additions & 5 deletions src/creature_tracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ void Creature_tracker::add_to_faction_map( const shared_ptr_fast<monster> &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 );
}
}

Expand Down Expand Up @@ -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;
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/creature_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define CATA_SRC_CREATURE_TRACKER_H

#include <cstddef>
#include <map>
#include <memory>
#include <set>
#include <unordered_map>
Expand Down Expand Up @@ -32,8 +33,8 @@ class Creature_tracker
}
};

std::unordered_map<mfaction_id, std::set<weak_ptr_fast<monster>, weak_ptr_comparator>>
monster_faction_map_;
std::unordered_map<mfaction_id, std::map<int, std::set<weak_ptr_fast<monster>, weak_ptr_comparator>>>
monster_faction_map_;

/**
* Creatures that get removed via @ref remove are stored here until the end of the turn.
Expand Down
26 changes: 26 additions & 0 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OVERMAP_LAYERS> map::get_inter_level_visibility( const int origin_zlevel ) const
{
std::bitset<OVERMAP_LAYERS> 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 );
Expand All @@ -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 ) {
Expand All @@ -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;
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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<OVERMAP_LAYERS> 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 );
Expand Down
95 changes: 55 additions & 40 deletions src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OVERMAP_LAYERS> 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;
Expand Down Expand Up @@ -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;
Expand All @@ -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 );
Expand Down Expand Up @@ -435,33 +441,38 @@ 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<monster> &weak : fac.second ) {
const shared_ptr_fast<monster> shared = weak.lock();
if( !shared ) {
for( auto &fac : fac_list.second ) {
if( !seen_levels.test( fac.first + OVERMAP_DEPTH ) ) {
continue;
}
monster &mon = *shared;
float rating = rate_target( mon, dist, smart_planning );
if( rating == dist ) {
++valid_targets;
if( one_in( valid_targets ) ) {
for( const weak_ptr_fast<monster> &weak : fac.second ) {
const shared_ptr_fast<monster> 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;
}
}
}
Expand All @@ -480,32 +491,36 @@ void monster::plan()
}
swarms = swarms && target == nullptr; // Only swarm if we have no target
if( group_morale || swarms ) {
for( const weak_ptr_fast<monster> &weak : myfaction_iter->second ) {
const shared_ptr_fast<monster> shared = weak.lock();
if( !shared ) {
for( auto &fac : myfaction_iter->second ) {
if( !seen_levels.test( fac.first + OVERMAP_DEPTH ) ) {
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( const weak_ptr_fast<monster> &weak : fac.second ) {
const shared_ptr_fast<monster> 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;
}
}
}
}
}

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 ) ) {
Expand Down

0 comments on commit 65a8b8a

Please sign in to comment.