diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 34ad34b1c..1a63d934a 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -562,6 +562,7 @@ std::string EQ::constants::GetEmoteEventTypeName(uint8 emote_event_type) const std::map &EQ::constants::GetEmoteTypeMap() { static const std::map emote_type_map = { + { EmoteTypes::Say, "Say" }, { EmoteTypes::Emote, "Emote" }, { EmoteTypes::Shout, "Shout" }, { EmoteTypes::Proximity, "Proximity" } @@ -572,7 +573,7 @@ const std::map &EQ::constants::GetEmoteTypeMap() std::string EQ::constants::GetEmoteTypeName(uint8 emote_type) { - if (!EQ::ValueWithin(emote_type, EmoteTypes::Emote, EmoteTypes::Proximity)) { + if (!EQ::ValueWithin(emote_type, EmoteTypes::Say, EmoteTypes::Proximity)) { return std::string(); } diff --git a/common/emu_constants.h b/common/emu_constants.h index 40bec0b74..f886b0d77 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -350,6 +350,7 @@ namespace EQ }; enum EmoteTypes : uint8 { + Say, Emote, Shout, Proximity diff --git a/zone/attack.cpp b/zone/attack.cpp index f40e7f396..1dd800a43 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1818,9 +1818,9 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); } - auto emote_id = killerMob->GetEmoteID(); + const uint32 emote_id = killerMob->GetEmoteID(); if (emote_id) { - killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); + killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emote_id, this); } killerMob->TrySpellOnKill(killed_level, spell); @@ -2725,7 +2725,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy entity_list.RemoveFromAutoXTargets(this); - uint32 emoteid = GetEmoteID(); + const uint32 emote_id = GetEmoteID(); corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata, level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTimeMS) : RuleI(NPC, MinorNPCCorpseDecayTimeMS)); @@ -2740,8 +2740,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy SetID(0); ApplyIllusionToCorpse(illusion_spell_id, corpse); - if (killer != 0 && emoteid != 0) - corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid, killer); + if (killer != 0 && emote_id) + corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emote_id, killer); if (killer != 0 && killer->IsClient()) { corpse->AllowPlayerLoot(killer, 0); if (killer->IsGrouped()) { @@ -2823,9 +2823,9 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy // Parse quests even if we're killed by an NPC if (oos) { if (IsNPC()) { - auto emote_id = GetEmoteID(); + const uint32 emote_id = GetEmoteID(); if (emote_id) { - DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob); + DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emote_id, killer_mob); } } @@ -2834,10 +2834,11 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); } - auto emote_id = oos->GetEmoteID(); + const uint32 emote_id = oos->GetEmoteID(); if (emote_id) { oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this); } + if (killer_mob) { killer_mob->TrySpellOnKill(killed_level, spell); } @@ -4308,7 +4309,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons } else { a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC } - + if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) { m_Delta.x += a->force * g_Math.FastSin(a->hit_heading); m_Delta.y += a->force * g_Math.FastCos(a->hit_heading); @@ -5314,8 +5315,8 @@ bool Mob::TryFinishingBlow(Mob *defender, int64 &damage) (RuleB(Combat, FinishingBlowOnlyWhenFleeing) && !defender->currently_fleeing) || !RuleB(Combat, FinishingBlowOnlyWhenFleeing) ) && - finishing_blow_level && - finishing_blow_damage && + finishing_blow_level && + finishing_blow_damage && defender->GetLevel() <= finishing_blow_level && proc_chance >= zone->random.Int(1, 1000) ) { @@ -6406,7 +6407,7 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) if (flurry_chance && zone->random.Roll(flurry_chance)) { Attack(target, hand, false, false, IsFromSpell); - + if (zone->random.Roll(flurry_chance)) { Attack(target, hand, false, false, IsFromSpell); } diff --git a/zone/client.cpp b/zone/client.cpp index e0ca92977..ee30ca17c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6203,10 +6203,7 @@ void Client::CheckLDoNHail(NPC* n) void Client::CheckEmoteHail(NPC* n, const char* message) { - if ( - !Strings::BeginsWith(message, "hail") && - !Strings::BeginsWith(message, "Hail") - ) { + if (!Strings::BeginsWith(Strings::ToLower(message), "hail")) { return; } @@ -6214,7 +6211,7 @@ void Client::CheckEmoteHail(NPC* n, const char* message) return; } - const auto emote_id = n->GetEmoteID(); + const uint32 emote_id = n->GetEmoteID(); if (emote_id) { n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id, this); } diff --git a/zone/entity.cpp b/zone/entity.cpp index 8f0e7d253..69d9dc000 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -680,8 +680,8 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue) parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); } - const auto emote_id = npc->GetEmoteID(); - if (emote_id != 0) { + const uint32 emote_id = npc->GetEmoteID(); + if (emote_id) { npc->DoNPCEmote(EQ::constants::EmoteEventTypes::OnSpawn, emote_id); } diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp index dcb338581..e462523aa 100755 --- a/zone/gm_commands/npcedit.cpp +++ b/zone/gm_commands/npcedit.cpp @@ -1092,6 +1092,22 @@ void command_npcedit(Client *c, const Seperator *sep) ); return; } + } else if (!strcasecmp(sep->arg[1], "emoteid")) { + if (sep->IsNumber(2)) { + const uint32 emote_id = Strings::ToUnsignedInt(sep->arg[2]); + n.emoteid = emote_id; + d = fmt::format( + "{} now has an Emote ID of {}.", + npc_id_string, + Strings::Commify(emote_id) + ); + } else { + c->Message( + Chat::White, + "Usage: #npcedit emoteid [Emote ID] - Sets an NPC's Emote ID" + ); + return; + } } else if (!strcasecmp(sep->arg[1], "spellscale")) { if (sep->IsNumber(2)) { auto spell_scale = Strings::ToUnsignedInt(sep->arg[2]); @@ -1720,6 +1736,7 @@ void SendNPCEditSubCommands(Client *c) c->Message(Chat::White, "Usage: #npcedit version [Version] - Sets an NPC's Version"); c->Message(Chat::White, "Usage: #npcedit maxlevel [Max Level] - Sets an NPC's Maximum Level"); c->Message(Chat::White, "Usage: #npcedit scalerate [Scale Rate] - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message(Chat::White, "Usage: #npcedit emoteid [Emote ID] - Sets an NPC's Emote ID"); c->Message(Chat::White, "Usage: #npcedit spellscale [Scale Rate] - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); c->Message(Chat::White, "Usage: #npcedit healscale [Scale Rate] - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); c->Message(Chat::White, "Usage: #npcedit no_target [Flag] - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]"); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index ff48dffe9..af7d48768 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1800,9 +1800,9 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); } - auto emote_id = GetEmoteID(); + const uint32 emote_id = GetEmoteID(); if (emote_id) { - CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid, attacker); + CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emote_id, attacker); } std::string mob_name = GetCleanName(); @@ -1843,14 +1843,13 @@ void Mob::AI_Event_NoLongerEngaged() { SetAssistAggro(false); if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { if (entity_list.GetNPCByID(GetID())) { - auto emote_id = CastToNPC()->GetEmoteID(); - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) { parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); } + const uint32 emote_id = CastToNPC()->GetEmoteID(); if (emote_id) { - CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emoteid); + CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id); } m_combat_record.Stop(); @@ -2120,7 +2119,7 @@ bool Mob::Rampage(ExtraAttackOptions *opts) if (m_target == GetTarget()) { continue; } - + if (m_target->IsCorpse()) { LogAggroDetail("[{}] is on [{}]'s rampage list", m_target->GetCleanName(), GetCleanName()); RemoveFromRampageList(m_target, true); diff --git a/zone/npc.cpp b/zone/npc.cpp index 436f9773a..d23ce670a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1113,9 +1113,9 @@ void NPC::UpdateEquipmentLight() } void NPC::Depop(bool start_spawn_timer) { - const auto emote_id = GetEmoteID(); + const uint32 emote_id = GetEmoteID(); if (emote_id) { - DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid); + DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emote_id); } if (IsNPC()) { @@ -3070,72 +3070,75 @@ void NPC::SendPayload(int payload_id, std::string payload_value) } } -NPC_Emote_Struct* NPC::GetNPCEmote(uint32 emoteid, uint8 event_) { - std::vector emotes; - for (auto &e: zone->npc_emote_list) { - NPC_Emote_Struct *nes = e; - if (emoteid == nes->emoteid && event_ == nes->event_) { - emotes.push_back(e); +NPC_Emote_Struct* NPC::GetNPCEmote(uint32 emote_id, uint8 event_) { + std::vector emotes; + + for (const auto &e : zone->npc_emote_list) { + auto nes = e; + + if (nes->emoteid == emote_id && nes->event_ == event_) { + emotes.emplace_back(e); } } if (emotes.empty()) { return nullptr; - } - else if (emotes.size() == 1) { - return emotes[0]; + } else if (emotes.size() == 1) { + return emotes.front(); } - int index = zone->random.Roll0(emotes.size()); + const int index = zone->random.Roll0(emotes.size()); return emotes[index]; } -void NPC::DoNPCEmote(uint8 event_, uint32 emoteid, Mob* target) +void NPC::DoNPCEmote(uint8 event_, uint32 emote_id, Mob* t) { - if (emoteid == 0) { + if (!emote_id) { return; } - NPC_Emote_Struct *nes = GetNPCEmote(emoteid, event_); - if (nes == nullptr) { + auto e = GetNPCEmote(emote_id, event_); + if (!e) { return; } - std::string processed = nes->text; + std::string processed = e->text; + + // Mob Variables Strings::FindReplace(processed, "$mname", GetCleanName()); - Strings::FindReplace(processed, "$mracep", GetRacePlural() = GetClass()); + Strings::FindReplace(processed, "$mracep", GetRacePlural()); 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"); - } + Strings::FindReplace(processed, "$mclassp", GetClassPlural()); - if (emoteid == nes->emoteid) { - if (event_ == EQ::constants::EmoteEventTypes::Hailed && target) { - DoQuestPause(target); + // Target Variables + Strings::FindReplace(processed, "$name", t ? t->GetCleanName() : "foe"); + Strings::FindReplace(processed, "$class", t ? GetClassIDName(t->GetClass()) : "class"); + Strings::FindReplace(processed, "$classp", t ? t->GetClassPlural() : "classes"); + Strings::FindReplace(processed, "$race", t ? GetPlayerRaceName(t->GetRace()) : "race"); + Strings::FindReplace(processed, "$racep", t ? t->GetRacePlural() : "races"); + + if (emoteid == e->emoteid) { + if (event_ == EQ::constants::EmoteEventTypes::Hailed && t) { + DoQuestPause(t); } - 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()); + if (e->type == EQ::constants::EmoteTypes::Say) { + Say(processed.c_str()); + } else if (e->type == EQ::constants::EmoteTypes::Emote) { + Emote(processed.c_str()); + } else if (e->type == EQ::constants::EmoteTypes::Shout) { + Shout(processed.c_str()); + } else if (e->type == EQ::constants::EmoteTypes::Proximity) { + entity_list.MessageCloseString( + this, + true, + 200, + Chat::NPCQuestSay, + GENERIC_STRING, + processed.c_str() + ); } } } diff --git a/zone/npc.h b/zone/npc.h index ccf50fa66..4ee0c5ad0 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -475,8 +475,8 @@ public: Timer *GetRefaceTimer() const { return reface_timer; } const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; } - NPC_Emote_Struct* GetNPCEmote(uint32 emoteid, uint8 event_); - void DoNPCEmote(uint8 event_, uint32 emoteid, Mob* target = nullptr); + NPC_Emote_Struct* GetNPCEmote(uint32 emote_id, uint8 event_); + void DoNPCEmote(uint8 event_, uint32 emote_id, Mob* t = nullptr); bool CanTalk(); void DoQuestPause(Mob *other); diff --git a/zone/zone.cpp b/zone/zone.cpp index fcc91f882..9a6b2a099 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -62,6 +62,7 @@ #include "../common/repositories/ldon_trap_entries_repository.h" #include "../common/repositories/ldon_trap_templates_repository.h" #include "../common/repositories/respawn_times_repository.h" +#include "../common/repositories/npc_emotes_repository.h" #include "../common/serverinfo.h" #include @@ -2570,27 +2571,29 @@ void Zone::DoAdventureActions() } -void Zone::LoadNPCEmotes(std::vector* NPCEmoteList) +void Zone::LoadNPCEmotes(std::vector* v) { + v->clear(); - NPCEmoteList->clear(); - const std::string query = "SELECT emoteid, event_, type, text FROM npc_emotes"; - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - return; - } + const auto& l = NpcEmotesRepository::All(content_db); - for (auto row = results.begin(); row != results.end(); ++row) - { - auto nes = new NPC_Emote_Struct; - nes->emoteid = Strings::ToInt(row[0]); - nes->event_ = Strings::ToInt(row[1]); - nes->type = Strings::ToInt(row[2]); - strn0cpy(nes->text, row[3], sizeof(nes->text)); - NPCEmoteList->push_back(nes); - } + for (const auto& e : l) { + auto n = new NPC_Emote_Struct; - LogInfo("Loaded [{}] npc emotes", Strings::Commify(results.RowCount())); + n->emoteid = e.emoteid; + n->event_ = e.event_; + n->type = e.type; + + strn0cpy(n->text, e.text.c_str(), sizeof(n->text)); + + v->push_back(n); + } + + LogInfo( + "Loaded [{}] NPC Emote{}", + Strings::Commify(l.size()), + l.size() != 1 ? "s" : "" + ); } diff --git a/zone/zone.h b/zone/zone.h index 525603302..66fcebf2f 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -274,7 +274,7 @@ public: void LoadMercSpells(); void LoadMercTemplates(); void LoadNewMerchantData(uint32 merchantid); - void LoadNPCEmotes(std::vector *NPCEmoteList); + void LoadNPCEmotes(std::vector* v); void LoadTempMerchantData(); void LoadVeteranRewards(); void LoadZoneDoors();