diff --git a/doc/JSON/MONSTERS.md b/doc/JSON/MONSTERS.md index 2a08182b9f7b8..e817b3525a2db 100644 --- a/doc/JSON/MONSTERS.md +++ b/doc/JSON/MONSTERS.md @@ -108,6 +108,7 @@ Property | Description `absorb_material` | (array of string) For monsters with the `ABSORB_ITEMS` special attack. Specifies the types of materials that the monster will seek to absorb. Items with multiple materials will be matched as long as it is made of at least one of the materials in this list. If not specified the monster will absorb all materials. `no_absorb_material` | (array of string) For monsters with the `ABSORB_ITEMS` special attack. Specifies the types of materials that the monster is unable to absorb. This takes precedence over absorb_material; even if the monster is whitelisted for this material, it cannot do so if any of its materials are found here. If not specified, there are no limits placed on what was whitelisted. `split_move_cost` | (int) For monsters with the `SPLIT` special attack. Determines the move cost when splitting into a copy of itself. +`revive_forms` | (array of objects) allows to define conditional monster revival, see explanation below Properties in the above tables are explained in more detail in the sections below. @@ -557,6 +558,24 @@ Field | Description When defined the monster's unpulped corpse will rise, zombifying into the defined (different) monster. For mutated animals (including giant arthropods) the `mon_meat_cocoon` line of monsters should be defined, depending on the monster's weight: No cocoon below 10 kg; 10 - 35 kg monsters zombify into the tiny cocoon; 36 - 100 kg monsters turn into the small cocoon; 101 - 300 kg monsters turn into the medium cocoon; 301+ kg monsters turn into a large cocoon. +## "revive_forms" + +Advanced form of `zombify_into`, allows to specify reviving into different monsters depending on condition; + +```c++ + "revive_forms": [ + { + // condition as dialogue condition, but with neither alpha nor beta talkers + // instead context variables `loc` (location of corpse) and `corpse_damage` are exposed + // can be omitted, in this case condition would be assumed to always be TRUE + "condition": { "map_terrain_with_flag": "SWIMMABLE", "loc": { "context_val": "loc" } }, + // either `monster` or `monster_group` should be used + "monster": "pseudo_debug_mon" + "monster_group": "GROUP_ANIMALPOUND_CATS" + } + ], +``` + ## "baby_flags" (Array, optional) Designate seasons during which this monster is capable of reproduction. ie: `[ "SPRING", "SUMMER" ]` diff --git a/src/game.cpp b/src/game.cpp index fca66979248c9..f10dcbe9d4fca 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5641,6 +5641,9 @@ bool game::revive_corpse( const tripoint_bub_ms &p, item &it, int radius ) // Failed reanimation due to corpse being butchered return false; } + + assing_revive_form( it, p ); + shared_ptr_fast newmon_ptr; if( it.has_var( "zombie_form" ) ) { // the monster was not a zombie but turns into one when its corpse is revived @@ -5667,6 +5670,29 @@ bool game::revive_corpse( const tripoint_bub_ms &p, item &it, int radius ) return place_critter_around( newmon_ptr, tripoint_bub_ms( p ), radius ); } +void game::assing_revive_form( item &it, tripoint_bub_ms p ) +{ + const mtype *montype = it.get_mtype(); + if( montype == nullptr ) { + return; + } + dialogue d( nullptr, nullptr ); + write_var_value( var_type::context, "loc", &d, get_map().get_abs( p ).to_string() ); + write_var_value( var_type::context, "corpse_damage", &d, it.damage() ); + for( const revive_type &rev_type : montype->revive_types ) { + if( rev_type.condition( d ) ) { + if( !rev_type.revive_mon.is_null() ) { + it.set_var( "zombie_form", rev_type.revive_mon.str() ); + return; + } + if( !rev_type.revive_monster_group.is_null() ) { + mtype_id mon = MonsterGroupManager::GetRandomMonsterFromGroup( rev_type.revive_monster_group ); + it.set_var( "zombie_form", mon.str() ); + } + } + } +} + void game::save_cyborg( item *cyborg, const tripoint_bub_ms &couch_pos, Character &installer ) { int damage = cyborg->damage(); diff --git a/src/game.h b/src/game.h index 4e2c7fcec34b5..3498db9e7ac7e 100644 --- a/src/game.h +++ b/src/game.h @@ -522,6 +522,8 @@ class game bool revive_corpse( const tripoint_bub_ms &p, item &it ); // same as above, but with relaxed placement radius. bool revive_corpse( const tripoint_bub_ms &p, item &it, int radius ); + // evaluate what monster it should be, if necessary + void assing_revive_form( item &it, tripoint_bub_ms p ); /**Turns Broken Cyborg monster into Cyborg NPC via surgery*/ void save_cyborg( item *cyborg, const tripoint_bub_ms &couch_pos, Character &installer ); /** Asks if the player wants to cancel their activity, and if so cancels it. */ diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 746112d08014b..6508eb55deb1c 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -995,6 +995,19 @@ void mtype::load( const JsonObject &jo, const std::string &src ) optional( jo_mount_items, was_loaded, "storage", mount_items.storage, itype_id() ); } + if( jo.has_array( "revive_forms" ) ) { + revive_type foo; + for( JsonObject jo_form : jo.get_array( "revive_forms" ) ) { + read_condition( jo_form, "condition", foo.condition, true ); + if( jo_form.has_string( "monster" ) ) { + mandatory( jo_form, was_loaded, "monster", foo.revive_mon ); + } else { + mandatory( jo_form, was_loaded, "monster_group", foo.revive_monster_group ); + } + revive_types.push_back( foo ); + } + } + optional( jo, was_loaded, "zombify_into", zombify_into, string_id_reader<::mtype> {}, mtype_id() ); diff --git a/src/mtype.h b/src/mtype.h index 4dfac2cf25ca8..6650d190de80d 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -288,6 +288,12 @@ struct reproduction_type { item_group_id baby_egg_group = item_group_id::NULL_ID(); }; +struct revive_type { + std::function condition; + mtype_id revive_mon = mtype_id::NULL_ID(); + mongroup_id revive_monster_group = mongroup_id::NULL_ID(); +}; + struct mtype { private: friend class MonsterGenerator; @@ -316,6 +322,8 @@ struct mtype { mongroup_id upgrade_group; mtype_id burn_into; + std::vector revive_types; + mtype_id zombify_into; // mtype_id this monster zombifies into mtype_id fungalize_into; // mtype_id this monster fungalize into