Skip to content

Commit

Permalink
[Rdy?]Redo random NPC spawn chance (cataclysmbnteam#2748)
Browse files Browse the repository at this point in the history
* Redo random NPC spawn chance

* Add debug log info
  • Loading branch information
Coolthulhu authored Jun 6, 2023
1 parent 6585139 commit 1479a17
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 14 deletions.
43 changes: 29 additions & 14 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11045,9 +11045,21 @@ void game::shift_monsters( const tripoint &shift )
critter_tracker->rebuild_cache();
}

double npc_overmap::spawn_chance_in_hour( int npc_num, double density )
{
static constexpr int days_in_year = 14 * 4;
const double expected_npc_count = days_in_year * density;
const double overcrowding_ratio = npc_num / expected_npc_count;
if( overcrowding_ratio < 1.0 ) {
return std::min( 1.0, density / 24.0 );
}
return ( 1.0 / 24.0 ) / overcrowding_ratio;
}

void game::perhaps_add_random_npc()
{
if( !calendar::once_every( 1_hours ) ) {
static constexpr time_duration spawn_interval = 1_hours;
if( !calendar::once_every( spawn_interval ) ) {
return;
}
// Create a new NPC?
Expand All @@ -11056,32 +11068,34 @@ void game::perhaps_add_random_npc()
return;
}

float density = get_option<float>( "NPC_DENSITY" );
static constexpr int density_search_radius = 60;
const float npc_num = overmap_buffer.get_npcs_near_player( density_search_radius ).size();
if( npc_num > 0.0 ) {
// 100%, 80%, 64%, 52%, 41%, 33%...
density *= std::pow( 0.8f, npc_num );
}

if( !x_in_y( density, 100 ) ) {
// We want the "NPC_DENSITY" to denote number of NPCs per week, per overmap, or so
// But soft-cap it at about a standard year (4*14 days) worth
const int npc_num = overmap_buffer.get_npcs_near_player(
npc_overmap::density_search_radius ).size();
const double chance = npc_overmap::spawn_chance_in_hour( npc_num,
get_option<float>( "NPC_DENSITY" ) );
add_msg( m_debug, "Random NPC spawn chance %0.3f%%", chance * 100 );
if( !x_in_y( chance, 1.0f ) ) {
return;
}

bool spawn_allowed = false;
tripoint_abs_omt spawn_point;
int counter = 0;
while( !spawn_allowed ) {
if( counter >= 10 ) {
if( counter >= 100 ) {
return;
}
static constexpr int radius_spawn_range = 120;
// Shouldn't be larger than search radius or it might get swarmy at the edges
static constexpr int radius_spawn_range = npc_overmap::density_search_radius;
const tripoint_abs_omt u_omt = u.global_omt_location();
spawn_point = u_omt + point( rng( -radius_spawn_range, radius_spawn_range ),
rng( -radius_spawn_range, radius_spawn_range ) );
spawn_point.z() = 0;
const oter_id oter = overmap_buffer.ter( spawn_point );
// shouldn't spawn on lakes or rivers.
if( !is_river_or_lake( oter ) ) {
// Shouldn't spawn on lakes or rivers.
// TODO: Prefer greater distance
if( !is_river_or_lake( oter ) || rl_dist( u_omt.xy(), spawn_point.xy() ) < 30 ) {
spawn_allowed = true;
}
counter += 1;
Expand All @@ -11105,6 +11119,7 @@ void game::perhaps_add_random_npc()
tmp->long_term_goal_action();
tmp->add_new_mission( mission::reserve_random( ORIGIN_ANY_NPC, tmp->global_omt_location(),
tmp->getID() ) );
dbg( DL::Debug ) << "Spawning a random NPC at " << spawn_point;
// This will make the new NPC active- if its nearby to the player
load_npcs();
}
Expand Down
8 changes: 8 additions & 0 deletions src/npc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1417,4 +1417,12 @@ void deactivate_weapon_cbm( npc &who );
// returns list of reloadable cbms.
std::vector<std::pair<bionic_id, item>> find_reloadable_cbms( npc &who );

namespace npc_overmap
{
/** Radius of the area in which we count NPCs for random spawn chance. */
static constexpr int density_search_radius = 120;
/** Chance that a random NPC spawns somewhere on overmap. */
double spawn_chance_in_hour( int current_npc_count, double density );
} // namespace npc_overmap

#endif // CATA_SRC_NPC_H
13 changes: 13 additions & 0 deletions tests/npc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,16 @@ TEST_CASE( "npc_move_through_vehicle_holes" )
CHECK( m2 == nullptr );

}

TEST_CASE( "random npc spawn chance" )
{
CHECK( npc_overmap::spawn_chance_in_hour( 0, 1.0 ) == Approx( 1.0 / 24.0 ) );
CHECK( npc_overmap::spawn_chance_in_hour( 0, 100.0 ) == 1.0 );

static constexpr int days_in_year = 14 * 4;
CHECK( npc_overmap::spawn_chance_in_hour( days_in_year, 1.0 ) == Approx( 1.0 / 24.0 ) );

CHECK( npc_overmap::spawn_chance_in_hour( 2 * days_in_year, 1.0 ) == Approx( 0.5 / 24.0 ) );
CHECK( npc_overmap::spawn_chance_in_hour( 4 * days_in_year, 1.0 ) == Approx( 0.25 / 24.0 ) );
CHECK( npc_overmap::spawn_chance_in_hour( 8 * days_in_year, 1.0 ) == Approx( 0.125 / 24.0 ) );
}

0 comments on commit 1479a17

Please sign in to comment.