From 46dce8499fa7afae7f42b51e09e3c726bbf04d14 Mon Sep 17 00:00:00 2001 From: dannuic Date: Wed, 22 Apr 2026 12:10:18 -0600 Subject: [PATCH] Expanded message patch to specialize fizzle messages --- zone/client.cpp | 102 ++++++++------------- zone/client.h | 3 +- zone/patch/client_version.h | 23 ++--- zone/patch/components/message/IMessage.h | 3 +- zone/patch/components/message/titanium.cpp | 8 +- zone/patch/components/message/titanium.h | 3 +- zone/patch/components/message/tob.cpp | 13 +++ zone/patch/components/message/tob.h | 3 + zone/spells.cpp | 32 ++----- zone/string_ids.h | 4 +- 10 files changed, 86 insertions(+), 108 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index d9e04ba7b..3306c9a1f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -44,6 +44,8 @@ #include "common/spdat.h" #include "common/strings.h" #include "common/zone_store.h" +#include "patch/client_version.h" +#include "patch/components/message/IMessage.h" #include "zone/bot_command.h" #include "zone/cheat_manager.h" #include "zone/command.h" @@ -66,8 +68,6 @@ #include #include -#include "patch/client_version.h" - extern QueryServ* QServ; extern EntityList entity_list; extern Zone* zone; @@ -1794,7 +1794,7 @@ void Client::Message(uint32 type, const char* message, ...) { } void Client::FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...) { - if (!FilteredMessageCheck(sender, filter)) + if (!ShouldGetPacket(sender, filter)) return; va_list argptr; @@ -3876,50 +3876,49 @@ void Client::MessageString(const CZClientMessageString_Struct* msg) } } -// helper function, returns true if we should see the message -bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter) +// helper function, returns true if the client should get the packet based on the filter and sender +bool Client::ShouldGetPacket(Mob *sender, eqFilterType filter) { eqFilterMode mode = GetFilter(filter); - // easy ones first - if (mode == FilterShow) { - return true; - } else if (mode == FilterHide) { - return false; - } - if (sender != this && mode == FilterShowSelfOnly) { + // easy ones first + if (mode == FilterShow) + return true; + + if (mode == FilterHide) return false; - } else if (sender) { - if (mode == FilterShowGroupOnly) { - auto g = GetGroup(); - auto r = GetRaid(); - if (g) { - if (g->IsGroupMember(sender)) { - return true; - } - } else if (r && sender->IsClient()) { - auto rgid1 = r->GetGroup(this); - auto rgid2 = r->GetGroup(sender->CastToClient()); - if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2) { - return true; - } - } else { - return false; - } + + if (sender != this && mode == FilterShowSelfOnly) + return false; + + if (sender != nullptr && mode == FilterShowGroupOnly) { + if (sender == this) + return true; + + auto g = GetGroup(); + if (g && g->IsGroupMember(sender)) + return true; + + auto r = GetRaid(); + if (r && sender->IsClient()) { + auto rgid1 = r->GetGroup(this); + auto rgid2 = r->GetGroup(sender->CastToClient()); + if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2) + return true; + } else { + return false; } } - // we passed our checks + // fallback case (send by default) return true; } void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id) { - if (!FilteredMessageCheck(sender, filter)) - return; - - MessageString(type, string_id); + if (ShouldGetPacket(sender, filter)) + MessageString(type, string_id); } void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id, @@ -3927,37 +3926,16 @@ void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter const char *message4, const char *message5, const char *message6, const char *message7, const char *message8, const char *message9) { - if (!FilteredMessageCheck(sender, filter)) - return; - - if (type == Chat::Emote) - type = 4; - if (!message1) { FilteredMessageString(sender, type, filter, string_id); // use the simple message instead - return; + } else if (ShouldGetPacket(sender, filter)) { + if (type == Chat::Emote) + type = 4; + + MessageString( + type, string_id, message1, message2, message3, message4, + message5, message6, message7, message8, message9); } - - const char *message_arg[] = { - message1, message2, message3, message4, message5, - message6, message7, message8, message9 - }; - - SerializeBuffer buf(20); - buf.WriteInt32(0); // unknown - buf.WriteInt32(string_id); - buf.WriteInt32(type); - for (auto &m : message_arg) { - if (m == nullptr) - break; - buf.WriteString(m); - } - - buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime - - auto outapp = std::make_unique(OP_FormattedMessage, std::move(buf)); - - QueuePacket(outapp.get()); } void Client::Tell_StringID(uint32 string_id, const char *who, const char *message) diff --git a/zone/client.h b/zone/client.h index d5fdcb803..2b416840b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -16,7 +16,6 @@ along with this program. If not, see . */ #pragma once -#include "patch/components/message/IMessage.h" class Client; class EQApplicationPacket; @@ -348,7 +347,7 @@ public: void MessageString(uint32 type, uint32 string_id, uint32 distance = 0) override; void MessageString(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0) override; void MessageString(const CZClientMessageString_Struct* msg); - bool FilteredMessageCheck(Mob *sender, eqFilterType filter); + bool ShouldGetPacket(Mob *sender, eqFilterType filter); void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id); void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id, const char *message1, const char *message2 = nullptr, diff --git a/zone/patch/client_version.h b/zone/patch/client_version.h index c78ababbd..f1856e2c7 100644 --- a/zone/patch/client_version.h +++ b/zone/patch/client_version.h @@ -42,6 +42,7 @@ public: auto [packet, _] = build_packets.try_emplace( ent->ClientVersion(), std::invoke(fun, GetMessageComponent(ent->ClientVersion()).get(), std::forward(args)...)); + if (packet->second != nullptr) ent->QueuePacket(packet->second, ackreq, Client::CLIENT_CONNECTED); } @@ -71,21 +72,13 @@ public: if ((!ignore_sender || client != sender) && client != skipped_mob && DistanceSquared(client->GetPosition(), sender->GetPosition()) < distance_squared && - client->Connected()) { - eqFilterMode client_filter = client->GetFilter(filter); - if (filter == FilterNone || client_filter == FilterShow || - (client_filter == FilterShowGroupOnly && - (sender == client || (client->GetGroup() && client->GetGroup()->IsGroupMember( - sender)))) || - (client_filter == FilterShowSelfOnly && client == sender) - ) { - auto [packet, _] = build_packets.try_emplace( - client->ClientVersion(), - std::invoke(fun, GetMessageComponent(client->ClientVersion()).get(), - std::forward(args)...)); - if (packet->second != nullptr) - client->QueuePacket(packet->second, is_ack_required, Client::CLIENT_CONNECTED); - } + client->Connected() && client->ShouldGetPacket(sender, filter)) { + auto [packet, _] = build_packets.try_emplace( + client->ClientVersion(), + std::invoke(fun, GetMessageComponent(client->ClientVersion()).get(), 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 f42e2c59a..4aac23b21 100644 --- a/zone/patch/components/message/IMessage.h +++ b/zone/patch/components/message/IMessage.h @@ -46,7 +46,8 @@ public: const char* spell_name_override = "") const = 0; // Everything else is specializations of logic needed to build strings that differ between patches - virtual EQApplicationPacket* Fizzle(Mob* m, uint32_t type, uint32_t message, uint32_t spell_id) const = 0; + 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; }; } // namespace Zone::Message diff --git a/zone/patch/components/message/titanium.cpp b/zone/patch/components/message/titanium.cpp index 34b01b5b2..b6396e99d 100644 --- a/zone/patch/components/message/titanium.cpp +++ b/zone/patch/components/message/titanium.cpp @@ -91,8 +91,12 @@ EQApplicationPacket* Titanium::InterruptSpellOther(Mob* sender, uint32_t message return outapp; } -EQApplicationPacket* Titanium::Fizzle(Mob* m, uint32_t type, uint32_t message, uint32_t spell_id) const { - return nullptr; +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 diff --git a/zone/patch/components/message/titanium.h b/zone/patch/components/message/titanium.h index 0f9ef46d9..b1072cfe0 100644 --- a/zone/patch/components/message/titanium.h +++ b/zone/patch/components/message/titanium.h @@ -38,7 +38,8 @@ public: EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id, const char* spell_name_override = "") const override; - EQApplicationPacket* Fizzle(Mob* m, uint32_t type, uint32_t message, uint32_t spell_id) 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; protected: virtual uint32_t ResolveID(uint32_t id) const; diff --git a/zone/patch/components/message/tob.cpp b/zone/patch/components/message/tob.cpp index 84c62f54a..9829385ad 100644 --- a/zone/patch/components/message/tob.cpp +++ b/zone/patch/components/message/tob.cpp @@ -107,4 +107,17 @@ 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)); + std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); + + return Formatted(type, message, spell_link.c_str()); +} + +EQApplicationPacket* TOB::FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const { + std::string spell_name(GetSpellName(spell_id)); + std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); + + return Formatted(type, message, caster, spell_link.c_str()); +} } // namespace Zone::Message diff --git a/zone/patch/components/message/tob.h b/zone/patch/components/message/tob.h index a0d9d3ec3..9491b6af1 100644 --- a/zone/patch/components/message/tob.h +++ b/zone/patch/components/message/tob.h @@ -30,6 +30,9 @@ public: 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; + protected: uint32_t ResolveID(uint32_t id) const override; }; diff --git a/zone/spells.cpp b/zone/spells.cpp index f2e1df516..94a393d4b 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -336,35 +336,21 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, Mob::SetMana(GetMana() - use_mana); // We send StopCasting which will update mana StopCasting(); - // TODO: can handle spell name overrides here - std::string spell_name(GetSpellName(spell_id)); - std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); - - // pre-TOB clients will just discard the extra argument here, so don't worry about patching them out in patches - MessageString(Chat::SpellFailure, fizzle_msg, spell_link.c_str()); + if (IsClient()) + ZoneClient::ClientPatch::QueuePacket(CastToClient(), &ZoneClient::Message::IMessage::Fizzle, + Chat::SpellFailure, fizzle_msg, spell_id); /** * Song Failure message - * pre-TOB clients will just discard the extra argument here, so don't worry about patching them out in patches */ - entity_list.FilteredMessageCloseString( - this, - true, - RuleI(Range, SpellMessages), - Chat::SpellFailure, - (IsClient() ? FilterPCSpells : FilterNPCSpells), - (fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER), - 0, - /* - MessageFormat: A missed note brings %1's song to a close! (TOB: A missed note brings %1's %2 to a close!) - MessageFormat: %1's spell fizzles! (TOB: %1's %2 spell fizzles!) - */ - GetName(), - spell_link.c_str() - ); + 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()); TryTriggerOnCastRequirement(); - return(false); + return false; } SaveSpellLoc(); diff --git a/zone/string_ids.h b/zone/string_ids.h index 7e3fe82e9..540b745cb 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -66,9 +66,9 @@ #define FISHING_SUCCESS_FISH_NAME 421 //You caught %1! #define FISHING_SPILL_BEER 171 //You spill your beer while bringing in your line. #define FISHING_LOST_BAIT 172 //You lost your bait! -#define SPELL_FIZZLE 173 //Your spell fizzles! // TODO: TOB - Your %1 spell fizzles! +#define SPELL_FIZZLE 173 //Your spell fizzles! #define MUST_EQUIP_ITEM 179 //You cannot use this item unless it is equipped. -#define MISS_NOTE 180 //You miss a note, bringing your song to a close! // TODO: TOB - You miss a note, bringing your %1 to a close! +#define MISS_NOTE 180 //You miss a note, bringing your song to a close! #define CANNOT_USE_ITEM 181 //Your race, class, or deity cannot use this item. // TODO: TOB moved this: 6611 Your class, deity, and/or race may not equip %1. #define ITEM_OUT_OF_CHARGES 182 //Item is out of charges. #define ALREADY_ON_A_MOUNT 189 //You are already on a mount.