diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index d09e8e5e9b..a833b3261e 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3103,7 +3103,10 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const // PvP case - can't attack when attacker or target are in sanctuary // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp - if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unitOrOwner && unitOrOwner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && (unitTarget->IsInSanctuary() || unitOrOwner->IsInSanctuary())) + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) + && unitOrOwner && unitOrOwner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) + && (unitTarget->IsInSanctuary() || unitOrOwner->IsInSanctuary()) + && (!bySpell || bySpell->HasAttribute(SPELL_ATTR8_IGNORE_SANCTUARY))) return false; // additional checks - only PvP case @@ -3206,8 +3209,8 @@ bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const return false; // can't assist player out of sanctuary from sanctuary if has pvp enabled - if (unitTarget->IsPvP()) - if (unit && unit->IsInSanctuary() && !unitTarget->IsInSanctuary()) + if (unitTarget->IsPvP() && (!bySpell || bySpell->HasAttribute(SPELL_ATTR8_IGNORE_SANCTUARY))) + if (unit->IsInSanctuary() && !unitTarget->IsInSanctuary()) return false; } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b5d978a0e8..41bb98efaf 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3127,7 +3127,7 @@ bool Player::HandlePassiveSpellLearn(SpellInfo const* spellInfo) } } - if (spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION)) + if (spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_AFFECTS_POINTS)) need_cast &= IsCurrentSpecMasterySpell(spellInfo); //Check CasterAuraStates @@ -26941,7 +26941,7 @@ void Player::UpdateArmorSpecialization() for (AuraApplicationMap::iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end();) { SpellInfo const* spell = itr->second->GetBase()->GetSpellInfo(); - if (spell->AttributesEx8 & SPELL_ATTR8_ARMOR_SPECIALIZATION) + if (spell->AttributesEx8 & SPELL_ATTR8_REQUIRES_EQUIPPED_INV_TYPES) RemoveAura(itr); else ++itr; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index bea9788d7b..0c1d73e933 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2305,7 +2305,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo bool canDodge = !spellInfo->HasAttribute(SPELL_ATTR7_NO_ATTACK_DODGE); bool canParry = !spellInfo->HasAttribute(SPELL_ATTR7_NO_ATTACK_PARRY); - bool canBlock = !spellInfo->HasAttribute(SPELL_ATTR8_CANT_BLOCK); + bool canBlock = !spellInfo->HasAttribute(SPELL_ATTR8_NO_ATTACK_BLOCK); // if victim is casting or cc'd it can't avoid attacks bool canUseDefenseWhileChanneling = victim->GetCurrentSpell(CURRENT_CHANNELED_SPELL) ? victim->GetCurrentSpell(CURRENT_CHANNELED_SPELL)->GetSpellInfo()->HasAttribute(SPELL_ATTR10_ALLOW_DEFENSE_WHILE_CHANNELING) : false; @@ -14277,7 +14277,7 @@ float Unit::GetCollisionHeight() const return collisionHeight == 0.0f ? DEFAULT_COLLISION_HEIGHT : collisionHeight; } -SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo) const +SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo, TriggerCastFlags& triggerFlag) const { Unit::AuraEffectList swaps = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS); Unit::AuraEffectList const& swaps2 = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_TRIGGERED); @@ -14287,8 +14287,15 @@ SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo) const for (AuraEffect const* auraEffect : swaps) { if (uint32(auraEffect->GetMiscValue()) == spellInfo->Id || auraEffect->IsAffectingSpell(spellInfo)) + { if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(auraEffect->GetAmount())) + { + if (auraEffect->GetSpellInfo()->HasAttribute(SPELL_ATTR8_IGNORE_SPELLCAST_OVERRIDE_COST)) + triggerFlag |= TRIGGERED_IGNORE_POWER_AND_REAGENT_COST; + return newInfo; + } + } } return spellInfo; @@ -14403,7 +14410,9 @@ void Unit::ProcessPendingSpellCastRequest() } } - if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->IsRaidMarker() && + TriggerCastFlags triggerFlag = TRIGGERED_NONE; + + if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->HasAttribute(SPELL_ATTR8_SKIP_IS_KNOWN_CHECK) && !caster->ToPlayer()->HasArchProject(static_cast(spellInfo->ResearchProjectId))) { bool allow = false; @@ -14431,7 +14440,7 @@ void Unit::ProcessPendingSpellCastRequest() } // Check possible spell cast overrides - spellInfo = caster->GetCastSpellInfo(spellInfo); + spellInfo = caster->GetCastSpellInfo(spellInfo, triggerFlag); // can't use our own spells when we're in possession of another unit, if (caster->isPossessing()) @@ -14493,7 +14502,7 @@ void Unit::ProcessPendingSpellCastRequest() player->SetArchData(archaeologyCastData); } - Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE); + Spell* spell = new Spell(caster, spellInfo, triggerFlag); spell->m_cast_count = _pendingSpellCastRequest->CastRequest.CastID; // set count of casts spell->m_glyphIndex = _pendingSpellCastRequest->CastRequest.Misc; spell->prepare(targets); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 4875d4c528..9bc0fa9246 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1438,7 +1438,7 @@ class TC_GAME_API Unit : public WorldObject Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; } Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; int32 GetCurrentSpellCastTime(uint32 spell_id) const; - SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const; + SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo, TriggerCastFlags& triggerFlag) const; virtual bool HasSpellFocus(Spell const* /*focusSpell*/ = nullptr) const { return false; } virtual bool IsMovementPreventedByCasting() const; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 8f2eaa76a7..bf118f0de4 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -646,40 +646,40 @@ enum SpellAttr7 : uint32 SPELL_ATTR7_ALWAYS_CAST_LOG = 0x80000000 // TITLE Always Cast Log }; -enum SpellAttr8 +enum SpellAttr8 : uint32 { - SPELL_ATTR8_CANT_BLOCK = 0x00000001, // 0 No Attack Block - SPELL_ATTR8_UNK1 = 0x00000002, // 1 - SPELL_ATTR8_UNK2 = 0x00000004, // 2 - SPELL_ATTR8_UNK3 = 0x00000008, // 3 - SPELL_ATTR8_UNK4 = 0x00000010, // 4 - SPELL_ATTR8_UNK5 = 0x00000020, // 5 - SPELL_ATTR8_UNK6 = 0x00000040, // 6 - SPELL_ATTR8_UNK7 = 0x00000080, // 7 - SPELL_ATTR8_AFFECT_PARTY_AND_RAID = 0x00000100, // 8 Nearly all spells have "all party and raid" in description - SPELL_ATTR8_PERIODIC_CAN_CRIT = 0x00000200, // 9 Periodic Can Crit - SPELL_ATTR8_NAME_CHANGED_DURING_TRANSFORM = 0x00000400, // 10 according to wowhead comments, name changes, title remains - SPELL_ATTR8_UNK11 = 0x00000800, // 11 - SPELL_ATTR8_AURA_SEND_AMOUNT = 0x00001000, // 12 Aura must have flag AFLAG_ANY_EFFECT_AMOUNT_SENT to send amount - SPELL_ATTR8_UNK13 = 0x00002000, // 13 - SPELL_ATTR8_UNK14 = 0x00004000, // 14 - SPELL_ATTR8_WATER_MOUNT = 0x00008000, // 15 only one River Boat used in Thousand Needles - SPELL_ATTR8_UNK16 = 0x00010000, // 16 - SPELL_ATTR8_HASTE_AFFECTS_DURATION = 0x00020000, // 17 Haste Affects Duration - SPELL_ATTR8_REMEMBER_SPELLS = 0x00040000, // 18 at some point in time, these auras remember spells and allow to cast them later - SPELL_ATTR8_USE_COMBO_POINTS_ON_ANY_TARGET = 0x00080000, // 19 allows to consume combo points from dead targets - SPELL_ATTR8_ARMOR_SPECIALIZATION = 0x00100000, // 20 - SPELL_ATTR8_UNK21 = 0x00200000, // 21 - SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC = 0x00400000, // 22 Melee Haste Affects Periodic - SPELL_ATTR8_BATTLE_RESURRECTION = 0x00800000, // 23 Used to limit the Amount of Resurrections in Boss Encounters - SPELL_ATTR8_HEAL_PREDICTION = 0x01000000, // 24 Displays a predicted health amount on the targets frame - SPELL_ATTR8_UNK25 = 0x02000000, // 25 - SPELL_ATTR8_RAID_MARKER = 0x04000000, // 26 probably spell no need learn to cast - SPELL_ATTR8_AI_FACE_TARGET = 0x08000000, // 27 AI Face Target - SPELL_ATTR8_NOT_IN_BG_OR_ARENA = 0x10000000, // 28 not allow to cast or deactivate currently active effect, not sure about Fast Track - SPELL_ATTR8_MASTERY_SPECIALIZATION = 0x20000000, // 29 - SPELL_ATTR8_UNK30 = 0x40000000, // 30 - SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG = 0x80000000 // 31 Do not check UNIT_FLAG_IMMUNE_TO_PC in IsValidAttackTarget + SPELL_ATTR8_NO_ATTACK_BLOCK = 0x00000001, // TITLE No Attack Block + SPELL_ATTR8_IGNORE_DYNAMIC_OBJECT_CASTER = 0x00000002, /*NYI*/ // TITLE Ignore Dynamic Object Caster + SPELL_ATTR8_REMOVE_OUTSIDE_DUNGEONS_AND_RAIDS = 0x00000004, // TITLE Remove Outside Dungeons and Raids + SPELL_ATTR8_ONLY_TARGET_IF_SAME_CREATOR = 0x00000008, // TITLE Only Target If Same Creator + SPELL_ATTR8_CAN_HIT_AOE_UNTARGETABLE = 0x00000010, /*NYI - no aoe immunity implementation*/ // TITLE Can Hit AOE Untargetable + SPELL_ATTR8_ALLOW_WHILE_CHARMED = 0x00000020, /*NYI - not implementable currently, charming replaces AI*/ // TITLE Allow While Charmed + SPELL_ATTR8_AURA_REQUIRED_BY_CLIENT = 0x00000040, /*NYI - we send all auras to client*/ // TITLE Aura Required by Client + SPELL_ATTR8_IGNORE_SANCTUARY = 0x00000080, // TITLE Ignore Sanctuary + SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING = 0x00000100, // TITLE Use Target's Level for Spell Scaling + SPELL_ATTR8_PERIODIC_CAN_CRIT = 0x00000200, // TITLE Periodic Can Crit + SPELL_ATTR8_MIRROR_CREATURE_NAME = 0x00000400, // TITLE Mirror creature name DESCRIPTION Transform auras also override name (handled clientside) + SPELL_ATTR8_ONLY_PLAYERS_CAN_CAST_THIS_SPELL = 0x00000800, // TITLE Only Players Can Cast This Spell + SPELL_ATTR8_AURA_POINTS_ON_CLIENT = 0x00001000, // TITLE Aura Points On Client + SPELL_ATTR8_NOT_IN_SPELLBOOK_UNTIL_LEARNED = 0x00002000, // TITLE Not In Spellbook Until Learned DESCRIPTION Hides autolearned spell from spellbook before learning (handled clientside) + SPELL_ATTR8_TARGET_PROCS_ON_CASTER = 0x00004000, // TITLE Target Procs On Caster DESCRIPTION Target (taken) procs happen on caster (actor) instead of aura target (action target) + SPELL_ATTR8_REQUIRES_LOCATION_TO_BE_ON_LIQUID_SURFACE = 0x00008000, // TITLE Requires location to be on liquid surface + SPELL_ATTR8_ONLY_TARGET_OWN_SUMMONS = 0x00010000, // TITLE Only Target Own Summons + SPELL_ATTR8_HASTE_AFFECTS_DURATION = 0x00020000, // TITLE Haste Affects Duration + SPELL_ATTR8_IGNORE_SPELLCAST_OVERRIDE_COST = 0x00040000, // TTILE Ignore Spellcast Override Cost + SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING = 0x00080000, /*NYI - no spawn tracking implementation*/ // TITLE Allow Targets Hidden by Spawn Tracking + SPELL_ATTR8_REQUIRES_EQUIPPED_INV_TYPES = 0x00100000, // TITLE Requires Equipped Inv Types + SPELL_ATTR8_NO_SUMMON_DEST_FROM_CLIENT_TARGETING_PATHING_REQUIREMENT = 0x00200000, /*NYI - vald path to a spell dest is not required currently if the dest comes from client*/ // TITLE No 'Summon + Dest from Client' Targeting Pathing Requirement + SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC = 0x00400000, // TITLE Melee Haste Affects Periodic + SPELL_ATTR8_ENFORCE_IN_COMBAT_RESSURECTION_LIMIT = 0x00800000, // TITLE Enforce In Combat Ressurection Limit DESCRIPTION Used to limit the number of resurrections in boss encounters + SPELL_ATTR8_HEAL_PREDICTION = 0x01000000, // TITLE Heal Prediction + SPELL_ATTR8_NO_LEVEL_UP_TOAST = 0x02000000, // TITLE No Level Up Toast + SPELL_ATTR8_SKIP_IS_KNOWN_CHECK = 0x04000000, // TITLE Skip Is Known Check + SPELL_ATTR8_AI_FACE_TARGET = 0x08000000, /*NYI - unknown facing conditions, needs research*/ // TITLE AI Face Target + SPELL_ATTR8_NOT_IN_BATTLEGROUND = 0x10000000, // TITLE Not in Battleground + SPELL_ATTR8_MASTERY_AFFECTS_POINTS = 0x20000000, // TITLE Mastery Affects Points + SPELL_ATTR8_DISPLAY_LARGE_AURA_ICON_ON_UNIT_FRAMES_BOSS_AURA = 0x40000000, // TITLE Display Large Aura Icon On Unit Frames (Boss Aura) + SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG = 0x80000000 // TITLE Can Attack ImmunePC DESCRIPTION Do not check UNIT_FLAG_IMMUNE_TO_PC in IsValidAttackTarget }; enum SpellAttr9 diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp index 0b2ea7f788..eb34d7a619 100644 --- a/src/server/game/Server/Packets/SpellPackets.cpp +++ b/src/server/game/Server/Packets/SpellPackets.cpp @@ -23,7 +23,7 @@ ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellHealPredicti { data << int32(predict.Points); data << uint8(predict.Type); - if (predict.BeaconGUID.has_value()) + if (predict.Type == SPELL_HEAL_PREDICTION_TARGET_AND_BEACON) data << predict.BeaconGUID->WriteAsPacked(); return data; } @@ -193,22 +193,22 @@ ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::TargetedHealPredi ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::ChannelStartInterruptImmunities const& immunity) { - data << immunity.SchoolImmunities; - data << immunity.Immunities; + data << int32(immunity.SchoolImmunities); + data << int32(immunity.Immunities); return data; } WorldPacket const* WorldPackets::Spells::ChannelStart::Write() { _worldPacket << CasterGUID.WriteAsPacked(); - _worldPacket << uint32(SpellID); - _worldPacket << int32(ChannelDuration); + _worldPacket << int32(SpellID); + _worldPacket << uint32(ChannelDuration); - _worldPacket << uint8(InterruptImmunities.has_value()); + _worldPacket << bool(InterruptImmunities.has_value()); if (InterruptImmunities) _worldPacket << *InterruptImmunities; - _worldPacket << uint8(HealPrediction.has_value()); + _worldPacket << bool(HealPrediction.has_value()); if (HealPrediction) _worldPacket << *HealPrediction; diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h index 8ce635d617..dbe522402e 100644 --- a/src/server/game/Server/Packets/SpellPackets.h +++ b/src/server/game/Server/Packets/SpellPackets.h @@ -129,9 +129,6 @@ namespace WorldPackets struct TargetedHealPrediction { - TargetedHealPrediction() { } - TargetedHealPrediction(ObjectGuid targetGuid, SpellHealPrediction prediction) : TargetGUID(targetGuid), Predict(prediction) { } - ObjectGuid TargetGUID; SpellHealPrediction Predict; }; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 68a101016d..11c36b00ed 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -483,7 +483,7 @@ int32 AuraEffect::CalculateAmount(Unit* caster) // default amount calculation int32 amount = 0; - if (!m_spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION) || G3D::fuzzyEq(m_spellInfo->Effects[m_effIndex].BonusMultiplier, 0.0f)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_AFFECTS_POINTS) || G3D::fuzzyEq(m_spellInfo->Effects[m_effIndex].BonusMultiplier, 0.0f)) amount = m_spellInfo->Effects[m_effIndex].CalcValue(caster, &m_baseAmount, GetBase()->GetOwner()->ToUnit()); else if (caster && caster->GetTypeId() == TYPEID_PLAYER) amount = int32(caster->GetFloatValue(PLAYER_MASTERY) * m_spellInfo->Effects[m_effIndex].BonusMultiplier); @@ -770,7 +770,7 @@ void AuraEffect::ChangeAmount(int32 newAmount, bool mark, bool onStackOrReapply) HandleEffect(aurApp, handleMask, true); } - if (GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT) || Aura::EffectTypeNeedsSendingAmount(GetAuraType())) + if (GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_POINTS_ON_CLIENT) || Aura::EffectTypeNeedsSendingAmount(GetAuraType())) GetBase()->SetNeedClientUpdateForTargets(); } @@ -1318,7 +1318,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (!spellInfo || !(spellInfo->IsPassive() || spellInfo->HasAttribute(SPELL_ATTR0_DO_NOT_DISPLAY_SPELLBOOK_AURA_ICON_COMBAT_LOG))) continue; - if (spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION) && !plrTarget->IsCurrentSpecMasterySpell(spellInfo)) + if (spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_AFFECTS_POINTS) && !plrTarget->IsCurrentSpecMasterySpell(spellInfo)) continue; if (spellInfo->Stances & (UI64LIT(1) << (GetMiscValue() - 1))) @@ -6245,6 +6245,8 @@ void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEve { Unit* triggerCaster = aurApp->GetTarget(); Unit* triggerTarget = eventInfo.GetProcTarget(); + if (GetSpellInfo()->HasAttribute(SPELL_ATTR8_TARGET_PROCS_ON_CASTER) && eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) + triggerTarget = eventInfo.GetActor(); uint32 triggerSpellId = GetSpellInfo()->Effects[GetEffIndex()].TriggerSpell; if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId)) @@ -6260,6 +6262,9 @@ void AuraEffect::HandleProcTriggerSpellCopyAuraProc(AuraApplication* aurApp, Pro { Unit* triggerCaster = aurApp->GetTarget(); Unit* triggerTarget = eventInfo.GetProcTarget(); + if (GetSpellInfo()->HasAttribute(SPELL_ATTR8_TARGET_PROCS_ON_CASTER) && eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) + triggerTarget = eventInfo.GetActor(); + SpellInfo const* triggeredSpellInfo = eventInfo.GetSpellInfo(); if (!triggeredSpellInfo) return; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index c918a401f5..0c860a39f9 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -173,7 +173,7 @@ void AuraApplication::_InitFlags(Unit* caster, uint8 effMask) return false; }(); - if (GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT) || effectNeedsAmount) + if (GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_POINTS_ON_CLIENT) || effectNeedsAmount) _flags |= AFLAG_SCALABLE; } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 4ea468ee81..a035f4edf4 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -918,6 +918,22 @@ void Spell::SelectSpellTargets() } } + if (m_targets.HasDst()) + { + if (m_spellInfo->HasAttribute(SPELL_ATTR8_REQUIRES_LOCATION_TO_BE_ON_LIQUID_SURFACE)) + { + ZLiquidStatus status = m_caster->GetMap()->GetLiquidStatus(m_caster->GetPhaseShift(), + m_targets.GetDstPos()->GetPositionX(), m_targets.GetDstPos()->GetPositionY(), m_targets.GetDstPos()->GetPositionZ(), + map_liquidHeaderTypeFlags::AllLiquids); + if (!(status & (LIQUID_MAP_WATER_WALK | LIQUID_MAP_IN_WATER))) + { + SendCastResult(SPELL_FAILED_NO_LIQUID); + finish(false); + return; + } + } + } + if (uint64 dstDelay = CalculateDelayMomentForDst()) m_delayMoment = dstDelay; } @@ -2583,7 +2599,7 @@ void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) if (unit->IsAlive() != IsAlive) return; - if (spell->getState() == SPELL_STATE_DELAYED && !spell->IsPositive() && (GameTime::GetGameTimeMS() - TimeDelay) <= unit->m_lastSanctuaryTime) + if (!spell->m_spellInfo->HasAttribute(SPELL_ATTR8_IGNORE_SANCTUARY) && spell->getState() == SPELL_STATE_DELAYED && !spell->IsPositive() && (GameTime::GetGameTimeMS() - TimeDelay) <= unit->m_lastSanctuaryTime) return; // No missinfo in that case if (_spellHitTarget) @@ -4476,9 +4492,8 @@ void Spell::SendSpellStart() if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE) castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it - if (m_spellInfo->HasAttribute(SPELL_ATTR8_HEAL_PREDICTION)) - if (m_casttime || m_spellInfo->HasAura(SPELL_AURA_PERIODIC_HEAL)) - castFlags |= CAST_FLAG_HEAL_PREDICTION; + if (m_spellInfo->HasAttribute(SPELL_ATTR8_HEAL_PREDICTION) && m_casttime && m_caster->IsUnit()) + castFlags |= CAST_FLAG_HEAL_PREDICTION; WorldPackets::Spells::SpellStart packet; WorldPackets::Spells::SpellCastData& castData = packet.Cast; @@ -4541,7 +4556,7 @@ void Spell::SendSpellStart() } if (castFlags & CAST_FLAG_HEAL_PREDICTION) - UpdateSpellHealPrediction(castData.Predict.emplace()); + UpdateSpellHealPrediction(castData.Predict.emplace(), false); m_caster->SendMessageToSet(packet.Write(), true); } @@ -4782,63 +4797,83 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) ammo.InventoryType = ammoInventoryType; } -void Spell::UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& predict) +static std::pair CalcPredictedHealing(SpellInfo const* spellInfo, Unit const* unitCaster, Unit* target, Spell* spell, bool withPeriodic) { - Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster->ToUnit(); - if (!target) - return; - - predict.Type = 0; - - /* - * @todo: implement - if (AuraEffect const* beaconOfLightEffect = m_caster->GetDummyAuraEffect(SPELLFAMILY_PALADIN, 3032, EFFECT_0)) + int32 points = 0; + SpellHealPredictionType type = SPELL_HEAL_PREDICTION_TARGET; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - if (beaconOfLightEffect->GetCasterGUID() == m_caster->GetGUID()) + SpellEffectInfo const& spellEffectInfo = spellInfo->Effects[i]; + switch (spellEffectInfo.Effect) { - predict.Type = 2; - predict.BeaconGUID = beaconOfLightEffect->GetBase()->GetUnitOwner()->GetGUID(); + case SPELL_EFFECT_HEAL: + case SPELL_EFFECT_HEAL_PCT: + points += unitCaster->SpellHealingBonusDone(target, + spellInfo, spellEffectInfo.CalcValue(unitCaster, nullptr, target), + DIRECT_DAMAGE, i, 1, spell); + + if (target != unitCaster && (spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_CASTER || spellEffectInfo.TargetB.GetTarget() == TARGET_UNIT_CASTER)) + type = SPELL_HEAL_PREDICTION_TARGET_AND_CASTER; // Binding Heal-like spells + else if (spellEffectInfo.TargetA.GetCheckType() == TARGET_CHECK_PARTY || spellEffectInfo.TargetB.GetCheckType() == TARGET_CHECK_PARTY) + type = SPELL_HEAL_PREDICTION_TARGET_PARTY; // Prayer of Healing (old party-wide targeting) + break; + default: + break; } - } - */ - - if (Unit* unitCaster = m_caster->ToUnit()) - { - for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) + + if (withPeriodic) { - SpellEffectInfo const& effect = m_spellInfo->Effects[effIndex]; - if (!effect.IsEffect()) - continue; - - switch (effect.Effect) + switch (spellEffectInfo.ApplyAuraName) { - case SPELL_EFFECT_HEAL: - case SPELL_EFFECT_HEAL_PCT: - predict.Points += unitCaster->SpellHealingBonusDone(target, m_spellInfo, effect.CalcValue(unitCaster, nullptr, target), HEAL, effIndex); + case SPELL_AURA_PERIODIC_HEAL: + case SPELL_AURA_OBS_MOD_HEALTH: + points += unitCaster->SpellHealingBonusDone(target, + spellInfo, spellEffectInfo.CalcValue(unitCaster, nullptr, target), + DIRECT_DAMAGE, i, 1, spell) * spellInfo->GetMaxTicks(); break; - case SPELL_EFFECT_APPLY_AURA: - { - switch (effect.ApplyAuraName) - { - case SPELL_AURA_PERIODIC_HEAL: - predict.Points += unitCaster->SpellHealingBonusDone(target, m_spellInfo, effect.CalcValue(unitCaster, nullptr, target), HEAL, effIndex) * m_spellInfo->GetMaxTicks(); - break; - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(effect.TriggerSpell)) - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (spell->Effects[i].Effect == SPELL_EFFECT_HEAL || spell->Effects[i].Effect == SPELL_EFFECT_HEAL_PCT) - predict.Points += unitCaster->SpellHealingBonusDone(target, spell, spell->Effects[i].CalcValue(unitCaster, nullptr, target), HEAL, i) * m_spellInfo->GetMaxTicks(); - break; - default: - break; - } + case SPELL_AURA_PERIODIC_TRIGGER_SPELL: + if (SpellInfo const* triggered = sSpellMgr->GetSpellInfo(spellEffectInfo.TriggerSpell)) + points += CalcPredictedHealing(triggered, unitCaster, target, nullptr, withPeriodic).first; break; - } default: break; } } } + + return { points, type }; +} + +void Spell::UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& healPrediction, bool withPeriodic) +{ + healPrediction.BeaconGUID = ObjectGuid::Empty; + healPrediction.Points = 0; + healPrediction.Type = SPELL_HEAL_PREDICTION_TARGET; + + Unit const* unitCaster = m_caster->ToUnit(); + + if (Unit* target = m_targets.GetUnitTarget()) + { + auto [points, type] = CalcPredictedHealing(m_spellInfo, unitCaster, target, this, withPeriodic); + healPrediction.Points = points; + healPrediction.Type = type; + } + + static constexpr uint32 beaconSpellId = 53651; + + if (healPrediction.Type == SPELL_HEAL_PREDICTION_TARGET && unitCaster->HasAura(beaconSpellId, unitCaster->GetGUID())) + { + auto beacon = std::find_if(unitCaster->GetSingleCastAuras().begin(), unitCaster->GetSingleCastAuras().end(), [](Aura const* aura) + { + return aura->GetSpellInfo()->Effects[EFFECT_1].IsEffect() && aura->GetSpellInfo()->Effects[EFFECT_1].TriggerSpell == beaconSpellId; + }); + + if (beacon != unitCaster->GetSingleCastAuras().end()) + { + healPrediction.BeaconGUID = (*beacon)->GetOwner()->GetGUID(); + healPrediction.Type = SPELL_HEAL_PREDICTION_TARGET_AND_BEACON; + } + } } void Spell::SendLogExecute() @@ -5024,26 +5059,7 @@ void Spell::SendChannelStart(uint32 duration) if (!unitCaster) return; - WorldPackets::Spells::ChannelStart packet; - packet.CasterGUID = m_caster->GetGUID(); - packet.SpellID = m_spellInfo->Id; - packet.ChannelDuration = duration; - - uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; - uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_spellInfo->GetMechanicImmunityMask(unitCaster); - - if (schoolImmunityMask || mechanicImmunityMask) - castFlags |= CAST_FLAG_IMMUNITY; - - if (castFlags & CAST_FLAG_IMMUNITY) - { - packet.InterruptImmunities.emplace(); - packet.InterruptImmunities->SchoolImmunities = schoolImmunityMask; // CastSchoolImmunities - packet.InterruptImmunities->Immunities = mechanicImmunityMask; // CastImmunities - } - - m_timer = duration; + m_timer = duration; if (!m_targets.HasDst()) { @@ -5086,22 +5102,37 @@ void Spell::SendChannelStart(uint32 duration) else if (m_spellInfo->HasAttribute(SPELL_ATTR1_IS_SELF_CHANNELLED)) unitCaster->SetChannelObjectGuid(unitCaster->GetGUID()); - - if (m_spellInfo->HasAttribute(SPELL_ATTR8_HEAL_PREDICTION)) - { - castFlags |= CAST_FLAG_HEAL_PREDICTION; - - WorldPackets::Spells::SpellHealPrediction predict; - UpdateSpellHealPrediction(predict); - packet.HealPrediction.emplace(unitCaster->GetChannelObjectGuid(), predict); - } - - m_caster->SendMessageToSet(packet.Write(), true); m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); if (Creature* creatureCaster = m_caster->ToCreature()) if (!creatureCaster->HasSpellFocus(this)) creatureCaster->SetSpellFocus(this, nullptr); + + WorldPackets::Spells::ChannelStart packet; + packet.CasterGUID = m_caster->GetGUID(); + packet.SpellID = m_spellInfo->Id; + packet.ChannelDuration = duration; + + uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + uint32 mechanicImmunityMask = m_spellInfo->GetMechanicImmunityMask(unitCaster); + + if (schoolImmunityMask || mechanicImmunityMask) + { + packet.InterruptImmunities.emplace(); + packet.InterruptImmunities->SchoolImmunities = schoolImmunityMask; // CastSchoolImmunities + packet.InterruptImmunities->Immunities = mechanicImmunityMask; // CastImmunities + } + + if (m_spellInfo->HasAttribute(SPELL_ATTR8_HEAL_PREDICTION) && m_caster->IsUnit()) + { + WorldPackets::Spells::TargetedHealPrediction& healPrediction = packet.HealPrediction.emplace(); + if (!unitCaster->GetChannelObjectGuid().IsEmpty() && unitCaster->GetChannelObjectGuid().IsUnit()) + healPrediction.TargetGUID = unitCaster->GetChannelObjectGuid(); + + UpdateSpellHealPrediction(healPrediction.Predict, true); + } + + m_caster->SendMessageToSet(packet.Write(), true); } void Spell::SendResurrectRequest(Player* target) @@ -5648,6 +5679,9 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_CUSTOM_ERROR; } + if (m_spellInfo->HasAttribute(SPELL_ATTR8_ONLY_PLAYERS_CAN_CAST_THIS_SPELL) && !m_caster->IsPlayer()) + return SPELL_FAILED_CASTER_AURASTATE; + // Check global cooldown if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_GCD) && HasGlobalCooldown()) return !m_spellInfo->HasAttribute(SPELL_ATTR0_COOLDOWN_ON_EVENT) ? SPELL_FAILED_NOT_READY : SPELL_FAILED_DONT_REPORT; @@ -5669,7 +5703,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_ONLY_INDOORS; } - if (m_spellInfo->HasAttribute(SPELL_ATTR8_BATTLE_RESURRECTION) && m_caster->GetMap()->IsRaid()) + if (m_spellInfo->HasAttribute(SPELL_ATTR8_ENFORCE_IN_COMBAT_RESSURECTION_LIMIT) && m_caster->GetMap()->IsRaid()) if (InstanceScript* instance = m_caster->GetInstanceScript()) if (instance->IsEncounterInProgress()) if (!instance->GetCombatResurrectionCharges()) @@ -7851,7 +7885,7 @@ bool Spell::IsPositive() const bool Spell::IsNeedSendToClient() const { return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || m_spellInfo->IsChanneled() || - m_spellInfo->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT) || m_spellInfo->Speed > 0.0f || (!m_triggeredByAuraSpell && !IsTriggered()) || + m_spellInfo->HasAttribute(SPELL_ATTR8_AURA_POINTS_ON_CLIENT) || m_spellInfo->Speed > 0.0f || (!m_triggeredByAuraSpell && !IsTriggered()) || m_spellInfo->HasAttribute(SPELL_ATTR7_ALWAYS_CAST_LOG); } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 8e6e2f9ae8..35b28f6f63 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -114,6 +114,14 @@ enum SpellCastFlags CAST_FLAG_UNKNOWN_32 = 0x80000000 }; +enum SpellHealPredictionType : uint8 +{ + SPELL_HEAL_PREDICTION_TARGET = 0, + SPELL_HEAL_PREDICTION_TARGET_AND_CASTER = 1, + SPELL_HEAL_PREDICTION_TARGET_AND_BEACON = 2, + SPELL_HEAL_PREDICTION_TARGET_PARTY = 3, +}; + enum SpellRangeFlag { SPELL_RANGE_DEFAULT = 0, @@ -807,7 +815,7 @@ class TC_GAME_API Spell void CalculateJumpSpeeds(uint8 i, float dist, float & speedxy, float & speedz); void UpdateSpellCastDataTargets(WorldPackets::Spells::SpellHitInfo& data); void UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& data); - void UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& predict); + void UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& predict, bool withPeriodic); SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); // ------------------------------------------- diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 4ece4a880a..e904c40125 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -190,6 +190,8 @@ enum TriggerCastFlags : uint32 TRIGGERED_FULL_DEBUG_MASK = 0xFFFFFFFF }; +DEFINE_ENUM_FLAG(TriggerCastFlags); + struct TC_GAME_API CastSpellExtraArgs { CastSpellExtraArgs() {} diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index b31229cce8..5d9aa89975 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -449,7 +449,7 @@ int32 SpellEffectInfo::CalcValue(WorldObject const* caster, int32 const* bp, Uni if (Scaling.Coefficient != 0.0f) { int32 level = _spellInfo->SpellLevel; - if (target && _spellInfo->IsPositiveEffect(_effIndex) && (Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_2)) + if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING)) level = target->getLevel(); else if (casterUnit) level = casterUnit->getLevel(); @@ -492,7 +492,12 @@ int32 SpellEffectInfo::CalcValue(WorldObject const* caster, int32 const* bp, Uni { if (casterUnit && basePointsPerLevel != 0.0f) { - int32 level = int32(casterUnit->getLevel()); + int32 level = 1; + if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING)) + level = target->getLevel(); + else if (caster && caster->IsUnit()) + level = casterUnit->getLevel(); + if (level > int32(_spellInfo->MaxLevel) && _spellInfo->MaxLevel > 0) level = int32(_spellInfo->MaxLevel); else if (level < int32(_spellInfo->BaseLevel)) @@ -1320,11 +1325,6 @@ bool SpellInfo::IsPassive() const return HasAttribute(SPELL_ATTR0_PASSIVE); } -bool SpellInfo::IsRaidMarker() const -{ - return AttributesEx8 & SPELL_ATTR8_RAID_MARKER; -} - bool SpellInfo::IsAutocastable() const { if (IsPassive()) @@ -1769,6 +1769,20 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a return SPELL_FAILED_NOT_IN_RAID_INSTANCE; } + if (HasAttribute(SPELL_ATTR8_REMOVE_OUTSIDE_DUNGEONS_AND_RAIDS)) + { + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if (!mapEntry || !mapEntry->IsDungeon()) + return SPELL_FAILED_TARGET_NOT_IN_INSTANCE; + } + + if (HasAttribute(SPELL_ATTR8_NOT_IN_BATTLEGROUND)) + { + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if (!mapEntry || mapEntry->IsBattleground()) + return SPELL_FAILED_NOT_IN_BATTLEGROUND; + } + // DB base check (if non empty then must fit at least single for allow) SpellAreaMapBounds saBounds = sSpellMgr->GetSpellAreaMapBounds(Id); if (saBounds.first != saBounds.second) @@ -1888,6 +1902,13 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co Unit const* unitTarget = target->ToUnit(); + /* + * @todo: 4.x - old hold due to an upcoming tempsummon rework + if (HasAttribute(SPELL_ATTR8_ONLY_TARGET_IF_SAME_CREATOR)) + if (caster != target && caster->GetGUID() != target->GetOwnerGUID()) + return SPELL_FAILED_BAD_TARGETS; + */ + // creature/player specific target checks if (unitTarget) { @@ -1938,6 +1959,10 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co } } } + + if (HasAttribute(SPELL_ATTR8_ONLY_TARGET_OWN_SUMMONS)) + if (!unitTarget->IsSummon() || unitTarget->ToTempSummon()->GetSummonerGUID() != caster->GetGUID()) + return SPELL_FAILED_BAD_TARGETS; } // corpse specific target checks else if (Corpse const* corpseTarget = target->ToCorpse()) @@ -3390,6 +3415,17 @@ int32 SpellInfo::GetMaxDuration() const return (DurationEntry->MaxDuration == -1) ? -1 : abs(DurationEntry->MaxDuration); } +float SpellInfo::CalcPeriodicHasteMod(Unit const* caster) const +{ + float hasteMod = 1.f; + if (HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) + hasteMod = caster->GetFloatValue(UNIT_MOD_CAST_HASTE); + else if (HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC) && caster->IsPlayer()) + hasteMod = caster->GetFloatValue(PLAYER_FIELD_MOD_HASTE); + + return hasteMod; +} + int32 SpellInfo::CalcDuration(WorldObject const* caster /*= nullptr*/) const { if (!DurationEntry) @@ -3443,13 +3479,14 @@ int32 SpellInfo::CalcDuration(WorldObject const* caster /*= nullptr*/) const return false; }(); - if (hasPeriodicEffect && HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) && !HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) + if (hasPeriodicEffect && !HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS) && (HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) || HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC))) { - float hasteMod = unitCaster->GetFloatValue(UNIT_MOD_CAST_HASTE); + float hasteMod = CalcPeriodicHasteMod(unitCaster); if (hasteMod > 0.001f) { if (HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION)) return int32(duration * hasteMod); + int32 period = CalcPeriod(caster, periodicEffectIndex); if (period > 0) duration = int32(period * (duration / (float)period)); @@ -3480,9 +3517,9 @@ int32 SpellInfo::CalcPeriod(WorldObject const* caster, SpellEffIndex effIndex, O if (Player const* modOwner = unitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(Id, SpellModOp::Period, period); - if (HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) && !HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) + if (!HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS) && (HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) || HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC))) { - float hasteMod = unitCaster->GetFloatValue(UNIT_MOD_CAST_HASTE); + float hasteMod = CalcPeriodicHasteMod(unitCaster); if (hasteMod > 0.001f) period = ((float)period * hasteMod); } diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index f8179c6b61..71b312f32c 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -520,7 +520,6 @@ class TC_GAME_API SpellInfo bool IsSelfCast() const; bool IsPassive() const; - bool IsRaidMarker() const; bool IsAutocastable() const; bool IsStackableWithRanks() const; bool IsPassiveStackableWithRanks() const; @@ -583,6 +582,7 @@ class TC_GAME_API SpellInfo int32 GetDuration() const; int32 GetMaxDuration() const; + float CalcPeriodicHasteMod(Unit const* caster) const; int32 CalcDuration(WorldObject const* caster = nullptr) const; int32 CalcPeriod(WorldObject const* caster, SpellEffIndex effIndex, Optional periodOverride = {}) const;