Fix suppressed buff effect restoration

This commit is contained in:
Vayle 2026-03-09 15:13:24 -04:00
parent 4b0dab8377
commit 6b0cc98b06
3 changed files with 154 additions and 134 deletions

View File

@ -520,100 +520,13 @@ void Client::ReapplyBuff(uint32 index, bool from_suppress)
if (!IsValidSpell(buffs[index].spellid))
return;
const SPDat_Spell_Struct &spell = spells[buffs[index].spellid];
int NimbusEffect = GetSpellNimbusEffect(buffs[index].spellid);
if (NimbusEffect) {
if (!IsNimbusEffectActive(NimbusEffect))
SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true);
}
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
switch (spell.effect_id[x1]) {
case SpellEffect::Illusion: {
if (GetIllusionBlock()) {
break;
}
if (from_suppress || buffs[index].persistant_buff) {
Mob *caster = entity_list.GetMobID(buffs[index].casterid);
ApplySpellEffectIllusion(spell.id, caster, index, spell.base_value[x1], spell.limit_value[x1], spell.max_value[x1]);
}
break;
}
case SpellEffect::SummonHorse: {
if (!from_suppress && (RuleB(Character, PreventMountsFromZoning) || !zone->CanCastOutdoor())) {
BuffFadeByEffect(SpellEffect::SummonHorse);
} else {
SummonHorse(buffs[index].spellid);
}
break;
}
case SpellEffect::Silence:
{
Silence(true);
break;
}
case SpellEffect::Amnesia:
{
Amnesia(true);
break;
}
case SpellEffect::DivineAura:
{
invulnerable = true;
break;
}
case SpellEffect::Invisibility2:
case SpellEffect::Invisibility:
{
SendAppearancePacket(AppearanceType::Invisibility, Invisibility::Invisible);
break;
}
case SpellEffect::Levitate:
{
if (!zone->CanLevitate()) {
if (!GetGM()) {
SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SpellEffect::Levitate);
Message(Chat::Red, "You can't levitate in this zone.");
break;
}
Message(Chat::White, "Your GM flag allows you to levitate in this zone.");
}
SendAppearancePacket(
AppearanceType::FlyMode,
(
spell.limit_value[x1] == 1 ?
EQ::constants::GravityBehavior::LevitateWhileRunning :
EQ::constants::GravityBehavior::Levitating
),
true,
!from_suppress
);
break;
}
case SpellEffect::AddMeleeProc:
case SpellEffect::WeaponProc:
{
AddProcToWeapon(GetProcID(buffs[index].spellid, x1), false, 100 + spells[buffs[index].spellid].limit_value[x1], buffs[index].spellid, buffs[index].casterlevel, GetSpellProcLimitTimer(buffs[index].spellid, ProcType::MELEE_PROC));
break;
}
case SpellEffect::DefensiveProc:
{
AddDefensiveProc(GetProcID(buffs[index].spellid, x1), 100 + spells[buffs[index].spellid].limit_value[x1], buffs[index].spellid, GetSpellProcLimitTimer(buffs[index].spellid, ProcType::DEFENSIVE_PROC));
break;
}
case SpellEffect::RangedProc:
{
AddRangedProc(GetProcID(buffs[index].spellid, x1), 100 + spells[buffs[index].spellid].limit_value[x1], buffs[index].spellid, GetSpellProcLimitTimer(buffs[index].spellid, ProcType::RANGED_PROC));
break;
}
}
}
ReapplyBuffEffects(index, from_suppress);
}
// Finish client connecting state

View File

@ -452,6 +452,7 @@ public:
void BuffFadeNonPersistDeath();
void BuffFadeDetrimental();
void BuffFadeBySlot(int slot, bool iRecalcBonuses = true, bool suppress = false, uint32 suppresstics = 0);
void ReapplyBuffEffects(uint32 index, bool from_suppress = false);
void BuffFadeDetrimentalByCaster(Mob *caster);
void BuffFadeBySitModifier();
void BuffFadeSongs();

View File

@ -4220,6 +4220,155 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
}
// removes the buff in the buff slot 'slot'
void Mob::ReapplyBuffEffects(uint32 index, bool from_suppress)
{
if (!IsValidSpell(buffs[index].spellid)) {
return;
}
const auto &spell = spells[buffs[index].spellid];
bool refresh_weapon_stance = false;
for (int i = 0; i < EFFECT_COUNT; ++i) {
switch (spell.effect_id[i]) {
case SpellEffect::Illusion:
{
if (GetIllusionBlock()) {
break;
}
if (from_suppress || buffs[index].persistant_buff) {
Mob *caster = entity_list.GetMobID(buffs[index].casterid);
ApplySpellEffectIllusion(spell.id, caster, index, spell.base_value[i], spell.limit_value[i], spell.max_value[i]);
}
break;
}
case SpellEffect::SummonHorse:
{
if (!IsClient()) {
break;
}
if (!from_suppress && (RuleB(Character, PreventMountsFromZoning) || !zone->CanCastOutdoor())) {
BuffFadeByEffect(SpellEffect::SummonHorse);
} else {
CastToClient()->SummonHorse(buffs[index].spellid);
}
break;
}
case SpellEffect::Silence:
{
Silence(true);
break;
}
case SpellEffect::Amnesia:
{
Amnesia(true);
break;
}
case SpellEffect::DivineAura:
{
SetInvul(true);
break;
}
case SpellEffect::Invisibility2:
case SpellEffect::Invisibility:
{
if (IsClient()) {
SendAppearancePacket(AppearanceType::Invisibility, Invisibility::Invisible);
}
break;
}
case SpellEffect::Levitate:
{
if (!zone->CanLevitate()) {
if (IsClient()) {
if (CastToClient()->GetGM()) {
Message(Chat::White, "Your GM flag allows you to levitate in this zone.");
} else {
SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SpellEffect::Levitate);
Message(Chat::Red, "You can't levitate in this zone.");
break;
}
} else {
SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SpellEffect::Levitate);
break;
}
}
if (IsClient()) {
SendAppearancePacket(
AppearanceType::FlyMode,
(
spell.limit_value[i] == 1 ?
EQ::constants::GravityBehavior::LevitateWhileRunning :
EQ::constants::GravityBehavior::Levitating
),
true,
!from_suppress
);
} else {
SendAppearancePacket(
AppearanceType::FlyMode,
(
spell.limit_value[i] == 1 ?
EQ::constants::GravityBehavior::LevitateWhileRunning :
EQ::constants::GravityBehavior::Levitating
)
);
}
break;
}
case SpellEffect::AddMeleeProc:
case SpellEffect::WeaponProc:
{
AddProcToWeapon(GetProcID(buffs[index].spellid, i), false, 100 + spell.limit_value[i], buffs[index].spellid, buffs[index].casterlevel, GetSpellProcLimitTimer(buffs[index].spellid, ProcType::MELEE_PROC));
break;
}
case SpellEffect::DefensiveProc:
{
AddDefensiveProc(GetProcID(buffs[index].spellid, i), 100 + spell.limit_value[i], buffs[index].spellid, GetSpellProcLimitTimer(buffs[index].spellid, ProcType::DEFENSIVE_PROC));
break;
}
case SpellEffect::RangedProc:
{
AddRangedProc(GetProcID(buffs[index].spellid, i), 100 + spell.limit_value[i], buffs[index].spellid, GetSpellProcLimitTimer(buffs[index].spellid, ProcType::RANGED_PROC));
break;
}
case SpellEffect::BindSight:
{
auto *caster = entity_list.GetMobID(buffs[index].casterid);
if (caster && caster->IsClient()) {
caster->CastToClient()->SetBindSightTarget(this);
}
break;
}
case SpellEffect::SetBodyType:
{
SetBodyType(spell.base_value[i], false);
break;
}
case SpellEffect::Weapon_Stance:
{
if (IsClient()) {
refresh_weapon_stance = true;
}
break;
}
default:
break;
}
}
if (refresh_weapon_stance && IsClient()) {
CastToClient()->CalcBonuses();
CastToClient()->ApplyWeaponsStance();
}
}
void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses, bool suppress, uint32 suppresstics)
{
if(slot < 0 || slot > GetMaxTotalSlots())
@ -4694,59 +4843,16 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses, bool suppress, uint32 su
safe_delete(action_packet);
client->ReapplyBuff(slot, true);
} else if (IsPet() && GetOwner() && GetOwner()->IsClient()) {
// Reapply visual/state effects for client pets only
// Other non-client mobs (NPCs, bots, mercs) use the normal dispel mechanic
if (IsValidSpell(buffs[slot].spellid)) {
const auto& spell = spells[buffs[slot].spellid];
// Restore nimbus (visual aura) effect before processing individual spell effects,
// mirroring Client::ReapplyBuff behavior for client-owned pets.
if (spell.nimbus_effect > 0) {
SetNimbusEffect(static_cast<uint32>(spell.nimbus_effect));
}
for (int i = 0; i < EFFECT_COUNT; i++) {
switch (spell.effect_id[i]) {
case SpellEffect::Illusion:
ApplySpellEffectIllusion(spell.id, entity_list.GetMobID(buffs[slot].casterid), slot, spell.base_value[i], spell.limit_value[i], spell.max_value[i]);
break;
case SpellEffect::Silence:
Silence(true);
break;
case SpellEffect::Amnesia:
Amnesia(true);
break;
case SpellEffect::AddMeleeProc:
case SpellEffect::WeaponProc:
AddProcToWeapon(GetProcID(buffs[slot].spellid, i), false, 100 + spell.limit_value[i], buffs[slot].spellid, buffs[slot].casterlevel, GetSpellProcLimitTimer(buffs[slot].spellid, ProcType::MELEE_PROC));
break;
case SpellEffect::DefensiveProc:
AddDefensiveProc(GetProcID(buffs[slot].spellid, i), 100 + spell.limit_value[i], buffs[slot].spellid, GetSpellProcLimitTimer(buffs[slot].spellid, ProcType::DEFENSIVE_PROC));
break;
case SpellEffect::RangedProc:
AddRangedProc(GetProcID(buffs[slot].spellid, i), 100 + spell.limit_value[i], buffs[slot].spellid, GetSpellProcLimitTimer(buffs[slot].spellid, ProcType::RANGED_PROC));
break;
case SpellEffect::Levitate:
{
// Restore levitate visual effects after suppression expires
if (!zone->CanLevitate()) {
SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SpellEffect::Levitate);
} else {
if (spell.limit_value[i] == 1) {
SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::LevitateWhileRunning);
} else {
SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::Levitating);
}
}
break;
}
default:
break;
}
}
ReapplyBuffEffects(slot, true);
}
} else {
buffs[slot].spellid = SPELL_UNKNOWN;
}
} else {
buffs[slot].spellid = SPELL_UNKNOWN;
}
if(IsPet() && GetOwner() && GetOwner()->IsClient()) {