diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2c1537d10..92fdcfe57 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -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 diff --git a/zone/mob.h b/zone/mob.h index 6433b0ead..bd1e54971 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -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(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6417a0efe..f8adf2874 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4219,6 +4219,142 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) CalcBonuses(); } +// Re-applies runtime-only buff effects after unsuppression or zone-in +void Mob::ReapplyBuffEffects(uint32 index, bool from_suppress) +{ + if (index >= GetMaxTotalSlots() || !IsValidSpell(buffs[index].spellid)) { + return; + } + + // Snapshot the spell ID before the loop so that if BuffFadeByEffect + // clears buffs[index].spellid mid-iteration the remaining effect + // handlers still reference the correct spell. + const uint32 spell_id = buffs[index].spellid; + const auto &spell = spells[spell_id]; + 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); + return; + } else { + CastToClient()->SummonHorse(spell_id); + } + break; + } + case SpellEffect::Silence: + { + Silence(true); + break; + } + case SpellEffect::Amnesia: + { + Amnesia(true); + break; + } + case SpellEffect::DivineAura: + { + if (IsClient()) { + 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."); + return; + } + } else { + SendAppearancePacket(AppearanceType::FlyMode, 0); + BuffFadeByEffect(SpellEffect::Levitate); + return; + } + } + + auto fly_mode = (spell.limit_value[i] == 1) + ? EQ::constants::GravityBehavior::LevitateWhileRunning + : EQ::constants::GravityBehavior::Levitating; + if (IsClient()) { + SendAppearancePacket(AppearanceType::FlyMode, fly_mode, true, !from_suppress); + } else { + SendAppearancePacket(AppearanceType::FlyMode, fly_mode); + } + + break; + } + case SpellEffect::AddMeleeProc: + case SpellEffect::WeaponProc: + { + AddProcToWeapon(GetProcID(spell_id, i), false, 100 + spell.limit_value[i], spell_id, buffs[index].casterlevel, GetSpellProcLimitTimer(spell_id, ProcType::MELEE_PROC)); + break; + } + case SpellEffect::DefensiveProc: + { + AddDefensiveProc(GetProcID(spell_id, i), 100 + spell.limit_value[i], spell_id, GetSpellProcLimitTimer(spell_id, ProcType::DEFENSIVE_PROC)); + break; + } + case SpellEffect::RangedProc: + { + AddRangedProc(GetProcID(spell_id, i), 100 + spell.limit_value[i], spell_id, GetSpellProcLimitTimer(spell_id, ProcType::RANGED_PROC)); + 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(); + } +} + // removes the buff in the buff slot 'slot' void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses, bool suppress, uint32 suppresstics) { @@ -4694,55 +4830,12 @@ 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(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 { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9885af113..7683961f7 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3053,6 +3053,13 @@ void ZoneDatabase::LoadBuffs(Client *client) for (int slot_id = 0; slot_id < max_buff_slots; ++slot_id) { if (buffs[slot_id].spellid == SPELL_SUPPRESSED) { + // Suppressed illusions should drop on zone/relog rather than being unsuppressed later + if (IsValidSpell(buffs[slot_id].suppressedid) && + IsEffectInSpell(buffs[slot_id].suppressedid, SpellEffect::Illusion)) { + buffs[slot_id].spellid = SPELL_UNKNOWN; + buffs[slot_id].suppressedid = SPELL_UNKNOWN; + buffs[slot_id].suppressedticsremaining = -1; + } continue; }