From 0c105a2b914a2ec37670cf858a36f1efdab7678d Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 8 Jan 2023 11:27:17 -0500 Subject: [PATCH] [Bug Fix] Fix NPC Reference in EVENT_SPAWN (#2712) * [Bug Fix] Fix NPC Reference in EVENT_SPAWN # Notes - Event parsing was too early and memory wasn't fully allocated, meaning that sometimes you could use the NPC reference and other times you couldn't. - Most people worked around this by setting timers in `EVENT_SPAWN` then using `EVENT_TIMER` to handle the stuff they wanted to do on spawn. # Example Code in Perl ```pl sub EVENT_SPAWN { quest::shout($npc->GetCleanName() . " just spawned with an ID of " . $npc->GetID() . " and an NPC Type ID of " . $npc->GetNPCTypeID() . "."); }``` * Update bot.cpp --- zone/bot.cpp | 8 ++++---- zone/entity.cpp | 37 +++++++++++++++++++++---------------- zone/entity.h | 2 +- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2b16103f8..24e6865f1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8784,14 +8784,14 @@ void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) { AddToSpawnQueue(new_bot->GetID(), &ns); safe_delete(ns); } - - parse->EventBot(EVENT_SPAWN, new_bot, nullptr, "", 0); - - new_bot->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, new_bot, "", 0, nullptr); } bot_list.push_back(new_bot); mob_list.insert(std::pair(new_bot->GetID(), new_bot)); + + parse->EventBot(EVENT_SPAWN, new_bot, nullptr, "", 0); + + new_bot->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, new_bot, "", 0, nullptr); } } diff --git a/zone/entity.cpp b/zone/entity.cpp index 450fd3d0f..483a10f0f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -685,56 +685,61 @@ void EntityList::AddCorpse(Corpse *corpse, uint32 in_id) corpse_timer.Start(); } -void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) +void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue) { npc->SetID(GetFreeID()); //If this is not set here we will despawn pets from new AC changes auto owner_id = npc->GetOwnerID(); - if(owner_id) { + if (owner_id) { auto owner = entity_list.GetMob(owner_id); if (owner) { owner->SetPetID(npc->GetID()); } } - parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); - uint32 emoteid = npc->GetEmoteID(); - if (emoteid != 0) - npc->DoNPCEmote(EQ::constants::EmoteEventTypes::OnSpawn, emoteid); + const auto emote_id = npc->GetEmoteID(); + if (emote_id != 0) { + npc->DoNPCEmote(EQ::constants::EmoteEventTypes::OnSpawn, emote_id); + } + npc->SetSpawned(); - if (SendSpawnPacket) { - if (dontqueue) { // aka, SEND IT NOW BITCH! + + if (send_spawn_packet) { + if (dont_queue) { auto app = new EQApplicationPacket; npc->CreateSpawnPacket(app, npc); QueueClients(npc, app); npc->SendArmorAppearance(); - npc->SetAppearance(npc->GetGuardPointAnim(),false); - if (!npc->IsTargetable()) + npc->SetAppearance(npc->GetGuardPointAnim(), false); + + if (!npc->IsTargetable()) { npc->SendTargetable(false); + } + safe_delete(app); } else { auto ns = new NewSpawn_Struct; memset(ns, 0, sizeof(NewSpawn_Struct)); - npc->FillSpawnStruct(ns, nullptr); // Not working on player newspawns, so it's safe to use a ForWho of 0 + npc->FillSpawnStruct(ns, nullptr); AddToSpawnQueue(npc->GetID(), &ns); safe_delete(ns); } - if (npc->IsFindable()) + + if (npc->IsFindable()) { UpdateFindableNPCState(npc, false); + } } npc_list.insert(std::pair(npc->GetID(), npc)); mob_list.insert(std::pair(npc->GetID(), npc)); + parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); + entity_list.ScanCloseMobs(npc->close_mobs, npc, true); - /* Zone controller process EVENT_SPAWN_ZONE */ npc->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, npc, "", 0, nullptr); - /** - * Set whether NPC was spawned in or out of water - */ if (zone->HasMap() && zone->HasWaterMap()) { npc->SetSpawnedInWater(false); if (zone->watermap->InLiquid(npc->GetPosition())) { diff --git a/zone/entity.h b/zone/entity.h index e2c24b27a..b173861a5 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -278,7 +278,7 @@ public: bool MakeTrackPacket(Client* client); void SendTraders(Client* client); void AddClient(Client*); - void AddNPC(NPC*, bool SendSpawnPacket = true, bool dontqueue = false); + void AddNPC(NPC*, bool send_spawn_packet = true, bool dont_queue = false); void AddMerc(Merc*, bool SendSpawnPacket = true, bool dontqueue = false); void AddCorpse(Corpse* pc, uint32 in_id = 0xFFFFFFFF); void AddObject(Object*, bool SendSpawnPacket = true);