From 04a5d261946aba4905366aedb90a1313d0889a28 Mon Sep 17 00:00:00 2001 From: Qrox Date: Thu, 24 Dec 2020 19:29:20 +0800 Subject: [PATCH] Fused monster transfers killing mission to fuser --- src/map.cpp | 2 +- src/mission.cpp | 46 ++++++++++++++++++++++++++++++++++--------- src/mission.h | 1 + src/monattack.cpp | 5 +++++ src/monster.cpp | 19 +++++++++++++++++- src/monster.h | 4 +++- src/overmap.cpp | 2 +- src/savegame_json.cpp | 10 ++++++++-- 8 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 00b0278aec7bf..386b892945996 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -7564,7 +7564,7 @@ void map::spawn_monsters_submap( const tripoint &gp, bool ignore_sight ) for( int j = 0; j < i.count; j++ ) { monster tmp( i.type ); - tmp.mission_id = i.mission_id; + tmp.mission_ids = { i.mission_id }; if( i.name != "NONE" ) { tmp.unique_name = i.name; } diff --git a/src/mission.cpp b/src/mission.cpp index 23fcd55bc6697..865d13779bf4c 100644 --- a/src/mission.cpp +++ b/src/mission.cpp @@ -142,16 +142,18 @@ void mission::on_creature_death( Creature &poor_dead_dude ) } monster *mon = dynamic_cast( &poor_dead_dude ); if( mon != nullptr ) { - if( mon->mission_id == -1 ) { + if( mon->mission_ids.empty() ) { return; } - mission *found_mission = mission::find( mon->mission_id ); - const mission_type *type = found_mission->type; - if( type->goal == MGOAL_FIND_MONSTER ) { - found_mission->fail(); - } - if( type->goal == MGOAL_KILL_MONSTER ) { - found_mission->step_complete( 1 ); + for( const int mission_id : mon->mission_ids ) { + mission *found_mission = mission::find( mission_id ); + const mission_type *type = found_mission->type; + if( type->goal == MGOAL_FIND_MONSTER ) { + found_mission->fail(); + } + if( type->goal == MGOAL_KILL_MONSTER ) { + found_mission->step_complete( 1 ); + } } return; } @@ -188,6 +190,32 @@ void mission::on_creature_death( Creature &poor_dead_dude ) } } +void mission::on_creature_fusion( Creature &fuser, Creature &fused ) +{ + if( fuser.is_hallucination() || fused.is_hallucination() ) { + return; + } + monster *mon_fuser = dynamic_cast( &fuser ); + if( mon_fuser == nullptr ) { + debugmsg( "Unimplemented: fuser is not a monster" ); + return; + } + monster *mon_fused = dynamic_cast( &fused ); + if( mon_fused == nullptr ) { + debugmsg( "Unimplemented: fused it not a monster" ); + return; + } + for( const int mission_id : mon_fused->mission_ids ) { + const mission *const found_mission = mission::find( mission_id ); + const mission_type *const type = found_mission->type; + if( type->goal == MGOAL_KILL_MONSTER ) { + // the fuser has to be killed now! + mon_fuser->mission_ids.emplace( mission_id ); + mon_fused->mission_ids.erase( mission_id ); + } + } +} + void mission::on_talk_with_npc( const character_id &npc_id ) { switch( type->goal ) { @@ -468,7 +496,7 @@ bool mission::is_complete( const character_id &_npc_id ) const } return g->get_creature_if( [&]( const Creature & critter ) { const monster *const mon_ptr = dynamic_cast( &critter ); - return mon_ptr && mon_ptr->mission_id == uid; + return mon_ptr && mon_ptr->mission_ids.count( uid ); } ); case MGOAL_RECRUIT_NPC: { diff --git a/src/mission.h b/src/mission.h index 6f1de31cac801..76c216c5368ec 100644 --- a/src/mission.h +++ b/src/mission.h @@ -446,6 +446,7 @@ class mission */ /*@{*/ static void on_creature_death( Creature &poor_dead_dude ); + static void on_creature_fusion( Creature &fuser, Creature &fused ); /*@}*/ // Serializes and unserializes all missions diff --git a/src/monattack.cpp b/src/monattack.cpp index e6b86a49aae22..45510a066d65b 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -53,6 +53,7 @@ #include "material.h" #include "memorial_logger.h" #include "messages.h" +#include "mission.h" #include "mondefense.h" #include "monfaction.h" #include "monster.h" @@ -5750,6 +5751,10 @@ bool mattack::zombie_fuse( monster *z ) z->add_effect( effect_grown_of_fuse, 10_days, true, critter->get_hp_max() + z->get_effect( effect_grown_of_fuse ).get_intensity() ); z->heal( critter->get_hp(), true ); + z->mission_fused.emplace_back( critter->name() ); + z->mission_fused.insert( z->mission_fused.end(), + critter->mission_fused.begin(), critter->mission_fused.end() ); + mission::on_creature_fusion( *z, *critter ); critter->death_drops = false; critter->die( z ); return true; diff --git a/src/monster.cpp b/src/monster.cpp index 4070f670c4f63..aa22d213aa810 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -207,7 +207,6 @@ monster::monster() anger = 0; morale = 2; faction = mfaction_id( 0 ); - mission_id = -1; no_extra_death_drops = false; dead = false; death_drops = true; @@ -673,6 +672,17 @@ int monster::print_info( const catacurses::window &w, int vStart, int vLines, in mvwprintz( w, point( column, ++vStart ), c_light_gray, lines[i] ); } + if( !mission_fused.empty() ) { + // Mission monsters fused into this monster + const std::string fused_desc = string_format( _( "Parts of %s extrude from its body." ), + enumerate_as_string( mission_fused ) ); + lines = foldstring( fused_desc, max_width ); + numlines = lines.size(); + for( int i = 0; i < numlines && vStart < vEnd; i++ ) { + mvwprintz( w, point( column, ++vStart ), c_light_gray, lines[i] ); + } + } + // Riding indicator on next line after description. if( has_effect( effect_ridden ) && mounted_player ) { mvwprintz( w, point( column, ++vStart ), c_white, _( "Rider: %s" ), mounted_player->disp_name() ); @@ -732,6 +742,13 @@ std::string monster::extended_description() const ss += "--\n"; ss += string_format( "%s", type->get_description() ) + "\n"; ss += "--\n"; + if( !mission_fused.empty() ) { + // Mission monsters fused into this monster + const std::string fused_desc = string_format( _( "Parts of %s extrude from its body." ), + enumerate_as_string( mission_fused ) ); + ss += string_format( "%s", fused_desc ) + "\n"; + ss += "--\n"; + } ss += string_format( _( "It is %s in size." ), size_names.at( get_size() ) ) + "\n"; diff --git a/src/monster.h b/src/monster.h index f964438b0af2e..459c41b2eec05 100644 --- a/src/monster.h +++ b/src/monster.h @@ -469,7 +469,9 @@ class monster : public Creature // Our faction (species, for most monsters) mfaction_id faction; // If we're related to a mission - int mission_id = 0; + std::set mission_ids; + // Names of mission monsters fused with this monster + std::vector mission_fused; const mtype *type; // If true, don't spawn loot items as part of death. bool no_extra_death_drops = false; diff --git a/src/overmap.cpp b/src/overmap.cpp index 388f6416595be..549ddd759d704 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -2047,7 +2047,7 @@ void overmap::move_hordes() type.id == mtype_id( "mon_jabberwock" ) || // Jabberwockies are an exception. this_monster.get_speed() <= 30 || // So are very slow zombies, like crawling zombies. !this_monster.will_join_horde( INT_MAX ) || // So are zombies who won't join a horde of any size. - this_monster.mission_id != -1 // We mustn't delete monsters that are related to missions. + !this_monster.mission_ids.empty() // We mustn't delete monsters that are related to missions. ) { // Don't delete the monster, just increment the iterator. monster_map_it++; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index f7ed49110e8ee..cdd18394d6aca 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2135,7 +2135,12 @@ void monster::load( const JsonObject &data ) } data.read( "friendly", friendly ); - data.read( "mission_id", mission_id ); + if( data.has_member( "mission_ids" ) ) { + data.read( "mission_ids", mission_ids ); + } else if( data.has_member( "mission_id" ) ) { + mission_ids = { data.get_int( "mission_id" ) }; + } + data.read( "mission_fused", mission_fused ); data.read( "no_extra_death_drops", no_extra_death_drops ); data.read( "dead", dead ); data.read( "anger", anger ); @@ -2211,7 +2216,8 @@ void monster::store( JsonOut &json ) const json.member( "friendly", friendly ); json.member( "fish_population", fish_population ); json.member( "faction", faction.id().str() ); - json.member( "mission_id", mission_id ); + json.member( "mission_ids", mission_ids ); + json.member( "mission_fused", mission_fused ); json.member( "no_extra_death_drops", no_extra_death_drops ); json.member( "dead", dead ); json.member( "anger", anger );