From c7735fbcb61a0362676025fe8f0194f15857040c Mon Sep 17 00:00:00 2001 From: Efstratios Karatzas Date: Thu, 7 Mar 2024 20:25:35 -0800 Subject: [PATCH 1/2] feat: add reproduce area options in debug-map menu Extends the map submenu of the debug menu with an option to force monsters in a rectangular area to reproduce. cow goes moo, sheep goes bah --- src/debug_menu.cpp | 63 +++++++++++++++++++++++++++++++++------------- src/monster.cpp | 8 ++++-- src/monster.h | 3 ++- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 0e8fac04fa73..c30ce1551860 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -128,6 +128,7 @@ enum debug_menu_index { DEBUG_SPAWN_NPC, DEBUG_SPAWN_MON, DEBUG_GAME_STATE, + DEBUG_REPRODUCE_AREA, DEBUG_KILL_AREA, DEBUG_KILL_NPCS, DEBUG_MUTATE, @@ -295,6 +296,7 @@ static int map_uilist() { const std::vector uilist_initializer = { { uilist_entry( DEBUG_REVEAL_MAP, true, 'r', _( "Reveal map" ) ) }, + { uilist_entry( DEBUG_REPRODUCE_AREA, true, 'R', _( "Reproduce in Area" ) ) }, { uilist_entry( DEBUG_KILL_AREA, true, 'a', _( "Kill in Area" ) ) }, { uilist_entry( DEBUG_KILL_NPCS, true, 'k', _( "Kill NPCs" ) ) }, { uilist_entry( DEBUG_MAP_EDITOR, true, 'M', _( "Map editor" ) ) }, @@ -1381,6 +1383,33 @@ void benchmark( const int max_difference, bench_kind kind ) difference / 1000.0, 1000.0 * draw_counter / static_cast( difference ) ); } +// prompts player to select 2 points that will form a rectangular area +static std::optional> select_area() +{ + static_popup popup; + popup.on_top( true ); + popup.message( "%s", _( "Select first point." ) ); + + tripoint initial_pos = g->u.pos(); + const look_around_result first = g->look_around( false, initial_pos, initial_pos, + false, true, false, false, tripoint_zero, true ); + + if( !first.position ) { + return std::nullopt; + } + + popup.message( "%s", _( "Select second point." ) ); + const look_around_result second = g->look_around( false, initial_pos, *first.position, + true, true, false, false, tripoint_zero, true ); + + if( !second.position ) { + return std::nullopt; + } + + return get_map().points_in_rectangle( + first.position.value(), second.position.value() ); +} + void debug() { bool debug_menu_has_hotkey = hotkey_for_action( ACTION_DEBUG, false ) != -1; @@ -1484,30 +1513,30 @@ void debug() g->disp_NPCs(); break; } - case DEBUG_KILL_AREA: { - static_popup popup; - popup.on_top( true ); - popup.message( "%s", _( "Select first point." ) ); - - tripoint initial_pos = g->u.pos(); - const look_around_result first = g->look_around( false, initial_pos, initial_pos, - false, true, false, false, tripoint_zero, true ); - - if( !first.position ) { + case DEBUG_REPRODUCE_AREA: { + const std::optional> points_opt = select_area(); + if( !points_opt.has_value() ) { break; } - popup.message( "%s", _( "Select second point." ) ); - const look_around_result second = g->look_around( false, initial_pos, *first.position, - true, true, false, false, tripoint_zero, true ); + const tripoint_range points = points_opt.value(); + std::vector creatures = g->get_creatures_if( + [&points]( const Creature & critter ) -> bool { + return !critter.is_avatar() && critter.is_monster() && points.is_point_inside( critter.pos() ); + } ); - if( !second.position ) { + for( Creature *critter : creatures ) { + static_cast( critter )->try_reproduce( true ); + } + } + break; + case DEBUG_KILL_AREA: { + const std::optional> points_opt = select_area(); + if( !points_opt.has_value() ) { break; } - const tripoint_range points = get_map().points_in_rectangle( - first.position.value(), second.position.value() ); - + const tripoint_range points = points_opt.value(); std::vector creatures = g->get_creatures_if( [&points]( const Creature & critter ) -> bool { return !critter.is_avatar() && points.is_point_inside( critter.pos() ); diff --git a/src/monster.cpp b/src/monster.cpp index c518e99c3baa..1a37863150c0 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -463,7 +463,7 @@ void monster::try_upgrade( bool pin_time ) } } -void monster::try_reproduce() +void monster::try_reproduce( const bool force_reproduce ) { if( !reproduces ) { return; @@ -478,6 +478,10 @@ void monster::try_reproduce() baby_timer.emplace( calendar::turn + *type->baby_timer ); } + if( force_reproduce ) { + baby_timer = calendar::turn; + } + bool season_spawn = false; bool season_match = true; @@ -513,7 +517,7 @@ void monster::try_reproduce() // wildlife creatures that are pets of the player will spawn pet offspring const spawn_disposition disposition = is_pet() ? spawn_disposition::SpawnDisp_Pet : spawn_disposition::SpawnDisp_Default; - if( season_match && female && one_in( chance ) ) { + if( ( season_match && female && one_in( chance ) ) || force_reproduce ) { int spawn_cnt = rng( 1, type->baby_count ); if( type->baby_monster ) { g->m.add_spawn( type->baby_monster, spawn_cnt, pos(), disposition ); diff --git a/src/monster.h b/src/monster.h index b31d084ab6cf..0e638af03ef7 100644 --- a/src/monster.h +++ b/src/monster.h @@ -122,7 +122,8 @@ class monster : public Creature, public location_visitable int get_upgrade_time() const; void allow_upgrade(); void try_upgrade( bool pin_time ); - void try_reproduce(); + // check if monster should reproduce. boolean parameter can force reproduction + void try_reproduce( const bool force_reproduce = false ); void refill_udders(); void spawn( const tripoint &p ); m_size get_size() const override; From 873ef77bdbc145b3edc7251d3d956c6dd1842709 Mon Sep 17 00:00:00 2001 From: scarf Date: Fri, 8 Mar 2024 14:41:22 +0900 Subject: [PATCH 2/2] refactor: separate reproduce check and execution --- src/debug_menu.cpp | 2 +- src/monster.cpp | 53 ++++++++++++++++++++++++++-------------------- src/monster.h | 6 ++++-- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index c30ce1551860..a61ae56d20f6 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -1526,7 +1526,7 @@ void debug() } ); for( Creature *critter : creatures ) { - static_cast( critter )->try_reproduce( true ); + static_cast( critter )->reproduce(); } } break; diff --git a/src/monster.cpp b/src/monster.cpp index 1a37863150c0..4444721483e2 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -463,11 +463,8 @@ void monster::try_upgrade( bool pin_time ) } } -void monster::try_reproduce( const bool force_reproduce ) +void monster::try_reproduce() { - if( !reproduces ) { - return; - } // This can happen if the monster type has changed (from reproducing to non-reproducing monster) if( !type->baby_timer ) { return; @@ -478,10 +475,6 @@ void monster::try_reproduce( const bool force_reproduce ) baby_timer.emplace( calendar::turn + *type->baby_timer ); } - if( force_reproduce ) { - baby_timer = calendar::turn; - } - bool season_spawn = false; bool season_match = true; @@ -514,26 +507,40 @@ void monster::try_reproduce( const bool force_reproduce ) chance += 2; - // wildlife creatures that are pets of the player will spawn pet offspring - const spawn_disposition disposition = is_pet() ? spawn_disposition::SpawnDisp_Pet : - spawn_disposition::SpawnDisp_Default; - if( ( season_match && female && one_in( chance ) ) || force_reproduce ) { - int spawn_cnt = rng( 1, type->baby_count ); - if( type->baby_monster ) { - g->m.add_spawn( type->baby_monster, spawn_cnt, pos(), disposition ); - } else { - detached_ptr item_to_spawn = item::spawn( type->baby_egg, *baby_timer, spawn_cnt ); - if( disposition == spawn_disposition::SpawnDisp_Pet ) { - item_to_spawn->set_flag( flag_SPAWN_FRIENDLY ); - } - g->m.add_item_or_charges( pos(), std::move( item_to_spawn ), true ); - } + if( ( season_match && female && one_in( chance ) ) ) { + reproduce(); } - *baby_timer += *type->baby_timer; } } +void monster::reproduce() +{ + if( !reproduces ) { + return; + } + + const int spawn_cnt = rng( 1, type->baby_count ); + const auto birth = baby_timer ? *baby_timer : calendar::turn; + + // wildlife creatures that are pets of the player will spawn pet offspring + const spawn_disposition disposition = is_pet() + ? spawn_disposition::SpawnDisp_Pet + : spawn_disposition::SpawnDisp_Default; + + if( type->baby_monster ) { + g->m.add_spawn( type->baby_monster, spawn_cnt, pos(), disposition ); + } else { + detached_ptr item_to_spawn = item::spawn( type->baby_egg, birth, spawn_cnt ); + + if( disposition == spawn_disposition::SpawnDisp_Pet ) { + item_to_spawn->set_flag( flag_SPAWN_FRIENDLY ); + } + + g->m.add_item_or_charges( pos(), std::move( item_to_spawn ), true ); + } +} + void monster::refill_udders() { if( type->starting_ammo.empty() ) { diff --git a/src/monster.h b/src/monster.h index 0e638af03ef7..e333bd365788 100644 --- a/src/monster.h +++ b/src/monster.h @@ -122,8 +122,10 @@ class monster : public Creature, public location_visitable int get_upgrade_time() const; void allow_upgrade(); void try_upgrade( bool pin_time ); - // check if monster should reproduce. boolean parameter can force reproduction - void try_reproduce( const bool force_reproduce = false ); + /// Check if monster is ready to reproduce and do so if possible, refreshing baby timer. + void try_reproduce(); + /// Immediatly spawn an offspring without mutating baby timer. + void reproduce(); void refill_udders(); void spawn( const tripoint &p ); m_size get_size() const override;