From 365395d6fe76b6e50b5041dba5dc3a453e7d83fe Mon Sep 17 00:00:00 2001
From: Anton Simakov <67688115+GuardianDll@users.noreply.github.com>
Date: Thu, 5 Sep 2024 02:42:58 +0200
Subject: [PATCH] add baby_monster_group (#76122)

* add baby_monster_group

* Simplify baby check

* Update src/monmove.cpp

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix lost id

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
 doc/MONSTERS.md          |  5 +++--
 src/monmove.cpp          |  4 +++-
 src/monster.cpp          |  7 +++++++
 src/monstergenerator.cpp | 15 +++++++++++++--
 src/mtype.cpp            |  1 +
 src/mtype.h              |  1 +
 6 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/doc/MONSTERS.md b/doc/MONSTERS.md
index 72a1c22ade0b1..6cc1c105a2106 100644
--- a/doc/MONSTERS.md
+++ b/doc/MONSTERS.md
@@ -545,8 +545,9 @@ The monster's reproduction cycle, if any. Supports:
 
 Field          | Description
 ---            | ---
-`baby_monster` | (string, optional) the id of the monster spawned on reproduction for monsters who give live births. You must declare either this or `baby_egg` for reproduction to work.
-`baby_egg`     | (string, optional) The id of the egg type to spawn for egg-laying monsters. You must declare either this or "baby_monster" for reproduction to work. (see [JSON_INFO.md](JSON_INFO.md#comestibles) `rot_spawn`)
+`baby_monster` | (string, optional) the id of the monster spawned on reproduction for monsters who give live births. You must declare either this, `baby_monster_group` or `baby_egg` for reproduction to work.
+`baby_monster_group` | (string, optional) the id of the monstergroup spawned on reproduction for monsters who give live births. You must declare either this, `baby_monster`, or `baby_egg` for reproduction to work.
+`baby_egg`     | (string, optional) The id of the egg type to spawn for egg-laying monsters. You must declare either this, `baby_monster_group` or `baby_monster` for reproduction to work. (see [JSON_INFO.md](JSON_INFO.md#comestibles) `rot_spawn`)
 `baby_count`   | (int) Number of new creatures or eggs to spawn on reproduction.
 `baby_timer`   | (int) Number of days between reproduction events.
 
diff --git a/src/monmove.cpp b/src/monmove.cpp
index a279b139762c7..e0925364d2a9d 100644
--- a/src/monmove.cpp
+++ b/src/monmove.cpp
@@ -34,6 +34,7 @@
 #include "memory_fast.h"
 #include "messages.h"
 #include "monfaction.h"
+#include "mongroup.h"
 #include "monster_oracle.h"
 #include "mtype.h"
 #include "npc.h"
@@ -415,7 +416,8 @@ void monster::anger_cub_threatened( monster_plan &mon_plan )
     }
 
     for( monster &tmp : g->all_monsters() ) {
-        if( type->baby_monster == tmp.type->id ) {
+        if( type->baby_monster == tmp.type->id ||
+            MonsterGroupManager::IsMonsterInGroup( type->baby_monster_group, tmp.type->id ) ) {
             // baby nearby; is the player too close?
             mon_plan.dist = tmp.rate_target( *mon_plan.target, mon_plan.dist, mon_plan.smart_planning );
             if( mon_plan.dist <= 3 ) {
diff --git a/src/monster.cpp b/src/monster.cpp
index 8ade126638dbb..5da1331f05f43 100644
--- a/src/monster.cpp
+++ b/src/monster.cpp
@@ -607,6 +607,13 @@ void monster::try_reproduce()
             int spawn_cnt = rng( 1, type->baby_count );
             if( type->baby_monster ) {
                 here.add_spawn( type->baby_monster, spawn_cnt, pos_bub(), friendly );
+            } else if( type->baby_monster_group ) {
+                std::vector<MonsterGroupResult> babies = MonsterGroupManager::GetResultFromGroup(
+                            type->baby_monster_group, &spawn_cnt,
+                            nullptr, false, nullptr, true );
+                for( const MonsterGroupResult &mgr : babies ) {
+                    here.add_spawn( mgr.name, spawn_cnt * mgr.pack_size, pos_bub(), friendly );
+                }
             } else {
                 const item egg( type->baby_egg, *baby_timer );
                 for( int i = 0; i < spawn_cnt; i++ ) {
diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp
index 9eea7bebce6c8..1304e222d97c3 100644
--- a/src/monstergenerator.cpp
+++ b/src/monstergenerator.cpp
@@ -1190,6 +1190,7 @@ void mtype::load( const JsonObject &jo, const std::string &src )
         }
         optional( repro, was_loaded, "baby_monster", baby_monster, string_id_reader<::mtype> {},
                   mtype_id::NULL_ID() );
+        optional( repro, was_loaded, "baby_monster_group", baby_monster_group, mongroup_id::NULL_ID() );
         optional( repro, was_loaded, "baby_egg", baby_egg, string_id_reader<::itype> {},
                   itype_id::NULL_ID() );
         reproduces = true;
@@ -1762,16 +1763,26 @@ void MonsterGenerator::check_monster_definitions() const
                 debugmsg( "Number of children (%d) is invalid for %s",
                           mon.baby_count, mon.id.c_str() );
             }
-            if( !mon.baby_monster && mon.baby_egg.is_null() ) {
-                debugmsg( "No baby or egg defined for monster %s", mon.id.c_str() );
+            if( !mon.baby_monster && mon.baby_egg.is_null() && !mon.baby_monster_group ) {
+                debugmsg( "No baby, baby group, or egg defined for monster %s", mon.id.c_str() );
             }
             if( mon.baby_monster && !mon.baby_egg.is_null() ) {
                 debugmsg( "Both an egg and a live birth baby are defined for %s", mon.id.c_str() );
             }
+            if( mon.baby_monster_group && !mon.baby_egg.is_null() ) {
+                debugmsg( "Both an egg and a baby group are defined for %s", mon.id.c_str() );
+            }
+            if( mon.baby_monster && mon.baby_monster_group ) {
+                debugmsg( "Both baby and a baby group are defined for %s", mon.id.c_str() );
+            }
             if( !mon.baby_monster.is_valid() ) {
                 debugmsg( "baby_monster %s of monster %s is not a valid monster id",
                           mon.baby_monster.c_str(), mon.id.c_str() );
             }
+            if( !mon.baby_monster_group.is_valid() ) {
+                debugmsg( "baby_monster_group %s of monster %s is not a valid monster group id",
+                          mon.baby_monster.c_str(), mon.id.c_str() );
+            }
             if( !item::type_is_defined( mon.baby_egg ) ) {
                 debugmsg( "item_id %s of monster %s is not a valid item id",
                           mon.baby_egg.c_str(), mon.id.c_str() );
diff --git a/src/mtype.cpp b/src/mtype.cpp
index 13088f2acb1ac..3464bb37ccedb 100644
--- a/src/mtype.cpp
+++ b/src/mtype.cpp
@@ -308,6 +308,7 @@ mtype::mtype()
     reproduces = false;
     baby_count = -1;
     baby_monster = mtype_id::NULL_ID();
+    baby_monster_group = mongroup_id::NULL_ID();
     baby_egg = itype_id::NULL_ID();
 
     biosignatures = false;
diff --git a/src/mtype.h b/src/mtype.h
index 6014204a7dda6..61bb9c1aa70e8 100644
--- a/src/mtype.h
+++ b/src/mtype.h
@@ -312,6 +312,7 @@ struct mtype {
         mtype_id fungalize_into; // mtype_id this monster fungalize into
 
         mtype_id baby_monster;
+        mongroup_id baby_monster_group;
         itype_id baby_egg;
         // Monster biosignature variables
         itype_id biosig_item;