Expanded message patch to specialize fizzle messages

This commit is contained in:
dannuic 2026-04-22 12:10:18 -06:00
parent 2c4cb14a6c
commit 46dce8499f
10 changed files with 86 additions and 108 deletions

View File

@ -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 <cstdio>
#include <cstdarg>
#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<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
QueuePacket(outapp.get());
}
void Client::Tell_StringID(uint32 string_id, const char *who, const char *message)

View File

@ -16,7 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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,

View File

@ -42,6 +42,7 @@ public:
auto [packet, _] = build_packets.try_emplace(
ent->ClientVersion(),
std::invoke(fun, GetMessageComponent(ent->ClientVersion()).get(), std::forward<Args>(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>(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>(args)...));
if (packet->second != nullptr)
client->QueuePacket(packet->second, is_ack_required, Client::CLIENT_CONNECTED);
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
};

View File

@ -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();

View File

@ -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.