diff --git a/common/patches/IBuff.h b/common/patches/IBuff.h index 176d0981c..adab437df 100644 --- a/common/patches/IBuff.h +++ b/common/patches/IBuff.h @@ -24,14 +24,16 @@ public: IBuff() = default; virtual ~IBuff() = default; - virtual std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + // TODO: I think all of the legacy stuff can just be rolled into regular buff functions and the call sites modified + virtual std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, bool for_target, bool clear_buffs) const = 0; + virtual std::unique_ptr LegacyBuffDefinition(Mob* mob, Buffs_Struct& buff, int slot) const = 0; // TODO: add the buffs definition packet virtual std::unique_ptr BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const = 0; virtual std::unique_ptr RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const = 0; - virtual void SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const = 0; + virtual void SetRefreshType(std::unique_ptr& packet, Mob* source, Client* target) const = 0; }; } // namespace Buff diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index b5c8a9840..e2cbf8f11 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -35,6 +35,8 @@ #include #include +#include "zone/mob.h" + namespace SoD { @@ -4285,3 +4287,66 @@ namespace SoD return index; // as long as we guard against bad slots server side, we should be fine } } /*SoD*/ + +namespace Buff { + +std::unique_ptr SoD::MakeLegacyBuffsPacket(Mob* mob, bool for_target, bool clear_buffs) const +{ + // SoD only supports target refresh, not self refresh packets + if (for_target) { + uint32 count = 0; + uint32 buff_count; + + // for self we want all buffs, for target, we want to skip song window buffs + // since NPCs and pets don't have a song window, we still see it for them :P + if (for_target) { + buff_count = (clear_buffs) ? 0 : mob->GetMaxBuffSlots(); + } + else { + buff_count = mob->GetMaxTotalSlots(); + } + + Buffs_Struct* buffs = mob->GetBuffs(); + + for(int i = 0; i < buff_count; ++i) { + if (buffs[i].spellid > 1) { + ++count; + } + } + + auto outapp = std::make_unique(OP_RefreshTargetBuffs, + sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); + + BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; + buff->entity_id = mob->GetID(); + buff->count = count; + buff->all_buffs = 1; + buff->tic_timer = mob->GetRemainingTicTime(); + // there are more types, the client doesn't seem to really care though. The others are also currently hard to fill in here ... + // (see comment in common/eq_packet_structs.h) + if (for_target) + buff->type = mob->IsClient() ? 5 : 7; + else + buff->type = 0; + + buff->name_lengths = 0; // hacky shit + uint32 index = 0; + for(int i = 0; i < buff_count; ++i) { + if (buffs[i].spellid > 1) { + buff->entries[index].buff_slot = i; + buff->entries[index].spell_id = buffs[i].spellid; + buff->entries[index].tics_remaining = buffs[i].ticsremaining; + buff->entries[index].num_hits = buffs[i].hit_number; + strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64); + buff->name_lengths += strlen(buff->entries[index].caster); + ++index; + } + } + + return outapp; + } + + return nullptr; +} + +} // namespace Buff diff --git a/common/patches/sod.h b/common/patches/sod.h index 9390e23ea..cc4a31b7a 100644 --- a/common/patches/sod.h +++ b/common/patches/sod.h @@ -68,6 +68,9 @@ class SoD : public SoF public: SoD() = default; ~SoD() override = default; + + std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, bool for_target, + bool clear_buffs) const override; }; } // namespace Buff diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 3c9bfb1eb..35d907e5f 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -4019,58 +4019,40 @@ void Titanium::ResolveArguments(uint32_t id, std::array& args) c } // namespace Message namespace Buff { -std::unique_ptr Titanium::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, - bool clear_buffs) const +std::unique_ptr Titanium::MakeLegacyBuffsPacket(Mob* mob, bool for_target, bool clear_buffs) const { - uint32 count = 0; - uint32 buff_count; + return nullptr; +} - // for self we want all buffs, for target, we want to skip song window buffs - // since NPCs and pets don't have a song window, we still see it for them :P - if (for_target) { - buff_count = (clear_buffs) ? 0 : mob->GetMaxBuffSlots(); - } - else { - buff_count = mob->GetMaxTotalSlots(); - } +std::unique_ptr Titanium::LegacyBuffDefinition(Mob* mob, Buffs_Struct& buff, int slot) const +{ + auto outapp = std::make_unique(OP_BuffDefinition, sizeof(SpellBuffPacket_Struct)); + SpellBuffPacket_Struct* sbf = (SpellBuffPacket_Struct*) outapp->pBuffer; - Buffs_Struct* buffs = mob->GetBuffs(); + sbf->entityid = mob->GetID(); - for(int i = 0; i < buff_count; ++i) { - if (buffs[i].spellid > 1) { - ++count; - } - } + sbf->buff.effect_type = 2; - //Create it for a targeting window, else create it for a create buff packet. - auto outapp = std::make_unique(for_target ? OP_RefreshTargetBuffs : OP_RefreshBuffs, - sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); + sbf->buff.level = buff.casterlevel > 0 ? buff.casterlevel : mob->GetLevel(); + sbf->buff.bard_modifier = buff.instrument_mod; + sbf->buff.spellid = buff.spellid; + sbf->buff.duration = buff.ticsremaining; + if (buff.dot_rune) + sbf->buff.counters = buff.dot_rune; + else if (buff.magic_rune) + sbf->buff.counters = buff.magic_rune; + else if (buff.melee_rune) + sbf->buff.counters = buff.melee_rune; + else if (buff.counters) + sbf->buff.counters = buff.counters; + sbf->buff.player_id = buff.casterid; + sbf->buff.num_hits = buff.hit_number; + sbf->buff.y = buff.caston_y; + sbf->buff.x = buff.caston_x; + sbf->buff.z = buff.caston_z; - BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; - buff->entity_id = mob->GetID(); - buff->count = count; - buff->all_buffs = 1; - buff->tic_timer = timer; - // there are more types, the client doesn't seem to really care though. The others are also currently hard to fill in here ... - // (see comment in common/eq_packet_structs.h) - if (for_target) - buff->type = mob->IsClient() ? 5 : 7; - else - buff->type = 0; - - buff->name_lengths = 0; // hacky shit - uint32 index = 0; - for(int i = 0; i < buff_count; ++i) { - if (buffs[i].spellid > 1) { - buff->entries[index].buff_slot = i; - buff->entries[index].spell_id = buffs[i].spellid; - buff->entries[index].tics_remaining = buffs[i].ticsremaining; - buff->entries[index].num_hits = buffs[i].hit_number; - strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64); - buff->name_lengths += strlen(buff->entries[index].caster); - ++index; - } - } + sbf->slotid = slot; + sbf->bufffade = 0; return outapp; } @@ -4087,6 +4069,6 @@ std::unique_ptr Titanium::RefreshBuffs(EmuOpcode opcode, Mo return nullptr; } -void Titanium::SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const {} +void Titanium::SetRefreshType(std::unique_ptr& packet, Mob* source, Client* target) const {} } // namespace Buff diff --git a/common/patches/titanium.h b/common/patches/titanium.h index 33c48ccce..3e5439a04 100644 --- a/common/patches/titanium.h +++ b/common/patches/titanium.h @@ -85,14 +85,15 @@ public: Titanium() = default; ~Titanium() override = default; - std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, bool for_target, bool clear_buffs) const override; + std::unique_ptr LegacyBuffDefinition(Mob* mob, Buffs_Struct& buff, int slot) const override; std::unique_ptr BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const override; std::unique_ptr RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const override; - void SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const override; + void SetRefreshType(std::unique_ptr& packet, Mob* source, Client* target) const override; }; } // namespace Buff diff --git a/common/patches/tob.cpp b/common/patches/tob.cpp index 79853f4cb..8a8c2e4a5 100644 --- a/common/patches/tob.cpp +++ b/common/patches/tob.cpp @@ -5613,8 +5613,7 @@ std::unique_ptr TOB::InterruptSpellOther(Mob* sender, uint3 } // namespace Message namespace Buff { -std::unique_ptr TOB::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, - bool clear_buffs) const { return nullptr; } +// std::unique_ptr TOB::MakeLegacyBuffsPacket(Mob* mob, bool for_target, bool clear_buffs) const { return nullptr; } std::unique_ptr TOB::BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const { @@ -5712,7 +5711,7 @@ std::unique_ptr TOB::RefreshBuffs(EmuOpcode opcode, Mob* mo } // 0 = self buff window, 1 = self target window, 2 = pet buff or target window, 4 = group, 5 = PC, 7 = NPC -void TOB::SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const +void TOB::SetRefreshType(std::unique_ptr& packet, Mob* source, Client* target) const { unsigned char* type = &packet->pBuffer[packet->size - 2]; diff --git a/common/patches/tob.h b/common/patches/tob.h index d27a5c0ff..c90dc122e 100644 --- a/common/patches/tob.h +++ b/common/patches/tob.h @@ -65,14 +65,14 @@ public: TOB() = default; ~TOB() override = default; - std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, - bool clear_buffs) const override; + // std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, bool for_target, + // bool clear_buffs) const override; std::unique_ptr BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const override; std::unique_ptr RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const override; - void SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const override; + void SetRefreshType(std::unique_ptr& packet, Mob* source, Client* target) const override; }; } // namespace Buff diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index b4c1b60c7..f0ad16e1a 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -38,6 +38,8 @@ #include #include +#include "zone/mob.h" + namespace UF { @@ -5234,3 +5236,64 @@ namespace UF return index; // as long as we guard against bad slots server side, we should be fine } } /*UF*/ + +namespace Buff { + +std::unique_ptr UF::MakeLegacyBuffsPacket(Mob* mob, bool for_target, bool clear_buffs) const +{ + // UF introduced the self update buff packet + + uint32 count = 0; + uint32 buff_count; + + // for self we want all buffs, for target, we want to skip song window buffs + // since NPCs and pets don't have a song window, we still see it for them :P + if (for_target) { + buff_count = (clear_buffs) ? 0 : mob->GetMaxBuffSlots(); + } + else { + buff_count = mob->GetMaxTotalSlots(); + } + + Buffs_Struct* buffs = mob->GetBuffs(); + + for(int i = 0; i < buff_count; ++i) { + if (buffs[i].spellid > 1) { + ++count; + } + } + + //Create it for a targeting window, else create it for a create buff packet. + auto outapp = std::make_unique(for_target ? OP_RefreshTargetBuffs : OP_RefreshBuffs, + sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); + + BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; + buff->entity_id = mob->GetID(); + buff->count = count; + buff->all_buffs = 1; + buff->tic_timer = mob->GetRemainingTicTime(); + // there are more types, the client doesn't seem to really care though. The others are also currently hard to fill in here ... + // (see comment in common/eq_packet_structs.h) + if (for_target) + buff->type = mob->IsClient() ? 5 : 7; + else + buff->type = 0; + + buff->name_lengths = 0; // hacky shit + uint32 index = 0; + for(int i = 0; i < buff_count; ++i) { + if (buffs[i].spellid > 1) { + buff->entries[index].buff_slot = i; + buff->entries[index].spell_id = buffs[i].spellid; + buff->entries[index].tics_remaining = buffs[i].ticsremaining; + buff->entries[index].num_hits = buffs[i].hit_number; + strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64); + buff->name_lengths += strlen(buff->entries[index].caster); + ++index; + } + } + + return outapp; +} + +} // namespace Buff diff --git a/common/patches/uf.h b/common/patches/uf.h index dfdc5e481..5332f2ff3 100644 --- a/common/patches/uf.h +++ b/common/patches/uf.h @@ -68,6 +68,9 @@ class UF : public SoD public: UF() = default; ~UF() override = default; + + std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, bool for_target, + bool clear_buffs) const override; }; } // namespace Buff diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d76ea7b86..7424f686f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -61,6 +61,8 @@ #include #include +#include "client_version.h" + extern QueryServ* QServ; extern Zone* zone; extern volatile bool is_zone_loaded; @@ -933,10 +935,8 @@ void Client::CompleteConnect() delete pack; } - if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } + if (IsClient()) + Buff::SendLegacyBuffsPacket(CastToClient(), this, false); // TODO: load these states // We at least will set them to the correct state for now @@ -15108,30 +15108,7 @@ void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) if (nt) { SetTarget(nt); - bool inspect_buffs = false; - // rank 1 gives you ability to see NPC buffs in target window (SoD+) - if (nt->IsNPC()) { - if (IsRaidGrouped()) { - Raid *raid = GetRaid(); - if (raid) { - uint32 gid = raid->GetGroup(this); - if (gid < 12 && raid->GroupCount(gid) > 2) - inspect_buffs = raid->GetLeadershipAA(groupAAInspectBuffs, gid); - } - } - else { - Group *group = GetGroup(); - if (group && group->GroupCount() > 2) - inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs); - } - } - if (GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs) || nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || - (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || - (nt->IsBot() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || // TODO: bot pets - (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) - { - nt->SendBuffsToClient(this); - } + Buff::SendLegacyBuffsPacket(this, nt); } else { diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 6953a6728..431b2af29 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -45,6 +45,8 @@ #include +#include "client_version.h" + extern QueryServ* QServ; extern Zone* zone; extern volatile bool is_zone_loaded; @@ -2307,11 +2309,8 @@ void Client::ClearHover() entity_list.QueueClients(this, outapp, false); safe_delete(outapp); - if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) - { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } + if (IsClient()) + Buff::SendLegacyBuffsPacket(CastToClient(), this, false); dead = false; } diff --git a/zone/client_version.h b/zone/client_version.h index 974ab1693..e9ea3eb7f 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -18,6 +18,7 @@ namespace ClientPatch { using ClientList = std::unordered_map; template using ComponentGetter = std::function; +using SendPredicate = std::function; template requires std::is_member_function_pointer_v @@ -37,9 +38,8 @@ static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = requires std::is_member_function_pointer_v { std::array, EQ::versions::ClientVersionCount> build_packets; - std::unordered_map client_list = entity_list.GetClientList(); - for (auto [_, ent] : client_list) { + for (auto [_, ent] : entity_list.GetClientList()) { if (!ignore_sender || ent != sender) { auto& packet = build_packets.at(static_cast(ent->ClientVersion())); if (!packet) @@ -106,66 +106,25 @@ static void FastQueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) } } -static auto QueueClientsByTarget(Mob* sender, bool iSendToSender, Mob* SkipThisMob, bool ackreq, bool HoTT, - uint32 ClientVersionBits, bool inspect_buffs, bool clear_target_window) +static auto QueueClientsByTarget(Mob* sender, bool ackreq, bool HoTT, const SendPredicate& ShouldSend) { return [=](Fun fun, const ComponentGetter component, Args&&... args) requires std::is_member_function_pointer_v { - std::unordered_map build_packets; - std::unordered_map client_list = entity_list.GetClientList(); + if (sender != nullptr) { + std::array, EQ::versions::ClientVersionCount> build_packets; - for (auto [_, c] : client_list) { - if (c != SkipThisMob && (iSendToSender || c != sender)) { - Mob* Target = c->GetTarget(); - if (Target != nullptr) { - // if (Target == sender || (iSendToSender && c == sender) || (HoTT && Target->GetTarget() == sender)) { - // // the client has the sender targeted, so check if we send the packet - // EQApplicationPacket* app = std::invoke(fun, component_getter(c), std::forward(args)...); - // } - Mob* TargetsTarget = Target->GetTarget(); - bool Send = iSendToSender && c == sender; - if (c != sender) { - if (Target == sender) { - if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA - Send = clear_target_window; - if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) { - if (c->GetGM()) { - if (!c->EntityVariableExists(SEE_BUFFS_FLAG)) { - c->Message(Chat::White, - "Your GM flag allows you to always see your targets' buffs."); - c->SetEntityVariable(SEE_BUFFS_FLAG, "1"); - } - } + for (auto [_, c] : entity_list.GetClientList()) { + if (c != sender) { + Mob* Target = c->GetTarget(); + if ((Target == sender || (HoTT && Target != nullptr && Target->GetTarget() == sender)) && ShouldSend(c)) { + auto& packet = build_packets.at(static_cast(c->ClientVersion())); + if (!packet) + if (auto comp = component(c); comp != nullptr) + packet = std::invoke(fun, comp, std::forward(args)...); - Send = !clear_target_window; - } else 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)) - Send = !clear_target_window; - } - } - } else { - Group* group = c->GetGroup(); - if (group && group->GroupCount() >= 3) { - if (group->GetLeadershipAA(groupAAInspectBuffs)) { - Send = !clear_target_window; - } - } - } - } else { - Send = true; - } - } else if (HoTT && TargetsTarget == sender) { - Send = true; - } - } - - if (Send && (c->ClientVersionBit() & ClientVersionBits)) { - EQApplicationPacket* app = std::invoke(fun, component(c), std::forward(args)...); + if (packet) + c->QueuePacket(packet.get(), ackreq, Client::CLIENT_CONNECTED); } } } @@ -238,14 +197,58 @@ static std::function GetComponent = [](const Client* c) -> IBuff* { return GetBuffComponent(c->GetClientVersion()).get(); }; -inline void SendLegacyBuffsPacket(Client* c, Mob* sender, int32 timer, bool for_target = true, bool clear_buffs = false) +static bool ShouldSendTargetBuffs(Client* c) { - ClientPatch::FastQueuePacket(c, &IBuff::MakeLegacyBuffsPacket, GetComponent(c), sender, timer, for_target, clear_buffs); + // 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)) { + 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; } -inline void SendLegacyBuffsPacketToClients(Client* c, bool for_target = true, bool clear_buffs = false) +inline void SendLegacyBuffsPacket(Client* c, Mob* sender, bool for_target = true, bool clear_buffs = false) { + ClientPatch::FastQueuePacket(c, &IBuff::MakeLegacyBuffsPacket, GetComponent(c), sender, for_target, clear_buffs); +} +inline void SendLegacyBuffsPacketToClients(Mob* sender, bool ackreq = true, bool HoTT = false, bool clear_buffs = false) +{ + ClientPatch::QueueClientsByTarget(sender, ackreq, HoTT, ShouldSendTargetBuffs)( + &IBuff::MakeLegacyBuffsPacket, GetComponent, sender, true, clear_buffs); +} + +inline void SendCharmDroppedBuffsPacket(Mob* sender, bool ackreq = true) +{ + // dropping charm means that we want to remove the buff list from clients that shouldn't see non-pet buffs + ClientPatch::QueueClientsByTarget(sender, ackreq, false, [](Client* c) { return !ShouldSendTargetBuffs(c); })( + &IBuff::MakeLegacyBuffsPacket, GetComponent, sender, true, true); } } // namespace Buff diff --git a/zone/mob.h b/zone/mob.h index bd7d84a02..9055d7992 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -472,8 +472,6 @@ public: virtual uint32 GetLastBuffSlot(bool disc, bool song); virtual void InitializeBuffSlots() { buffs = nullptr; } virtual void UninitializeBuffSlots() { } - EQApplicationPacket *MakeBuffsPacket(bool for_target = true, bool clear_buffs = false); - void SendBuffsToClient(Client *c); inline Buffs_Struct* GetBuffs() { return buffs; } void DoGravityEffect(); void DamageShield(Mob* other, bool spell_ds = false); @@ -1303,7 +1301,6 @@ public: virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false); virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0); - //uint32 GetInstrumentMod(uint16 spell_id) const; uint32 GetInstrumentMod(uint16 spell_id); int64 CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0,uint16 casterid=0); int64 CalcSpellEffectValue_formula(uint32 formula, int64 base_value, int64 max_value, int caster_level, uint16 spell_id, int ticsremaining = 0); @@ -1368,6 +1365,7 @@ public: Timer* GetAIMovementTimer() { return AI_movement_timer.get(); } Timer GetAttackTimer() { return attack_timer; } Timer GetAttackDWTimer() { return attack_dw_timer; } + uint32 GetRemainingTicTime() const { return tic_timer.GetRemainingTime(); } inline bool IsFindable() { return findable; } inline uint8 GetManaPercent() { return (uint8)((float)current_mana / (float)max_mana * 100.0f); } virtual uint8 GetEndurancePercent() { return 0; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4930f1154..60ed0b15b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -33,6 +33,8 @@ #include +#include "client_version.h" + extern Zone* zone; extern volatile bool is_zone_loaded; @@ -150,11 +152,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (spells[spell_id].endurance_upkeep > 0) SetEndurUpkeep(true); - if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) - { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } + if (IsClient()) + Buff::SendLegacyBuffsPacket(CastToClient(), this, false); } if (IsClient()) { @@ -813,9 +812,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove // This was done in AddBuff, but we were not a pet yet, so // the target windows didn't get updated. - EQApplicationPacket *outapp = MakeBuffsPacket(); - entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQ::versions::maskSoDAndLater); - safe_delete(outapp); + Buff::SendLegacyBuffsPacketToClients(this); if(caster->IsClient()){ auto app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); @@ -4435,9 +4432,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) // no longer see the buffs on the old pet. // QueueClientsByTarget preserves GM and leadership cases. - EQApplicationPacket *outapp = MakeBuffsPacket(true, true); - - entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQ::versions::maskSoDAndLater, true, true); + Buff::SendCharmDroppedBuffsPacket(this); if (IsAIControlled()) { @@ -4664,32 +4659,14 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if(IsPet() && GetOwner() && GetOwner()->IsClient()) { SendPetBuffsToClient(); } - if((IsClient() && !CastToClient()->GetPVP()) || - (IsPet() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) || - (IsBot() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) || - (IsMerc() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP())) - { - EQApplicationPacket *outapp = MakeBuffsPacket(); - entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQ::versions::maskSoDAndLater); - if(IsClient() && GetTarget() == this) { - CastToClient()->QueuePacket(outapp); - } + Buff::SendLegacyBuffsPacketToClients(this); - safe_delete(outapp); - } + if (IsClient() && GetTarget() == this) + Buff::SendLegacyBuffsPacket(CastToClient(), this); - if (IsNPC()) { - EQApplicationPacket *outapp = MakeBuffsPacket(); - entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQ::versions::maskSoDAndLater, true); - safe_delete(outapp); - } - - if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) - { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } + if (IsClient()) + Buff::SendLegacyBuffsPacket(CastToClient(), this, false); // we will eventually call CalcBonuses() even if we skip it right here, so should correct itself if we still have them degenerating_effects = false; diff --git a/zone/spells.cpp b/zone/spells.cpp index 80dfffb99..f172a25db 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3744,26 +3744,10 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid if (IsPet() && GetOwner() && GetOwner()->IsClient()) SendPetBuffsToClient(); - if((IsClient() && !CastToClient()->GetPVP()) || - (IsPet() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) || - (IsBot() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) || - (IsMerc() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP())) - { - EQApplicationPacket *outapp = MakeBuffsPacket(); + Buff::SendLegacyBuffsPacketToClients(this); - entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQ::versions::maskSoDAndLater); - - if(IsClient() && GetTarget() == this) - CastToClient()->QueuePacket(outapp); - - safe_delete(outapp); - } - - if (IsNPC()) { - EQApplicationPacket *outapp = MakeBuffsPacket(); - entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQ::versions::maskSoDAndLater, true); - safe_delete(outapp); - } + if (IsClient() && GetTarget() == this) + Buff::SendLegacyBuffsPacket(CastToClient(), this); // recalculate bonuses since we stripped/added buffs CalcBonuses(); @@ -6615,76 +6599,6 @@ void Mob::SendPetBuffsToClient() safe_delete(outapp); } -void Mob::SendBuffsToClient(Client *c) -{ - if(!c) - return; - - if (c->ClientVersionBit() & EQ::versions::maskSoDAndLater) - { - EQApplicationPacket *outapp = MakeBuffsPacket(); - c->FastQueuePacket(&outapp); - } -} - -EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target, bool clear_buffs) -{ - uint32 count = 0; - uint32 buff_count; - - // for self we want all buffs, for target, we want to skip song window buffs - // since NPCs and pets don't have a song window, we still see it for them :P - if (for_target) { - buff_count = (clear_buffs) ? 0 : GetMaxBuffSlots(); - } - else { - buff_count = GetMaxTotalSlots(); - } - - for(int i = 0; i < buff_count; ++i) { - if (IsValidSpell(buffs[i].spellid)) { - ++count; - } - } - - EQApplicationPacket* outapp = nullptr; - - //Create it for a targeting window, else create it for a create buff packet. - if(for_target) { - outapp = new EQApplicationPacket(OP_RefreshTargetBuffs, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); - } - else { - outapp = new EQApplicationPacket(OP_RefreshBuffs, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); - } - BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; - buff->entity_id = GetID(); - buff->count = count; - buff->all_buffs = 1; - buff->tic_timer = tic_timer.GetRemainingTime(); - // there are more types, the client doesn't seem to really care though. The others are also currently hard to fill in here ... - // (see comment in common/eq_packet_structs.h) - if (for_target) - buff->type = IsClient() ? 5 : 7; - else - buff->type = 0; - - buff->name_lengths = 0; // hacky shit - uint32 index = 0; - for(int i = 0; i < buff_count; ++i) { - if (IsValidSpell(buffs[i].spellid)) { - buff->entries[index].buff_slot = i; - buff->entries[index].spell_id = buffs[i].spellid; - buff->entries[index].tics_remaining = buffs[i].ticsremaining; - buff->entries[index].num_hits = buffs[i].hit_number; - strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64); - buff->name_lengths += strlen(buff->entries[index].caster); - ++index; - } - } - - return outapp; -} - void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) { int buff_count = GetMaxTotalSlots();