Add Buff infrastructure to go through different logic paths from different patches (#5073)
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run

This commit is contained in:
dannuic
2026-05-02 22:53:26 -06:00
committed by GitHub
parent c253734c57
commit 18df055f16
201 changed files with 2856 additions and 3105 deletions
+50 -86
View File
@@ -33,6 +33,8 @@
#include <cmath>
#include "client_version.h"
extern Zone* zone;
extern volatile bool is_zone_loaded;
@@ -41,7 +43,7 @@ extern WorldServer worldserver;
// the spell can still fail here, if the buff can't stack
// in this case false will be returned, true otherwise
bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness, int32 duration_override, bool disable_buff_overwrite)
bool Mob::SpellEffect(Mob* caster, int32 spell_id, float partial, int level_override, int reflect_effectiveness, int32 duration_override, bool disable_buff_overwrite)
{
int caster_level, buffslot, effect, effect_value, i;
EQ::ItemInstance *SummonedItem=nullptr;
@@ -150,11 +152,7 @@ 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);
}
ClientPatch::SendFullBuffRefresh(this);
}
if (IsClient()) {
@@ -813,9 +811,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);
ClientPatch::SendFullBuffRefresh(this);
if(caster->IsClient()){
auto app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct));
@@ -825,7 +821,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
ps->command = 1;
entity_list.QueueClients(this, app);
safe_delete(app);
SendPetBuffsToClient();
ClientPatch::SendFullBuffRefresh(this);
SendAppearancePacket(AppearanceType::Pet, caster->GetID(), true, true);
}
@@ -1614,7 +1610,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
GetPetType() != PetType::Charmed
)
{
uint16 pet_spellid = CastToNPC()->GetPetSpellID();
int32 pet_spellid = CastToNPC()->GetPetSpellID();
uint16 pet_ActSpellCost = caster->GetActSpellCost(pet_spellid, spells[pet_spellid].mana);
int16 ImprovedReclaimMod = caster->spellbonuses.ImprovedReclaimEnergy +
caster->itembonuses.ImprovedReclaimEnergy +
@@ -3024,7 +3020,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
if (caster && zone->random.Roll(spells[spell_id].base_value[i])) {
uint32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].limit_value[i]);
int32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].limit_value[i]);
if (IsValidSpell(best_spell_id))
caster->SpellFinished(best_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].resist_difficulty);
@@ -3441,7 +3437,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
return true;
}
int64 Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster,
int64 Mob::CalcSpellEffectValue(int32 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster,
int ticsremaining, uint16 caster_id)
{
if (!IsValidSpell(spell_id) || effect_id < 0 || effect_id >= EFFECT_COUNT)
@@ -3510,7 +3506,7 @@ int64 Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level
}
// generic formula calculations
int64 Mob::CalcSpellEffectValue_formula(uint32 formula, int64 base_value, int64 max_value, int caster_level, uint16 spell_id, int ticsremaining)
int64 Mob::CalcSpellEffectValue_formula(uint32 formula, int64 base_value, int64 max_value, int caster_level, int32 spell_id, int ticsremaining)
{
#ifdef LUA_EQEMU
int64 lua_ret = 0;
@@ -3868,19 +3864,15 @@ void Mob::BuffProcess()
}
}
if(IsClient())
{
if(buffs[buffs_i].UpdateClient == true)
{
CastToClient()->SendBuffDurationPacket(buffs[buffs_i], buffs_i);
// Hack to get UF to play nicer, RoF seems fine without it
if (CastToClient()->ClientVersion() == EQ::versions::ClientVersion::UF && buffs[buffs_i].hit_number > 0)
CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i);
buffs[buffs_i].UpdateClient = false;
}
// this is for older clients. Newer clients will simply discard this packet
if (IsClient() && buffs[buffs_i].UpdateClient == true) {
ClientPatch::SendSingleBuffChange(this, buffs[buffs_i], buffs_i);
buffs[buffs_i].UpdateClient = false;
}
}
}
ClientPatch::SendFullBuffRefresh(this);
}
void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
@@ -4251,9 +4243,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
if(!IsValidSpell(buffs[slot].spellid))
return;
if (IsClient() && !CastToClient()->IsDead())
CastToClient()->MakeBuffFadePacket(buffs[slot].spellid, slot);
LogSpells("Fading buff [{}] from slot [{}]", buffs[slot].spellid, slot);
const auto has_fade_event = parse->SpellHasQuestSub(buffs[slot].spellid, EVENT_SPELL_FADE);
@@ -4431,13 +4420,11 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
owner->SetPet(0);
}
// Any client that has a previous charmed pet targetted shouldo
// Any client that has a previous charmed pet targeted should
// 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);
ClientPatch::SendFullBuffRefresh(this, true);
if (IsAIControlled())
{
@@ -4660,36 +4647,13 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
if (spells[buffs[slot].spellid].nimbus_effect > 0)
RemoveNimbusEffect(spells[buffs[slot].spellid].nimbus_effect);
// the client expects remaining duration to be 0 in the single packet change
buffs[slot].ticsremaining = 0;
ClientPatch::SendSingleBuffChange(this, buffs[slot], slot, true);
// don't set the spell to unknown until after the server has sent the single remove packets
buffs[slot].spellid = SPELL_UNKNOWN;
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);
}
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() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater)
{
EQApplicationPacket *outapp = MakeBuffsPacket(false);
CastToClient()->FastQueuePacket(&outapp);
}
ClientPatch::SendFullBuffRefresh(this);
// we will eventually call CalcBonuses() even if we skip it right here, so should correct itself if we still have them
degenerating_effects = false;
@@ -4697,7 +4661,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
CalcBonuses();
}
int64 Mob::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
int64 Mob::CalcAAFocus(focusType type, const AA::Rank &rank, int32 spell_id)
{
const SPDat_Spell_Struct &spell = spells[spell_id];
@@ -5450,7 +5414,7 @@ int64 Mob::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
//given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any
//assumes that spell_id is not a bard spell and that both ids are valid spell ids
int64 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid, Mob *caster)
int64 Mob::CalcFocusEffect(focusType type, uint16 focus_id, int32 spell_id, bool best_focus, uint16 casterid, Mob *caster)
{
/*
'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target.
@@ -5472,7 +5436,7 @@ int64 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
int lvlModifier = 100;
int spell_level = 0;
int lvldiff = 0;
uint32 Caston_spell_id = 0;
int32 Caston_spell_id = 0;
int index_id = -1;
uint32 focus_reuse_time = 0; //If this is set and all limits pass, start timer at end of script.
@@ -6266,7 +6230,7 @@ int64 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
return (value * lvlModifier / 100);
}
void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id)
void Mob::TryTriggerOnCastFocusEffect(focusType type, int32 spell_id)
{
if (IsBardSong(spell_id)) {
return;
@@ -6367,7 +6331,7 @@ void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id)
}
}
bool Mob::TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc_spellid)
bool Mob::TryTriggerOnCastProc(int32 focusspellid, int32 spell_id, int32 proc_spellid)
{
// We confirm spell_id and focuspellid are valid before passing into this.
if (IsValidSpell(proc_spellid) && spell_id != focusspellid && spell_id != proc_spellid) {
@@ -6385,12 +6349,12 @@ bool Mob::TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc
return false;
}
uint16 Mob::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
int32 Mob::GetSympatheticFocusEffect(focusType type, int32 spell_id) {
if (IsBardSong(spell_id))
return 0;
uint16 proc_spellid = 0;
int32 proc_spellid = 0;
float ProcChance = 0.0f;
std::vector<int> SympatheticProcList;
@@ -6444,7 +6408,7 @@ uint16 Mob::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
//Spell Focus
if (spellbonuses.FocusEffects[type]){
uint16 focusspellid = 0;
int32 focusspellid = 0;
int buff_max = GetMaxTotalSlots();
for (int buff_slot = 0; buff_slot < buff_max; buff_slot++) {
@@ -6508,7 +6472,7 @@ uint16 Mob::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
return 0;
}
int64 Mob::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster, bool from_buff_tic)
int64 Mob::GetFocusEffect(focusType type, int32 spell_id, Mob *caster, bool from_buff_tic)
{
if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration && type != focusReduceRecastTime) {
return 0;
@@ -6882,7 +6846,7 @@ int64 Mob::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster, bool fro
return realTotal + realTotal2 + realTotal3 + worneffect_bonus;
}
int64 NPC::GetFocusEffect(focusType type, uint16 spell_id, Mob* caster, bool from_buff_tic) {
int64 NPC::GetFocusEffect(focusType type, int32 spell_id, Mob* caster, bool from_buff_tic) {
int64 realTotal = 0;
int64 realTotal2 = 0;
@@ -7005,7 +6969,7 @@ int64 NPC::GetFocusEffect(focusType type, uint16 spell_id, Mob* caster, bool fro
return realTotal + realTotal2;
}
void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id)
void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, int32 spell_id)
{
/*
Field 175 = numhits type
@@ -7047,7 +7011,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id)
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
ClientPatch::SendSingleBuffChange(this, buffs[d], d);
}
}
}
@@ -7062,7 +7026,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id)
if (!TryFadeEffect(buff_slot))
BuffFadeBySlot(buff_slot , true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[buff_slot], buff_slot);
ClientPatch::SendSingleBuffChange(this, buffs[buff_slot], buff_slot);
}
}
}
@@ -7080,7 +7044,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id)
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
ClientPatch::SendSingleBuffChange(this, buffs[d], d);
}
}
}
@@ -7108,7 +7072,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id)
}
//for some stupid reason SK procs return theirs one base off...
uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index)
int32 Mob::GetProcID(int32 spell_id, uint8 effect_index)
{
if (!RuleB(Spells, SHDProcIDOffByOne)) // UF+ spell files
return spells[spell_id].base_value[effect_index];
@@ -7300,7 +7264,7 @@ bool Mob::AffectedBySpellExcludingSlot(int slot, int effect)
return false;
}
float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate) {
float Mob::GetSympatheticProcChances(int32 spell_id, int16 ProcRateMod, int32 ItemProcRate) {
float ProcChance = 0.0f;
int32 total_cast_time = 0;
@@ -7326,7 +7290,7 @@ float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 I
return ProcChance;
}
int16 Mob::GetSympatheticSpellProcRate(uint16 spell_id)
int16 Mob::GetSympatheticSpellProcRate(int32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; i++){
if (spells[spell_id].effect_id[i] == SpellEffect::SympatheticProc)
@@ -7336,7 +7300,7 @@ int16 Mob::GetSympatheticSpellProcRate(uint16 spell_id)
return 0;
}
uint16 Mob::GetSympatheticSpellProcID(uint16 spell_id)
int32 Mob::GetSympatheticSpellProcID(int32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; i++){
if (spells[spell_id].effect_id[i] == SpellEffect::SympatheticProc)
@@ -7369,7 +7333,7 @@ int64 Mob::GetFcDamageAmtIncoming(Mob *caster, int32 spell_id, bool from_buff_ti
return dmg;
}
int64 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id) {
int64 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, int32 spell_id) {
//**** This can be removed when bot healing focus code is updated ****
@@ -7441,7 +7405,7 @@ bool Mob::PassLimitClass(uint32 Classes_, uint16 Class_)
return false;
}
void Mob::DispelMagic(Mob* caster, uint16 spell_id, int effect_value)
void Mob::DispelMagic(Mob* caster, int32 spell_id, int effect_value)
{
for (int slot = 0; slot < GetMaxTotalSlots(); slot++) {
if (
@@ -7457,7 +7421,7 @@ void Mob::DispelMagic(Mob* caster, uint16 spell_id, int effect_value)
}
}
bool Mob::TrySpellEffectResist(uint16 spell_id)
bool Mob::TrySpellEffectResist(int32 spell_id)
{
/*
SEResist variable
@@ -9614,7 +9578,7 @@ void Mob::SendCastRestrictionMessage(int requirement_id, bool target_requirement
}
}
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) {
bool Mob::TrySpellProjectile(Mob* spell_target, int32 spell_id, float speed) {
/*For mage 'Bolt' line and other various spells.
-This is mostly accurate for how the modern clients handle this effect.
@@ -9722,7 +9686,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) {
return true;
}
void Mob::ResourceTap(int64 damage, uint16 spellid)
void Mob::ResourceTap(int64 damage, int32 spellid)
{
//'this' = caster
if (!IsValidSpell(spellid))
@@ -9774,7 +9738,7 @@ void Mob::TryTriggerThreshHold(int64 damage, int effect_id, Mob* attacker){
if (spells[buffs[slot].spellid].effect_id[i] == effect_id){
uint16 spell_id = spells[buffs[slot].spellid].base_value[i];
int32 spell_id = spells[buffs[slot].spellid].base_value[i];
if (damage > spells[buffs[slot].spellid].limit_value[i]){
@@ -9842,7 +9806,7 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id)
}
}
void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster)
void Mob::CalcSpellPowerDistanceMod(int32 spell_id, float range, Mob* caster)
{
if (IsDistanceModifierSpell(spell_id)){
@@ -10097,7 +10061,7 @@ int Mob::GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool
return zone->random.Int(focus_base, focus_base2);
}
bool Mob::NegateSpellEffect(uint16 spell_id, int effect_id)
bool Mob::NegateSpellEffect(int32 spell_id, int effect_id)
{
/*
This works for most effects, anything handled purely by the client will bypass this (ie Gate, Shadowstep)