diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 49d225e6d..4c2d6b59e 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5133,6 +5133,16 @@ CHANGE COLUMN `slot` `inventory_slot` mediumint(9) NOT NULL DEFAULT -1 AFTER `st ALTER TABLE `starting_items` CHANGE COLUMN `temporary` `class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `id`; +)" + }, + ManifestEntry{ + .version = 9248, + .description = "2023_12_22_drop_npc_emotes_index.sql", + .check = "show index from npc_emotes where key_name = 'emoteid'", + .condition = "not_empty", + .match = "", + .sql = R"( +ALTER TABLE `npc_emotes` DROP INDEX `emoteid`; )" } diff --git a/common/repositories/base/base_npc_emotes_repository.h b/common/repositories/base/base_npc_emotes_repository.h index 6e2e5b37d..438ffbdf4 100644 --- a/common/repositories/base/base_npc_emotes_repository.h +++ b/common/repositories/base/base_npc_emotes_repository.h @@ -16,6 +16,7 @@ #include "../../strings.h" #include + class BaseNpcEmotesRepository { public: struct NpcEmotes { @@ -120,8 +121,9 @@ public: { auto results = db.QueryDatabase( fmt::format( - "{} WHERE id = {} LIMIT 1", + "{} WHERE {} = {} LIMIT 1", BaseSelect(), + PrimaryKey(), npc_emotes_id ) ); diff --git a/common/version.h b/common/version.h index 86edd720b..a7dcf1542 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9247 +#define CURRENT_BINARY_DATABASE_VERSION 9248 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9041 diff --git a/zone/attack.cpp b/zone/attack.cpp index eacea41db..58b9c974d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1820,7 +1820,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill auto emote_id = killerMob->GetEmoteID(); if (emote_id) { - killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid); + killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); } killerMob->TrySpellOnKill(killed_level, spell); @@ -2710,7 +2710,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy if (IsNPC()) { auto emote_id = GetEmoteID(); if (emote_id) { - DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid); + DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob); } } @@ -2721,7 +2721,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy auto emote_id = oos->GetEmoteID(); if (emote_id) { - oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id); + oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this); } if (killer_mob) { killer_mob->TrySpellOnKill(killed_level, spell); @@ -2799,7 +2799,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy ApplyIllusionToCorpse(illusion_spell_id, corpse); if (killer != 0 && emoteid != 0) - corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid); + corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid, killer); if (killer != 0 && killer->IsClient()) { corpse->AllowPlayerLoot(killer, 0); if (killer->IsGrouped()) { diff --git a/zone/client.cpp b/zone/client.cpp index 353319628..bc5cf904f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6172,7 +6172,7 @@ void Client::CheckEmoteHail(NPC* n, const char* message) const auto emote_id = n->GetEmoteID(); if (emote_id) { - n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id); + n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id, this); } } diff --git a/zone/gm_commands/find/emote.cpp b/zone/gm_commands/find/emote.cpp index c6926caf8..a8f1c433c 100644 --- a/zone/gm_commands/find/emote.cpp +++ b/zone/gm_commands/find/emote.cpp @@ -7,10 +7,7 @@ void FindEmote(Client *c, const Seperator *sep) if (sep->IsNumber(2)) { auto emote_id = Strings::ToUnsignedInt(sep->arg[2]); - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while (iterator.MoreElements()) { - auto &e = iterator.GetData(); + for (auto& e : zone->npc_emote_list) { if (emote_id == e->emoteid) { c->Message( Chat::White, @@ -40,7 +37,6 @@ void FindEmote(Client *c, const Seperator *sep) break; } - iterator.Advance(); } if (found_count == 50) { @@ -70,10 +66,7 @@ void FindEmote(Client *c, const Seperator *sep) const std::string& search_criteria = sep->argplus[2]; - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while (iterator.MoreElements()) { - auto &e = iterator.GetData(); + for (auto& e : zone->npc_emote_list) { const std::string& current_text = Strings::ToLower(e->text); @@ -106,7 +99,6 @@ void FindEmote(Client *c, const Seperator *sep) break; } - iterator.Advance(); } if (found_count == 50) { diff --git a/zone/gm_commands/show/emotes.cpp b/zone/gm_commands/show/emotes.cpp index 9e7e58e74..38d9c16ce 100644 --- a/zone/gm_commands/show/emotes.cpp +++ b/zone/gm_commands/show/emotes.cpp @@ -12,10 +12,7 @@ void ShowEmotes(Client *c, const Seperator *sep) uint32 emote_count = 0; const uint32 emote_id = t->GetEmoteID(); - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while (iterator.MoreElements()) { - const auto& e = iterator.GetData(); + for (auto& e : zone->npc_emote_list) { if (emote_id == e->emoteid) { c->Message( Chat::White, @@ -41,7 +38,6 @@ void ShowEmotes(Client *c, const Seperator *sep) emote_count++; } - iterator.Advance(); } c->Message( diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index b09e617b5..99ac0257d 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1800,7 +1800,7 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) auto emote_id = GetEmoteID(); if (emote_id) { - CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid); + CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid, attacker); } std::string mob_name = GetCleanName(); diff --git a/zone/npc.cpp b/zone/npc.cpp index aec368a21..e4e5d5555 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3046,42 +3046,72 @@ void NPC::SendPayload(int payload_id, std::string payload_value) } NPC_Emote_Struct* NPC::GetNPCEmote(uint32 emoteid, uint8 event_) { - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); + std::vector emotes; + for (auto &e: zone->npc_emote_list) { + NPC_Emote_Struct *nes = e; if (emoteid == nes->emoteid && event_ == nes->event_) { - return nes; + emotes.push_back(e); } - iterator.Advance(); } - return nullptr; + + if (emotes.empty()) { + return nullptr; + } + else if (emotes.size() == 1) { + return emotes[0]; + } + + int index = zone->random.Roll0(emotes.size()); + + return emotes[index]; } -void NPC::DoNPCEmote(uint8 event_, uint32 emoteid) +void NPC::DoNPCEmote(uint8 event_, uint32 emoteid, Mob* target) { - if (emoteid == 0) - { + if (emoteid == 0) { return; } - NPC_Emote_Struct* nes = GetNPCEmote(emoteid,event_); - if(nes == nullptr) - { + NPC_Emote_Struct *nes = GetNPCEmote(emoteid, event_); + if (nes == nullptr) { return; } - if(emoteid == nes->emoteid) - { - if(nes->type == 1) - Emote("%s",nes->text); - else if(nes->type == 2) - Shout("%s",nes->text); - else if(nes->type == 3) - entity_list.MessageCloseString(this, true, 200, 10, GENERIC_STRING, nes->text); - else - Say("%s",nes->text); + std::string processed = nes->text; + Strings::FindReplace(processed, "$mname", GetCleanName()); + Strings::FindReplace(processed, "$mracep", GetRacePlural() = GetClass()); + Strings::FindReplace(processed, "$mrace", GetPlayerRaceName(GetRace())); + Strings::FindReplace(processed, "$mclass", GetClassIDName(GetClass())); + if (target) { + Strings::FindReplace(processed, "$name", target->GetCleanName()); + Strings::FindReplace(processed, "$racep", GetRacePlural() = target->GetClass()); + Strings::FindReplace(processed, "$race", GetPlayerRaceName(target->GetRace())); + Strings::FindReplace(processed, "$class", GetClassIDName(target->GetClass())); + } + else { + Strings::FindReplace(processed, "$name", "foe"); + Strings::FindReplace(processed, "$race", "race"); + Strings::FindReplace(processed, "$racep", "races"); + Strings::FindReplace(processed, "$class", "class"); + } + + if (emoteid == nes->emoteid) { + if (event_ == EQ::constants::EmoteEventTypes::Hailed && target) { + DoQuestPause(target); + } + + if (nes->type == 1) { + Emote("%s", processed.c_str()); + } + else if (nes->type == 2) { + Shout("%s", processed.c_str()); + } + else if (nes->type == 3) { + entity_list.MessageCloseString(this, true, 200, 10, GENERIC_STRING, processed.c_str()); + } + else { + Say("%s", processed.c_str()); + } } } diff --git a/zone/npc.h b/zone/npc.h index 86d486f4b..77213e8d5 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -476,7 +476,7 @@ public: const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; } NPC_Emote_Struct* GetNPCEmote(uint32 emoteid, uint8 event_); - void DoNPCEmote(uint8 event_, uint32 emoteid); + void DoNPCEmote(uint8 event_, uint32 emoteid, Mob* target = nullptr); bool CanTalk(); void DoQuestPause(Mob *other); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 8c81abfdb..fef8b6b06 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2015,7 +2015,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (zone && zone->IsLoaded()) { zone->SendReloadMessage("NPC Emotes"); - zone->LoadNPCEmotes(&zone->NPCEmoteList); + zone->LoadNPCEmotes(&zone->npc_emote_list); } break; } diff --git a/zone/zone.cpp b/zone/zone.cpp index 6b6a985e5..6acb3c6b0 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1056,7 +1056,7 @@ Zone::~Zone() { safe_delete_array(short_name); safe_delete_array(long_name); safe_delete(Weather_Timer); - NPCEmoteList.Clear(); + npc_emote_list.clear(); zone_point_list.Clear(); entity_list.Clear(); ClearBlockedSpells(); @@ -1152,7 +1152,7 @@ bool Zone::Init(bool is_static) { LoadLDoNTrapEntries(); LoadVeteranRewards(); LoadAlternateCurrencies(); - LoadNPCEmotes(&NPCEmoteList); + LoadNPCEmotes(&npc_emote_list); LoadAlternateAdvancement(); @@ -1233,8 +1233,8 @@ void Zone::ReloadStaticData() { LoadVeteranRewards(); LoadAlternateCurrencies(); - NPCEmoteList.Clear(); - LoadNPCEmotes(&NPCEmoteList); + npc_emote_list.clear(); + LoadNPCEmotes(&npc_emote_list); //load the zone config file. if (!LoadZoneCFG(GetShortName(), GetInstanceVersion())) { // try loading the zone name... @@ -2548,10 +2548,10 @@ void Zone::DoAdventureActions() } -void Zone::LoadNPCEmotes(LinkedList* NPCEmoteList) +void Zone::LoadNPCEmotes(std::vector* NPCEmoteList) { - NPCEmoteList->Clear(); + NPCEmoteList->clear(); const std::string query = "SELECT emoteid, event_, type, text FROM npc_emotes"; auto results = content_db.QueryDatabase(query); if (!results.Success()) { @@ -2565,7 +2565,7 @@ void Zone::LoadNPCEmotes(LinkedList* NPCEmoteList) nes->event_ = Strings::ToInt(row[1]); nes->type = Strings::ToInt(row[2]); strn0cpy(nes->text, row[3], sizeof(nes->text)); - NPCEmoteList->Insert(nes); + NPCEmoteList->push_back(nes); } LogInfo("Loaded [{}] npc emotes", Strings::Commify(results.RowCount())); diff --git a/zone/zone.h b/zone/zone.h index fb64ed074..b8ac44203 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -185,7 +185,7 @@ public: DynamicZone *GetDynamicZone(); IPathfinder *pathing; - LinkedList NPCEmoteList; + std::vector npc_emote_list; LinkedList spawn2_list; LinkedList zone_point_list; std::vector virtual_zone_point_list; @@ -282,7 +282,7 @@ public: void LoadMercSpells(); void LoadMercTemplates(); void LoadNewMerchantData(uint32 merchantid); - void LoadNPCEmotes(LinkedList *NPCEmoteList); + void LoadNPCEmotes(std::vector *NPCEmoteList); void LoadTempMerchantData(); void LoadTickItems(); void LoadVeteranRewards();