From ef602d98cb1e0cfdf654cfe2fb135ffc5d1fb109 Mon Sep 17 00:00:00 2001 From: Atemo Date: Fri, 16 Dec 2022 21:22:31 +0100 Subject: [PATCH] Multiple dynamic npc (#7486) * Vectorize npc_id_dynamic to support multiple dynamic npc with different ids Co-authored-by: Lemongrass3110 Thanks to @dimasshotta and @eppc0330 ! --- src/map/npc.cpp | 20 ++++++++++++++------ src/map/pc.hpp | 2 +- src/map/unit.cpp | 21 +++++++++++++-------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/map/npc.cpp b/src/map/npc.cpp index d2bfcfe816..4bc1bded2a 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -3528,7 +3528,7 @@ int npc_unload(struct npc_data* nd, bool single) { map_session_data* owner = map_charid2sd( nd->dynamicnpc.owner_char_id ); if( owner != nullptr ){ - owner->npc_id_dynamic = 0; + util::vector_erase_if_exists(owner->npc_id_dynamic, nd->bl.id); } } @@ -4515,7 +4515,7 @@ const char* npc_parse_duplicate( char* w1, char* w2, char* w3, char* w4, const c if( owner != nullptr ){ nd->dynamicnpc.owner_char_id = owner->status.char_id; - owner->npc_id_dynamic = nd->bl.id; + owner->npc_id_dynamic.push_back(nd->bl.id); } switch( type ) { @@ -5796,7 +5796,7 @@ TIMER_FUNC(npc_dynamicnpc_removal_timer){ return 0; } - sd->npc_id_dynamic = 0; + // npc id from sd->npc_id_dynamic is removed in npc_unload } // Delete the NPC @@ -5808,9 +5808,17 @@ TIMER_FUNC(npc_dynamicnpc_removal_timer){ } struct npc_data* npc_duplicate_npc_for_player( struct npc_data& nd, map_session_data& sd ){ - if( sd.npc_id_dynamic != 0 ){ - clif_msg_color( &sd, C_DYNAMICNPC_TWICE, color_table[COLOR_LIGHT_YELLOW] ); - return nullptr; + // A duplicate of a duplicate is still a duplicate of the same NPC + int src_id = nd.src_id > 0 ? nd.src_id : nd.bl.id; + + for (const auto &it : sd.npc_id_dynamic) { + struct npc_data* src_nd = map_id2nd( it ); + + // Check if the source NPC id of currently active duplicates already exists. + if( src_nd != nullptr && src_nd->src_id == src_id ){ + clif_msg_color( &sd, C_DYNAMICNPC_TWICE, color_table[COLOR_LIGHT_YELLOW] ); + return nullptr; + } } if( map_getmapflag( sd.bl.m, MF_NODYNAMICNPC ) ){ diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 801105c087..c62b5dc0ad 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -492,7 +492,7 @@ public: unsigned char head_dir; //0: Look forward. 1: Look right, 2: Look left. t_tick client_tick; int npc_id,npc_shopid; //for script follow scriptoid; ,npcid - int npc_id_dynamic; + std::vector npc_id_dynamic; std::vector areanpc, npc_ontouch_; ///< Array of OnTouch and OnTouch_ NPC ID int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse) int npc_menu; // internal variable, used in npc menu handling diff --git a/src/map/unit.cpp b/src/map/unit.cpp index 873f55e196..cd7e57a8ab 100644 --- a/src/map/unit.cpp +++ b/src/map/unit.cpp @@ -3423,17 +3423,22 @@ int unit_free(struct block_list *bl, clr_type clrtype) sd->npc_id = 0; } - if( sd->npc_id_dynamic != 0 ){ - struct npc_data* nd = map_id2nd( sd->npc_id_dynamic ); + if( !sd->npc_id_dynamic.empty() ){ + for (const auto &it : sd->npc_id_dynamic) { + struct npc_data* nd = map_id2nd( it ); - if( nd != nullptr ){ - // Delete the NPC - npc_unload( nd, true ); - // Update NPC event database - npc_read_event_script(); + if( nd != nullptr ){ + // Erase the owner first to prevent loops from npc_unload + nd->dynamicnpc.owner_char_id = 0; + + // Delete the NPC + npc_unload( nd, true ); + } } + // Update NPC event database + npc_read_event_script(); - sd->npc_id_dynamic = 0; + sd->npc_id_dynamic.clear(); } sd->combos.clear();