/* EQEmu: EQEmulator Copyright (C) 2001-2026 EQEmu Development Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "client_version.h" using Version = EQ::versions::ClientVersion; void Client::SetClientVersion(Version client_version) { m_ClientVersion = client_version; m_ClientVersionBit = EQ::versions::ConvertClientVersionToClientVersionBit(client_version); } Version Client::GetClientVersion() const { return m_ClientVersion; } void ClientPatch::InterruptSpell(Client* c, uint32_t message, uint32_t spawn_id, const char* spell_link) { QueuePacket(c, &IMessage::InterruptSpell, GetClientComponent(c), message, spawn_id, spell_link); } void ClientPatch::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link) { QueueCloseClients(sender, true, RuleI(Range, SongMessages), nullptr, true, sender->IsClient() ? FilterPCSpells : FilterNPCSpells)( &IMessage::InterruptSpellOther, GetClientComponent, sender, message, spawn_id, name, spell_link); } static bool ShouldSendTargetBuffs(Client* c) { // this function checks for server rules against LAA and GM status to determine if a buffs packet should be sent // to a client (c) for targeted mobs if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) { // this rule bypasses LAA abilities, always return true if (c->GetGM()) { if (!c->EntityVariableExists(SEE_BUFFS_FLAG)) { // This flag just ensures that the following message is only sent once c->Message(Chat::White, "Your GM flag allows you to always see your targets' buffs."); c->SetEntityVariable(SEE_BUFFS_FLAG, "1"); } } return true; } if (c->IsRaidGrouped()) { Raid* raid = c->GetRaid(); if (raid) { uint32 gid = raid->GetGroup(c); if (gid < MAX_RAID_GROUPS && raid->GroupCount(gid) >= 3) { if (raid->GetLeadershipAA(groupAAInspectBuffs, gid)) return true; } } } else { Group* group = c->GetGroup(); if (group && group->GroupCount() >= 3) { if (group->GetLeadershipAA(groupAAInspectBuffs)) { return true; } } } return false; } void ClientPatch::SendFullBuffRefresh(Mob* sender, bool remove, bool ackreq) { bool suspended = zone->BuffTimersSuspended(); std::vector slots; // first, send to self if self is client if (sender->IsClient()) { Client* c = sender->CastToClient(); FastQueuePacket(c, &IBuff::RefreshBuffs, GetClientComponent(c), OP_RefreshBuffs, sender, false, suspended, slots); } // next, send to owner if self is a pet to a client if (sender->IsPet() && sender->GetOwner()->IsClient()) { if (Mob* owner = sender->GetOwner(); owner != nullptr && owner->IsClient()) { Client* c = owner->CastToClient(); FastQueuePacket(c, &IBuff::RefreshBuffs, GetClientComponent(c), OP_RefreshPetBuffs, sender, false, suspended, slots); } } // finally send to all clients targeting the mob, will need to mutate the packet to set the type auto mutate = [sender](std::unique_ptr& packet, Client* c) { GetClientComponent(c)->SetRefreshType(packet, sender, c); }; QueueClientsByTarget(sender, ackreq, ShouldSendTargetBuffs, mutate)( &IBuff::RefreshBuffs, GetClientComponent, OP_RefreshTargetBuffs, sender, false, suspended, slots); // if we have remove set, this will clear any target windows that shouldn't see the buffs if (remove) QueueClientsByTarget(sender, ackreq, [](Client* c) { return !ShouldSendTargetBuffs(c); }, mutate)( &IBuff::RefreshBuffs, GetClientComponent, OP_RefreshTargetBuffs, sender, true, suspended, slots); } void ClientPatch::SendSingleBuffChange(Mob* sender, const Buffs_Struct& buff, int slot, bool remove, bool ackreq) { bool suspended = zone->BuffTimersSuspended(); std::vector slots = { static_cast(slot) }; // first, send to self if self is client, which takes the definition and the refresh if (sender->IsClient()) { Client* c = sender->CastToClient(); FastQueuePacket(c, &IBuff::BuffDefinition, GetClientComponent(c), sender, buff, slot, remove); FastQueuePacket(c, &IBuff::RefreshBuffs, GetClientComponent(c), OP_RefreshBuffs, sender, remove, suspended, slots); } // the rest of the buff packets do not take the definition, only the refresh if (sender->IsPet() && sender->GetOwner()->IsClient()) { if (Mob* owner = sender->GetOwner(); owner != nullptr && owner->IsClient()) { Client* c = owner->CastToClient(); FastQueuePacket(c, &IBuff::RefreshBuffs, GetClientComponent(c), OP_RefreshPetBuffs, sender, remove, suspended, slots); } } auto mutate = [sender](std::unique_ptr& packet, Client* c) { GetClientComponent(c)->SetRefreshType(packet, sender, c); }; QueueClientsByTarget(sender, ackreq, ShouldSendTargetBuffs, mutate)( &IBuff::RefreshBuffs, GetClientComponent, OP_RefreshTargetBuffs, sender, remove, suspended, slots); // the client doesn't automatically do this for some reason, only send it to the sender (TOB doesn't actually need this, but it doesn't double show the message) if (remove && sender->IsClient()) sender->CastToClient()->SendColoredText(Chat::Spells, spells[buff.spellid].spell_fades); }