From 63c33ea4d6e764cfbc0145b4583944cd89666d48 Mon Sep 17 00:00:00 2001 From: dannuic Date: Thu, 23 Apr 2026 16:16:29 -0600 Subject: [PATCH] Simplified user interface --- zone/client.cpp | 22 +++--- zone/patch/client_version.h | 26 ++++--- zone/patch/components/message/IMessage.h | 81 +++++++++++++++++----- zone/patch/components/message/titanium.cpp | 56 +++++++-------- zone/patch/components/message/titanium.h | 28 +++----- zone/patch/components/message/tob.cpp | 71 ++++++++----------- zone/patch/components/message/tob.h | 20 ++---- zone/spells.cpp | 41 ++++++----- 8 files changed, 182 insertions(+), 163 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 9ae791995..66a4f7523 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3812,10 +3812,10 @@ void Client::MessageString(uint32 type, uint32 string_id, uint32 distance) return; if (distance > 0) - ZoneClient::ClientPatch::QueueCloseClients(this, false, distance)( - &ZoneClient::Message::IMessage::Simple, type, string_id); + ZoneClient::Message::CloseMessageString(this, false, static_cast(distance))( + type, string_id); else - ZoneClient::ClientPatch::QueuePacket(this, &ZoneClient::Message::IMessage::Simple, type, string_id); + ZoneClient::Message::MessageString(this, type, string_id); } // @@ -3825,9 +3825,9 @@ void Client::MessageString(uint32 type, uint32 string_id, uint32 distance) // This hack sucks but it's gonna work for now. // void Client::MessageString(uint32 type, uint32 string_id, const char* message1, - const char* message2,const char* message3,const char* message4, - const char* message5,const char* message6,const char* message7, - const char* message8,const char* message9, uint32 distance) + const char* message2, const char* message3, const char* message4, + const char* message5, const char* message6, const char* message7, + const char* message8, const char* message9, uint32 distance) { if (GetFilter(FilterSpellDamage) == FilterHide && type == Chat::NonMelee) return; @@ -3844,14 +3844,10 @@ void Client::MessageString(uint32 type, uint32 string_id, const char* message1, type = 4; if (distance > 0) - ZoneClient::ClientPatch::QueueCloseClients(this, false, distance)( - &ZoneClient::Message::IMessage::Formatted, type, string_id, - message1, message2, message3, message4, message5, - message6, message7, message8, message9); + ZoneClient::Message::CloseMessageString(this, false, static_cast(distance))(type, string_id, message1, + message2, message3, message4, message5, message6, message7, message8, message9); else - ZoneClient::ClientPatch::QueuePacket( - this, &ZoneClient::Message::IMessage::Formatted, type, string_id, - message1, message2, message3, message4, message5, + ZoneClient::Message::MessageString(this, type, string_id, message1, message2, message3, message4, message5, message6, message7, message8, message9); } diff --git a/zone/patch/client_version.h b/zone/patch/client_version.h index 23e493959..5733b7ac7 100644 --- a/zone/patch/client_version.h +++ b/zone/patch/client_version.h @@ -7,24 +7,25 @@ #include #include "common/emu_versions.h" -#include "components/message/IMessage.h" #include "zone/client.h" #include "zone/mob.h" namespace ZoneClient { +namespace Message { class IMessage; } + // store all static functions for the different patches here class ClientPatch { public: using ClientList = std::unordered_map; static const std::shared_ptr& GetMessageComponent(EQ::versions::ClientVersion version); - template - static void QueuePacket(Client* c, Fun fun, Args&&... args) + template + static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) { static_assert(std::is_member_function_pointer_v); - EQApplicationPacket* app = std::invoke(fun, c->GetMessageComponent().get(), std::forward(args)...); + EQApplicationPacket* app = std::invoke(fun, obj, std::forward(args)...); if (app != nullptr) { c->QueuePacket(app); delete app; @@ -34,8 +35,9 @@ public: // packet generator queue functions static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = true) { - return [=](Fun fun, Args&&... args) { - static_assert(std::is_member_function_pointer_v); + return [=](Fun fun, std::function component_getter, Args&&... args) { + static_assert(std::is_member_function_pointer_v && "Function is required to be a member function"); + std::unordered_map build_packets; std::unordered_map client_list = entity_list.GetClientList(); @@ -43,7 +45,7 @@ public: if (!ignore_sender || ent != sender) { auto [packet, _] = build_packets.try_emplace( ent->ClientVersion(), - std::invoke(fun, GetMessageComponent(ent->ClientVersion()).get(), std::forward(args)...)); + std::invoke(fun, component_getter(ent), std::forward(args)...)); if (packet->second != nullptr) ent->QueuePacket(packet->second, ackreq, Client::CLIENT_CONNECTED); @@ -61,11 +63,13 @@ public: Mob* skipped_mob = nullptr, bool is_ack_required = true, eqFilterType filter = FilterNone) { - if (distance <= 0) distance = zone->GetClientUpdateRange(); + if (distance <= 0) distance = static_cast(zone->GetClientUpdateRange()); + + return [=](Fun fun, std::function component_getter, Args&&... args) { + static_assert(std::is_member_function_pointer_v && "Function is required to be a member function"); - return [=](Fun fun, Args&&... args) { if (sender == nullptr) { - QueueClients(sender, ignore_sender, is_ack_required)(fun, std::forward(args)...); + QueueClients(sender, ignore_sender, is_ack_required)(fun, component_getter, std::forward(args)...); } else { float distance_squared = distance * distance; std::unordered_map build_packets; @@ -80,7 +84,7 @@ public: && client->ShouldGetPacket(sender, filter)) { auto [packet, _] = build_packets.try_emplace( client->ClientVersion(), - std::invoke(fun, GetMessageComponent(client->ClientVersion()).get(), std::forward(args)...)); + std::invoke(fun, component_getter(client), std::forward(args)...)); if (packet->second != nullptr) client->QueuePacket(packet->second, is_ack_required, Client::CLIENT_CONNECTED); diff --git a/zone/patch/components/message/IMessage.h b/zone/patch/components/message/IMessage.h index 153544e95..7b48b2604 100644 --- a/zone/patch/components/message/IMessage.h +++ b/zone/patch/components/message/IMessage.h @@ -17,7 +17,9 @@ */ #pragma once -#include +#include "patch/client_version.h" + +#include // Migration path: replace string_ids.h usage with ID enum values one call site at a time. @@ -26,28 +28,73 @@ class Mob; class EQApplicationPacket; namespace ZoneClient::Message { + +template +concept AllConstChar = (std::is_convertible_v && ...); + class IMessage { public: - IMessage() {} - virtual ~IMessage() {} + IMessage() = default; + virtual ~IMessage() = default; // these two are the basic string message packets - virtual EQApplicationPacket* Simple(uint32_t color, uint32_t id) const = 0; - virtual EQApplicationPacket* Formatted(uint32_t color, uint32_t id, - const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr, - const char* a4 = nullptr, const char* a5 = nullptr, const char* a6 = nullptr, - const char* a7 = nullptr, const char* a8 = nullptr, const char* a9 = nullptr) const = 0; + [[nodiscard]] virtual EQApplicationPacket* Simple(uint32_t color, uint32_t id) const = 0; + [[nodiscard]] virtual EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array& args) const = 0; // These aren't technically messages, but they use the same format and are similar enough to include here - virtual EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override = "") const = 0; + virtual EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const = 0; virtual EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, - uint32_t spell_id, const char* spell_name_override = "") const = 0; - - // Everything else is specializations of logic needed to build strings that differ between patches - virtual EQApplicationPacket* Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const = 0; - virtual EQApplicationPacket* FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, - const char* caster) const = 0; + const char* name, const char* spell_link) const = 0; }; -} // namespace Zone::Message + +static std::function GetComponent = [](const Client* c) -> IMessage* { + return ClientPatch::GetMessageComponent(c->GetClientVersion()).get(); +}; + +// Helper functions to wrap the packet construction in sends +template + requires (sizeof...(Args) <= 9) +void MessageString(Client* c, uint32_t type, uint32_t id, Args&&... args) +{ + if constexpr (sizeof...(Args) == 0) { + ClientPatch::QueuePacket(c, &IMessage::Simple, c->GetMessageComponent().get(), type, id); + } else { + std::array a = {args...}; + ClientPatch::QueuePacket(c, &IMessage::Formatted, c->GetMessageComponent().get(), type, id, a); + } +} + +static auto CloseMessageString( + Mob* sender, bool ignore_sender = false, float distance = 200.f, + Mob* skipped_mob = nullptr, bool is_ack_required = true, + eqFilterType filter = FilterNone) +{ + return [=](uint32_t type, uint32_t id, Args&&... args) { + static_assert(sizeof...(Args) <= 9, "Too many arguments"); + + auto queue_close_clients = ClientPatch::QueueCloseClients(sender, ignore_sender, distance, skipped_mob, + is_ack_required, filter); + + if constexpr (sizeof...(Args) == 0) { + return queue_close_clients(&IMessage::Simple, GetComponent, type, id); + } else { + std::array a = {args...}; + return queue_close_clients(&IMessage::Formatted, GetComponent, type, id, a); + } + }; +} + +inline void InterruptSpell(Client* c, uint32_t message, uint32_t spawn_id, const char* spell_link) +{ + ClientPatch::QueuePacket(c, &IMessage::InterruptSpell, c->GetMessageComponent().get(), message, spawn_id, spell_link); +} + +inline void InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link) +{ + ClientPatch::QueueCloseClients(sender, true, RuleI(Range, SongMessages), nullptr, true, + sender->IsClient() ? FilterPCSpells : FilterNPCSpells)( + &IMessage::InterruptSpellOther, GetComponent, sender, message, spawn_id, name, spell_link); +} + +} // namespace ZoneClient::Message diff --git a/zone/patch/components/message/titanium.cpp b/zone/patch/components/message/titanium.cpp index 73ab4e85c..4efc3b2c1 100644 --- a/zone/patch/components/message/titanium.cpp +++ b/zone/patch/components/message/titanium.cpp @@ -24,6 +24,7 @@ #include "common/serialize_buffer.h" namespace ZoneClient::Message { + EQApplicationPacket* Titanium::Simple(uint32_t color, uint32_t id) const { uint32_t string_id = ResolveID(id); @@ -41,27 +42,26 @@ EQApplicationPacket* Titanium::Simple(uint32_t color, uint32_t id) const } EQApplicationPacket* Titanium::Formatted( - uint32_t color, uint32_t id, - const char* a1, const char* a2, const char* a3, - const char* a4, const char* a5, const char* a6, - const char* a7, const char* a8, const char* a9) const + uint32_t color, uint32_t id, const std::array& args) const { uint32_t string_id = ResolveID(id); if (string_id > 0) { - if (!a1) + std::array resolved_args = args; + ResolveArguments(id, resolved_args); + if (!resolved_args[0]) return Simple(color, id); SerializeBuffer buf(20); - buf.WriteInt32(0); - buf.WriteInt32(string_id); - buf.WriteInt32(color); + buf.WriteUInt32(0); + buf.WriteUInt32(string_id); + buf.WriteUInt32(color); - for (const auto* a : {a1, a2, a3, a4, a5, a6, a7, a8, a9}) { + for (const auto* a : resolved_args) { if (a != nullptr) buf.WriteString(a); } - buf.WriteInt8(0); + buf.WriteUInt8(0); return new EQApplicationPacket(OP_FormattedMessage, std::move(buf)); } @@ -69,9 +69,7 @@ EQApplicationPacket* Titanium::Formatted( return nullptr; } -EQApplicationPacket* Titanium::InterruptSpell( - uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override) const +EQApplicationPacket* Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const { auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct)); auto ic = reinterpret_cast(outapp->pBuffer); @@ -82,11 +80,9 @@ EQApplicationPacket* Titanium::InterruptSpell( return outapp; } -EQApplicationPacket* Titanium::InterruptSpellOther( - Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override) const +EQApplicationPacket* Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, + const char* spell_link) const { - const char* name = sender->GetCleanName(); auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); @@ -95,16 +91,6 @@ EQApplicationPacket* Titanium::InterruptSpellOther( return outapp; } -EQApplicationPacket* Titanium::Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const -{ - return Simple(type, message); -} - -EQApplicationPacket* Titanium::FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const -{ - return Formatted(type, message, caster); -} - // A value of 0 means that the string isn't mapped in this client, valid string ids start at 1 uint32_t Titanium::ResolveID(uint32_t id) const { @@ -112,4 +98,20 @@ uint32_t Titanium::ResolveID(uint32_t id) const // override in patches where IDs need remapping return id; } + +void Titanium::ResolveArguments(uint32_t id, std::array& args) const +{ + switch (id) { + case SPELL_FIZZLE: + case MISS_NOTE: + args[0] = nullptr; // drop spell link + break; + case SPELL_FIZZLE_OTHER: + case MISSED_NOTE_OTHER: + args[1] = nullptr; // drop spell link + break; + default: + break; + } +} } // namespace ZoneClient::Message diff --git a/zone/patch/components/message/titanium.h b/zone/patch/components/message/titanium.h index 4c89ad10c..ebf7dfb7b 100644 --- a/zone/patch/components/message/titanium.h +++ b/zone/patch/components/message/titanium.h @@ -23,26 +23,18 @@ namespace ZoneClient::Message { class Titanium : public IMessage { public: - Titanium() {} - ~Titanium() override {} + Titanium() = default; + ~Titanium() override = default; - EQApplicationPacket* Simple(uint32_t color, uint32_t id) const override; + [[nodiscard]] EQApplicationPacket* Simple(uint32_t color, uint32_t id) const override; + [[nodiscard]] EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array& args) const override; - EQApplicationPacket* Formatted(uint32_t color, uint32_t id, - const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr, - const char* a4 = nullptr, const char* a5 = nullptr, const char* a6 = nullptr, - const char* a7 = nullptr, const char* a8 = nullptr, const char* a9 = nullptr) const override; - - EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override = "") const override; - EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override = "") const override; - - EQApplicationPacket* Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const override; - EQApplicationPacket* FizzleOther(uint32_t type, uint32_t message, - uint32_t spell_id, const char* caster) const override; + EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const override; + EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, + const char* spell_link) const override; protected: - virtual uint32_t ResolveID(uint32_t id) const; + [[nodiscard]] virtual uint32_t ResolveID(uint32_t id) const; + virtual void ResolveArguments(uint32_t id, std::array& args) const; }; -} // namespace Zone::Message +} // namespace ZoneClient::Message diff --git a/zone/patch/components/message/tob.cpp b/zone/patch/components/message/tob.cpp index c107918d5..0d8c888d5 100644 --- a/zone/patch/components/message/tob.cpp +++ b/zone/patch/components/message/tob.cpp @@ -73,6 +73,21 @@ uint32_t TOB::ResolveID(uint32_t id) const } } +void TOB::ResolveArguments(uint32_t id, std::array& args) const +{ + switch (id) { + case SPELL_FIZZLE: + case MISS_NOTE: + case SPELL_FIZZLE_OTHER: + case MISSED_NOTE_OTHER: + // take all arguments (spell link) + break; + default: + RoF2::ResolveArguments(id, args); + break; + } +} + // TOB is the first patch to fully support links in the client. This helper function is therefore internal to TOB // because any future patches would default to the TOB message strings static void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in) @@ -167,11 +182,18 @@ static void ServerToTOBConvertLinks(std::string& message_out, const std::string& } } -EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const char* a1, const char* a2, const char* a3, - const char* a4, const char* a5, const char* a6, const char* a7, const char* a8, const char* a9) const +EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const std::array& args) const + // const char* a1, const char* a2, const char* a3, + // const char* a4, const char* a5, const char* a6, + // const char* a7, const char* a8, const char* a9) const { uint32_t string_id = ResolveID(id); if (string_id > 0) { + std::array resolved_args = args; + ResolveArguments(id, resolved_args); + if (!resolved_args[0]) + return Simple(color, id); + SerializeBuffer buffer(49); // 49 is the minimum size needed for this packet since each arg writes at least 4 bytes buffer.WriteUInt32(0); @@ -180,7 +202,7 @@ EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const char* a1, buffer.WriteUInt32(string_id); buffer.WriteUInt32(color); - for (auto a : {a1, a2, a3, a4, a5, a6, a7, a8, a9}) { + for (auto a : resolved_args) { if (a != nullptr) { std::string new_message; ServerToTOBConvertLinks(new_message, a); @@ -195,16 +217,8 @@ EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const char* a1, return nullptr; } -EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override) const +EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const { - std::string spell_name = spell_name_override == nullptr || *spell_name_override == '\0' - ? GetSpellName(spell_id) - : spell_name_override; - - char spell_link[Links::MAX_LINK_SIZE]; - Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id, spell_name_override); - auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); @@ -215,17 +229,9 @@ EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, ui return outapp; } -EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override) const +EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, + const char* spell_link) const { - std::string spell_name = spell_name_override == nullptr || *spell_name_override == '\0' - ? GetSpellName(spell_id) - : spell_name_override; - - char spell_link[Links::MAX_LINK_SIZE]; - Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); - - const char* name = sender->GetCleanName(); auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + strlen(spell_link) + 2); auto ic = reinterpret_cast(outapp->pBuffer); @@ -236,23 +242,4 @@ EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uin return outapp; } -EQApplicationPacket* TOB::Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const -{ - std::string spell_name(GetSpellName(spell_id)); - - char spell_link[Links::MAX_LINK_SIZE]; - Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); - - return Formatted(type, message, spell_link); -} - -EQApplicationPacket* TOB::FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const -{ - std::string spell_name(GetSpellName(spell_id)); - - char spell_link[Links::MAX_LINK_SIZE]; - Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); - - return Formatted(type, message, caster, spell_link); -} -} // namespace Zone::Message +} // namespace ZoneClient::Message diff --git a/zone/patch/components/message/tob.h b/zone/patch/components/message/tob.h index 50d5d5222..a6084535a 100644 --- a/zone/patch/components/message/tob.h +++ b/zone/patch/components/message/tob.h @@ -26,21 +26,13 @@ public: TOB() {} ~TOB() override {} - EQApplicationPacket* Formatted(uint32_t color, uint32_t id, - const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr, - const char* a4 = nullptr, const char* a5 = nullptr, const char* a6 = nullptr, - const char* a7 = nullptr, const char* a8 = nullptr, const char* a9 = nullptr) const override; + [[nodiscard]] EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array& args) const override; - EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override) const override; - EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id, - const char* spell_name_override) const override; - - EQApplicationPacket* Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const override; - EQApplicationPacket* - FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const override; + EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const override; + EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link) const override; protected: - uint32_t ResolveID(uint32_t id) const override; + [[nodiscard]] uint32_t ResolveID(uint32_t id) const override; + void ResolveArguments(uint32_t id, std::array& args) const override; }; -} // namespace Zone::Message +} // namespace ZoneClient::Message diff --git a/zone/spells.cpp b/zone/spells.cpp index f556468e6..4bff2cf81 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -95,6 +95,9 @@ #include #include +#include "common/links.h" +#include "patch/components/message/IMessage.h" + extern Zone *zone; extern volatile bool is_zone_loaded; extern WorldServer worldserver; @@ -333,20 +336,18 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, Mob::SetMana(GetMana() - use_mana); // We send StopCasting which will update mana StopCasting(); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); + if (IsClient()) - ZoneClient::ClientPatch::QueuePacket( - CastToClient(), &ZoneClient::Message::IMessage::Fizzle, - Chat::SpellFailure, fizzle_msg, spell_id); + ZoneClient::Message::MessageString(CastToClient(), Chat::SpellFailure, fizzle_msg, spell_link); /** * Song Failure message */ - ZoneClient::ClientPatch::QueueCloseClients( - this, true, RuleI(Range, SpellMessages), - nullptr, true, - IsClient() ? FilterPCSpells : FilterNPCSpells)( - &ZoneClient::Message::IMessage::FizzleOther, Chat::SpellFailure, - fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER, spell_id, GetName()); + ZoneClient::Message::CloseMessageString(this, true, RuleI(Range, SpellMessages), + nullptr, true, IsClient() ? FilterPCSpells : FilterNPCSpells)( + Chat::SpellFailure, fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER, GetName(), spell_link); TryTriggerOnCastRequirement(); return false; @@ -1301,10 +1302,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) if (IsClient() && message != SONG_ENDS) { // the interrupt message - ZoneClient::ClientPatch::QueuePacket( - CastToClient(), &ZoneClient::Message::IMessage::InterruptSpell, - message, GetID(), spellid, ""); - + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spellid); + ZoneClient::Message::InterruptSpell(CastToClient(), message, GetID(), spell_link); SendSpellBarEnable(spellid); } @@ -1330,10 +1330,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) } // this is the actual message, it works the same as a formatted message - ZoneClient::ClientPatch::QueueCloseClients( - this, true, RuleI(Range, SongMessages), nullptr, true, - IsClient() ? FilterPCSpells : FilterNPCSpells)( - &ZoneClient::Message::IMessage::InterruptSpellOther, this, message_other, GetID(), spellid, ""); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spellid); + ZoneClient::Message::InterruptSpellOther(this, message_other, GetID(), GetCleanName(), spell_link); } // this is like interrupt, just it doesn't spam interrupt packets to everyone @@ -7269,11 +7268,11 @@ void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time Known bug: When a bard uses an augment with a clicky that has a cast time, the cast won't display. This issue only affects bards. */ if (is_casting_bard_song) { - //For spells with cast times. Cancel song cast, stop pusling and start item cast. + //For spells with cast times. Cancel song cast, stop pulsing and start item cast. if (cast_time != 0) { - ZoneClient::ClientPatch::QueuePacket( - CastToClient(), &ZoneClient::Message::IMessage::InterruptSpell, - SONG_ENDS, GetID(), spell_id, ""); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); + ZoneClient::Message::InterruptSpell(CastToClient(), SONG_ENDS, GetID(), spell_link); ZeroCastingVars(); ZeroBardPulseVars();