Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mongroup: subgroup spawning fixes #59281

Merged
merged 3 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions data/mods/TEST_DATA/monstergroups.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,46 @@
{ "group": "test_event_mongroup", "weight": 10 },
{ "group": "test_event_only", "weight": 5 }
]
},
{
"name": "test_l2_nested_mongroup",
"type": "monstergroup",
"monsters": [
{ "monster": "mon_test_speed_desc_base", "weight": 50 },
{ "monster": "mon_test_speed_desc_base_immobile", "weight": 50 }
]
},
{
"name": "test_l1_nested_mongroup",
"type": "monstergroup",
"monsters": [
{ "group": "test_l2_nested_mongroup", "weight": 5 },
{ "monster": "mon_test_shearable", "weight": 50 },
{ "monster": "mon_test_non_shearable", "weight": 50 }
]
},
{
"name": "test_top_level_mongroup",
"type": "monstergroup",
"monsters": [
{ "group": "test_l1_nested_mongroup", "weight": 5 },
{ "monster": "mon_test_CBM", "weight": 5 },
{ "monster": "mon_test_bovine", "weight": 5 }
]
},
{
"name": "test_nested_packsize",
"type": "monstergroup",
"monsters": [ { "monster": "mon_test_CBM", "pack_size": [ 2, 4 ] } ]
},
{
"name": "test_top_level_packsize",
"type": "monstergroup",
"monsters": [ { "group": "test_nested_packsize", "pack_size": [ 4, 6 ] } ]
},
{
"name": "test_top_level_no_packsize",
"type": "monstergroup",
"monsters": [ { "group": "test_nested_packsize" } ]
}
]
13 changes: 8 additions & 5 deletions src/explosion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,6 @@ void resonance_cascade( const tripoint &p )
Character &player_character = get_player_character();
const time_duration maxglow = time_duration::from_turns( 100 - 5 * trig_dist( p,
player_character.pos() ) );
MonsterGroupResult spawn_details;
if( maxglow > 0_turns ) {
const time_duration minglow = std::max( 0_turns, time_duration::from_turns( 60 - 5 * trig_dist( p,
player_character.pos() ) ) );
Expand Down Expand Up @@ -846,10 +845,14 @@ void resonance_cascade( const tripoint &p )
break;
case 13:
case 14:
case 15:
spawn_details = MonsterGroupManager::GetResultFromGroup( GROUP_NETHER );
g->place_critter_at( spawn_details.name, dest );
break;
case 15: {
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( GROUP_NETHER );
for( const MonsterGroupResult &mgr : spawn_details ) {
g->place_critter_at( mgr.name, dest );
}
}
break;
case 16:
case 17:
case 18:
Expand Down
31 changes: 18 additions & 13 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7518,8 +7518,10 @@ void map::rotten_item_spawn( const item &item, const tripoint &pnt )
}

if( rng( 0, 100 ) < comest->rot_spawn_chance ) {
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( mgroup );
add_spawn( spawn_details, pnt );
std::vector<MonsterGroupResult> spawn_details = MonsterGroupManager::GetResultFromGroup( mgroup );
for( const MonsterGroupResult &mgr : spawn_details ) {
add_spawn( mgr, pnt );
}
if( get_player_view().sees( pnt ) ) {
if( item.is_seed() ) {
add_msg( m_warning, _( "Something has crawled out of the %s plants!" ), item.get_plant_name() );
Expand Down Expand Up @@ -8019,18 +8021,21 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group,
if( pop ) {
// Populate the group from its population variable.
for( int m = 0; m < pop; m++ ) {
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( group.type, &pop );
if( !spawn_details.name ) {
continue;
}
monster tmp( spawn_details.name );
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( group.type, &pop );
for( const MonsterGroupResult &mgr : spawn_details ) {
if( !mgr.name ) {
continue;
}
monster tmp( mgr.name );

// If a monster came from a horde population, configure them to always be willing to rejoin a horde.
if( group.horde ) {
tmp.set_horde_attraction( MHA_ALWAYS );
}
for( int i = 0; i < spawn_details.pack_size; i++ ) {
group.monsters.push_back( tmp );
// If a monster came from a horde population, configure them to always be willing to rejoin a horde.
if( group.horde ) {
tmp.set_horde_attraction( MHA_ALWAYS );
}
for( int i = 0; i < mgr.pack_size; i++ ) {
group.monsters.push_back( tmp );
}
}
}
}
Expand Down
24 changes: 13 additions & 11 deletions src/map_field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,17 +742,19 @@ static void field_processor_monster_spawn( const tripoint &p, field_entry &cur,
int monster_spawn_count = int_level.monster_spawn_count;
if( monster_spawn_count > 0 && monster_spawn_chance > 0 && one_in( monster_spawn_chance ) ) {
for( ; monster_spawn_count > 0; monster_spawn_count-- ) {
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup(
int_level.monster_spawn_group, &monster_spawn_count );
if( !spawn_details.name ) {
continue;
}
if( const cata::optional<tripoint> spawn_point = random_point(
points_in_radius( p, int_level.monster_spawn_radius ),
[&pd]( const tripoint & n ) {
return pd.here.passable( n );
} ) ) {
pd.here.add_spawn( spawn_details, *spawn_point );
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( int_level.monster_spawn_group, &monster_spawn_count );
for( const MonsterGroupResult &mgr : spawn_details ) {
if( !mgr.name ) {
continue;
}
if( const cata::optional<tripoint> spawn_point =
random_point( points_in_radius( p, int_level.monster_spawn_radius ),
[&pd]( const tripoint & n ) {
return pd.here.passable( n );
} ) ) {
pd.here.add_spawn( mgr, *spawn_point );
}
}
}
}
Expand Down
40 changes: 24 additions & 16 deletions src/mapgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,18 @@ void map::generate( const tripoint &p, const time_point &when )
if( spawns.group && x_in_y( odds_after_density, 100 ) ) {
int pop = spawn_count * rng( spawns.population.min, spawns.population.max );
for( ; pop > 0; pop-- ) {
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( spawns.group, &pop );
if( !spawn_details.name ) {
continue;
}
if( const cata::optional<tripoint> pt =
random_point( *this, [this]( const tripoint & n ) {
return passable( n );
} ) ) {
add_spawn( spawn_details, *pt );
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( spawns.group, &pop );
for( const MonsterGroupResult &mgr : spawn_details ) {
if( !mgr.name ) {
continue;
}
if( const cata::optional<tripoint> pt =
random_point( *this, [this]( const tripoint & n ) {
return passable( n );
} ) ) {
add_spawn( mgr, *pt );
}
}
}
}
Expand Down Expand Up @@ -2355,11 +2358,13 @@ class jmapgen_monster : public jmapgen_piece

mongroup_id chosen_group = m_id.get( dat );
if( !chosen_group.is_null() ) {
MonsterGroupResult spawn_details =
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( chosen_group );
dat.m.add_spawn( spawn_details.name, spawn_count * pack_size.get(),
{ x.get(), y.get(), dat.m.get_abs_sub().z() },
friendly, -1, mission_id, name, data );
for( const MonsterGroupResult &mgr : spawn_details ) {
dat.m.add_spawn( mgr.name, spawn_count * pack_size.get(),
{ x.get(), y.get(), dat.m.get_abs_sub().z() },
friendly, -1, mission_id, name, data );
}
} else {
mtype_id chosen_type = ids.pick()->get( dat );
if( !chosen_type.is_null() ) {
Expand Down Expand Up @@ -6433,9 +6438,12 @@ void map::place_spawns( const mongroup_id &group, const int chance,
} while( impassable( p ) && tries > 0 );

// Pick a monster type
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( group, &num );
add_spawn( spawn_details.name, spawn_details.pack_size, { p, abs_sub.z() },
friendly, -1, mission_id, name, spawn_details.data );
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( group, &num );
for( const MonsterGroupResult &mgr : spawn_details ) {
add_spawn( mgr.name, mgr.pack_size, { p, abs_sub.z() },
friendly, -1, mission_id, name, mgr.data );
}
}
}

Expand Down
56 changes: 31 additions & 25 deletions src/mongroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ const MonsterGroup &MonsterGroupManager::GetUpgradedMonsterGroup( const mongroup
}

//Quantity is adjusted directly as a side effect of this function
MonsterGroupResult MonsterGroupManager::GetResultFromGroup(
const mongroup_id &group_name, int *quantity, bool *mon_found )
std::vector<MonsterGroupResult> MonsterGroupManager::GetResultFromGroup(
const mongroup_id &group_name, int *quantity, bool *mon_found, bool from_subgroup )
{
const MonsterGroup &group = GetUpgradedMonsterGroup( group_name );
int spawn_chance = rng( 1, group.event_adjusted_freq_total() );
//Our spawn details specify, by default, a single instance of the default monster
MonsterGroupResult spawn_details = MonsterGroupResult( group.defaultMonster, 1, spawn_data() );
std::vector<MonsterGroupResult> spawn_details;

bool monster_found = false;
// Loop invariant values
Expand All @@ -131,19 +131,6 @@ MonsterGroupResult MonsterGroupManager::GetResultFromGroup(
valid_entry = false;
}

// Check for monsters within subgroup
if( valid_entry && it->is_group() ) {
MonsterGroupResult tmp = GetResultFromGroup( it->group, quantity, &monster_found );
if( monster_found ) {
// Valid monster found within subgroup, break early
spawn_details = tmp;
break;
} else if( quantity ) {
// Nothing found in subgroup, reset quantity
( *quantity )++;
}
continue;
}
//Insure that the time is not before the spawn first appears or after it stops appearing
valid_entry = valid_entry && ( calendar::start_of_cataclysm + it->starts < calendar::turn );
valid_entry = valid_entry && ( it->lasts_forever() ||
Expand Down Expand Up @@ -201,19 +188,34 @@ MonsterGroupResult MonsterGroupManager::GetResultFromGroup(
valid_entry = false;
}

const int pack_size = it->pack_maximum > 1 ? rng( it->pack_minimum, it->pack_maximum ) : 1;

// Check for monsters within subgroup
bool found_in_subgroup = false;
int tmp_qty = !!quantity ? *quantity : 1;
std::vector<MonsterGroupResult> tmp_grp_list;
if( valid_entry && it->is_group() ) {
for( int i = 0; i < pack_size; i++ ) {
std::vector<MonsterGroupResult> tmp_grp =
GetResultFromGroup( it->group, !!quantity ? &tmp_qty : nullptr, &found_in_subgroup, true );
tmp_grp_list.insert( tmp_grp_list.end(), tmp_grp.begin(), tmp_grp.end() );
}
}

//If the entry was valid, check to see if we actually spawn it
if( valid_entry ) {
//If the monsters frequency is greater than the spawn_chance, select this spawn rule
if( it->frequency >= spawn_chance ) {
if( it->pack_maximum > 1 ) {
spawn_details = MonsterGroupResult( it->name, rng( it->pack_minimum, it->pack_maximum ), it->data );
if( found_in_subgroup ) {
//If spawned from a subgroup, we've already obtained that data
spawn_details = tmp_grp_list;
} else {
spawn_details = MonsterGroupResult( it->name, 1, it->data );
}
//And if a quantity pointer with remaining value was passed, will modify the external value as a side effect
//We will reduce it by the spawn rule's cost multiplier
if( quantity ) {
*quantity -= std::max( 1, it->cost_multiplier * spawn_details.pack_size );
spawn_details.emplace_back( MonsterGroupResult( it->name, pack_size, it->data ) );
//And if a quantity pointer with remaining value was passed, will modify the external value as a side effect
//We will reduce it by the spawn rule's cost multiplier
if( quantity ) {
*quantity -= std::max( 1, it->cost_multiplier * pack_size );
}
}
monster_found = true;
//Otherwise, subtract the frequency from spawn result for the next loop around
Expand All @@ -224,13 +226,17 @@ MonsterGroupResult MonsterGroupManager::GetResultFromGroup(
}

// Force quantity to decrement regardless of whether we found a monster.
if( quantity && !monster_found ) {
if( quantity && !monster_found && !from_subgroup ) {
( *quantity )--;
}
if( mon_found ) {
( *mon_found ) = monster_found;
}

if( !from_subgroup && spawn_details.empty() ) {
spawn_details.emplace_back( MonsterGroupResult( group.defaultMonster, 1, spawn_data() ) );
}

return spawn_details;
}

Expand Down
4 changes: 2 additions & 2 deletions src/mongroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ class MonsterGroupManager
static void LoadMonsterBlacklist( const JsonObject &jo );
static void LoadMonsterWhitelist( const JsonObject &jo );
static void FinalizeMonsterGroups();
static MonsterGroupResult GetResultFromGroup( const mongroup_id &group, int *quantity = nullptr,
bool *mon_found = nullptr );
static std::vector<MonsterGroupResult> GetResultFromGroup( const mongroup_id &group,
int *quantity = nullptr, bool *mon_found = nullptr, bool from_subgroup = false );
static bool IsMonsterInGroup( const mongroup_id &group, const mtype_id &monster );
static bool isValidMonsterGroup( const mongroup_id &group );
static const mongroup_id &Monster2Group( const mtype_id &monster );
Expand Down
16 changes: 10 additions & 6 deletions src/player_hardcoded_effects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,9 +592,11 @@ static void eff_fun_teleglow( Character &u, effect &it )
if( here.impassable( dest ) ) {
here.make_rubble( dest, f_rubble_rock, true );
}
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup(
GROUP_NETHER );
g->place_critter_at( spawn_details.name, dest );
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( GROUP_NETHER );
for( const MonsterGroupResult &mgr : spawn_details ) {
g->place_critter_at( mgr.name, dest );
}
if( uistate.distraction_hostile_spotted && player_character.sees( dest ) ) {
g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far,
_( "A monster appears nearby!" ) );
Expand Down Expand Up @@ -1300,9 +1302,11 @@ void Character::hardcoded_effects( effect &it )
if( here.impassable( dest ) ) {
here.make_rubble( dest, f_rubble_rock, true );
}
MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup(
GROUP_NETHER );
g->place_critter_at( spawn_details.name, dest );
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( GROUP_NETHER );
for( const MonsterGroupResult &mgr : spawn_details ) {
g->place_critter_at( mgr.name, dest );
}
if( uistate.distraction_hostile_spotted && player_character.sees( dest ) ) {
g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far,
_( "A monster appears nearby!" ) );
Expand Down
Loading