diff options
| author | Shauren <shauren.trinity@gmail.com> | 2023-10-22 18:39:59 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2023-10-22 18:39:59 +0200 |
| commit | dc9361fcc2eb16a59b52dfd8b0d47dfc1bf639be (patch) | |
| tree | b25b12a693f3e60f940be7a78df72b7ba47f4718 /src/server/game/Spells | |
| parent | b2393d6adeca9a54b6ff1fb62a87da48c068f010 (diff) | |
Core/Spells: Named and implemented most of SpellAttr8
Diffstat (limited to 'src/server/game/Spells')
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 10 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 157 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.h | 17 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 33 |
5 files changed, 183 insertions, 36 deletions
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 26bd7a0c0ba..f33d2313a63 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -768,6 +768,8 @@ float AuraEffect::CalculateEstimatedfTotalPeriodicAmount(Unit* caster, Unit* tar caster->ModSpellDurationTime(spellInfo, period); else if (spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) period = int32(period * caster->m_unitData->ModCastingSpeed); + else if (spellInfo->HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC)) + period = int32(period * caster->m_unitData->ModHaste); if (!period) return 0.0f; @@ -851,6 +853,8 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool resetPeriodicTimer /*= tru caster->ModSpellDurationTime(m_spellInfo, _period); else if (m_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) _period = int32(_period * caster->m_unitData->ModCastingSpeed); + else if (m_spellInfo->HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC)) + _period = int32(_period * caster->m_unitData->ModHaste); } } else // prevent infinite loop on Update @@ -967,7 +971,7 @@ void AuraEffect::ChangeAmount(int32 newAmount, bool mark, bool onStackOrReapply, HandleEffect(aurApp, handleMask, true, triggeredBy); } - 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(); } @@ -5905,6 +5909,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 = GetSpellEffectInfo().TriggerSpell; if (triggerSpellId == 0) @@ -5926,6 +5932,8 @@ void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp { 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 = GetSpellEffectInfo().TriggerSpell; if (triggerSpellId == 0) diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 8ca596c16a9..2fe302740aa 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -148,7 +148,7 @@ void AuraApplication::_InitFlags(Unit* caster, uint32 effMask) return effect && (GetEffectsToApply() & (1 << effect->GetEffIndex())) && Aura::EffectTypeNeedsSendingAmount(effect->GetAuraType()); }; - if (GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT) + if (GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_POINTS_ON_CLIENT) || std::find_if(GetBase()->GetAuraEffects().begin(), GetBase()->GetAuraEffects().end(), std::cref(effectNeedsAmount)) != GetBase()->GetAuraEffects().end()) _flags |= AFLAG_SCALABLE; } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index c0bc954a8b2..fce17019d45 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -824,6 +824,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(SPELL_FAILED_NO_LIQUID); + return; + } + } + } + if (uint64 dstDelay = CalculateDelayMomentForDst(m_spellInfo->LaunchDelay)) m_delayMoment = dstDelay; } @@ -2740,7 +2756,7 @@ void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, SpellEffectInfo const& sp 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) @@ -4672,6 +4688,9 @@ void Spell::SendSpellStart() if (HasPowerTypeCost(POWER_RUNES)) castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it + 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; @@ -4737,13 +4756,8 @@ void Spell::SendSpellStart() castData.Immunities.Value = mechanicImmunityMask; } - /** @todo implement heal prediction packet data if (castFlags & CAST_FLAG_HEAL_PREDICTION) - { - castData.Predict.BeconGUID = ?? - castData.Predict.Points = 0; - castData.Predict.Type = 0; - }**/ + UpdateSpellHealPrediction(castData.Predict, false); m_caster->SendMessageToSet(packet.Write(), true); } @@ -4948,6 +4962,84 @@ int32 Spell::GetSpellCastDataAmmo() return ammoDisplayID; } +static std::pair<int32, SpellHealPredictionType> CalcPredictedHealing(SpellInfo const* spellInfo, Unit const* unitCaster, Unit* target, uint32 castItemEntry, int32 castItemLevel, Spell* spell, bool withPeriodic) +{ + int32 points = 0; + SpellHealPredictionType type = SPELL_HEAL_PREDICTION_TARGET; + for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) + { + switch (spellEffectInfo.Effect) + { + case SPELL_EFFECT_HEAL: + case SPELL_EFFECT_HEAL_PCT: + points += unitCaster->SpellHealingBonusDone(target, + spellInfo, spellEffectInfo.CalcValue(unitCaster, nullptr, target, nullptr, castItemEntry, castItemLevel), + DIRECT_DAMAGE, spellEffectInfo, 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 (withPeriodic) + { + switch (spellEffectInfo.ApplyAuraName) + { + case SPELL_AURA_PERIODIC_HEAL: + case SPELL_AURA_OBS_MOD_HEALTH: + points += unitCaster->SpellHealingBonusDone(target, + spellInfo, spellEffectInfo.CalcValue(unitCaster, nullptr, target, nullptr, castItemEntry, castItemLevel), + DIRECT_DAMAGE, spellEffectInfo, 1, spell) * spellInfo->GetMaxTicks(); + break; + case SPELL_AURA_PERIODIC_TRIGGER_SPELL: + if (SpellInfo const* triggered = sSpellMgr->GetSpellInfo(spellEffectInfo.TriggerSpell, spellInfo->Difficulty)) + points += CalcPredictedHealing(triggered, unitCaster, target, castItemEntry, castItemLevel, 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, m_castItemEntry, m_castItemLevel, 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()->GetEffects().size() > EFFECT_1 && aura->GetSpellInfo()->GetEffect(EFFECT_1).TriggerSpell == beaconSpellId; + }); + + if (beacon != unitCaster->GetSingleCastAuras().end()) + { + healPrediction.BeaconGUID = (*beacon)->GetOwner()->GetGUID(); + healPrediction.Type = SPELL_HEAL_PREDICTION_TARGET_AND_BEACON; + } + } +} + void Spell::SendSpellExecuteLog() { if (_executeLogEffects.empty()) @@ -5112,23 +5204,6 @@ void Spell::SendChannelStart(uint32 duration) if (!unitCaster) return; - WorldPackets::Spells::SpellChannelStart spellChannelStart; - spellChannelStart.CasterGUID = unitCaster->GetGUID(); - spellChannelStart.SpellID = m_spellInfo->Id; - spellChannelStart.Visual = m_SpellVisual; - spellChannelStart.ChannelDuration = duration; - unitCaster->SendMessageToSet(spellChannelStart.Write(), true); - - uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = unitCaster->GetMechanicImmunityMask(); - - if (schoolImmunityMask || mechanicImmunityMask) - { - spellChannelStart.InterruptImmunities.emplace(); - spellChannelStart.InterruptImmunities->SchoolImmunities = schoolImmunityMask; - spellChannelStart.InterruptImmunities->Immunities = mechanicImmunityMask; - } - m_timer = duration; if (!m_targets.HasDst()) @@ -5176,6 +5251,33 @@ void Spell::SendChannelStart(uint32 duration) unitCaster->SetChannelSpellId(m_spellInfo->Id); unitCaster->SetChannelVisual(m_SpellVisual); + + WorldPackets::Spells::SpellChannelStart spellChannelStart; + spellChannelStart.CasterGUID = unitCaster->GetGUID(); + spellChannelStart.SpellID = m_spellInfo->Id; + spellChannelStart.Visual = m_SpellVisual; + spellChannelStart.ChannelDuration = duration; + + uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + uint32 mechanicImmunityMask = unitCaster->GetMechanicImmunityMask(); + + if (schoolImmunityMask || mechanicImmunityMask) + { + spellChannelStart.InterruptImmunities.emplace(); + spellChannelStart.InterruptImmunities->SchoolImmunities = schoolImmunityMask; + spellChannelStart.InterruptImmunities->Immunities = mechanicImmunityMask; + } + + if (m_spellInfo->HasAttribute(SPELL_ATTR8_HEAL_PREDICTION) && m_casttime && m_caster->IsUnit()) + { + WorldPackets::Spells::SpellTargetedHealPrediction& healPrediction = spellChannelStart.HealPrediction.emplace(); + if (unitCaster->m_unitData->ChannelObjects.size() == 1 && unitCaster->m_unitData->ChannelObjects[0].IsUnit()) + healPrediction.TargetGUID = unitCaster->m_unitData->ChannelObjects[0]; + + UpdateSpellHealPrediction(healPrediction.Predict, true); + } + + unitCaster->SendMessageToSet(spellChannelStart.Write(), true); } void Spell::SendResurrectRequest(Player* target) @@ -5594,6 +5696,9 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 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; @@ -6920,7 +7025,7 @@ SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules() return isArena ? SPELL_FAILED_NOT_IN_ARENA : SPELL_FAILED_NOT_IN_RATED_BATTLEGROUND; if (isArena && m_spellInfo->HasAttribute(SPELL_ATTR9_NOT_USABLE_IN_ARENA)) - return SPELL_FAILED_NOT_IN_ARENA; + return SPELL_FAILED_NOT_IN_ARENA; // check cooldowns uint32 spellCooldown = m_spellInfo->GetRecoveryTime(); @@ -8075,7 +8180,7 @@ bool Spell::IsPositive() const bool Spell::IsNeedSendToClient() const { return m_SpellVisual.SpellXSpellVisualID || m_SpellVisual.ScriptVisualID || m_spellInfo->IsChanneled() || - (m_spellInfo->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT)) || m_spellInfo->HasHitDelay() || (!m_triggeredByAuraSpell && !IsTriggered()) || + (m_spellInfo->HasAttribute(SPELL_ATTR8_AURA_POINTS_ON_CLIENT)) || m_spellInfo->HasHitDelay() || (!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 25f65e51759..b16bf1eb5e6 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -28,12 +28,10 @@ #include "SpellDefines.h" #include <memory> -namespace WorldPackets +namespace WorldPackets::Spells { - namespace Spells - { - struct SpellCastData; - } +struct SpellCastData; +struct SpellHealPrediction; } class Aura; @@ -147,6 +145,14 @@ enum SpellCastSource : uint8 SPELL_CAST_SOURCE_SPELL = 16, }; +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, @@ -888,6 +894,7 @@ class TC_GAME_API Spell void UpdateSpellCastDataTargets(WorldPackets::Spells::SpellCastData& data); int32 GetSpellCastDataAmmo(); + void UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& healPrediction, bool withPeriodic); SpellCastResult CanOpenLock(SpellEffectInfo const& effect, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); // ------------------------------------------- diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 541496075a1..9b4b12295e2 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -560,7 +560,7 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ if (Scaling.Coefficient != 0.0f) { uint32 level = _spellInfo->SpellLevel; - if (target && _spellInfo->IsPositiveEffect(EffectIndex) && (Effect == SPELL_EFFECT_APPLY_AURA)) + if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING)) level = target->GetLevel(); else if (caster && caster->IsUnit()) level = caster->ToUnit()->GetLevel(); @@ -632,7 +632,12 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ if (ContentTuningEntry const* contentTuning = sContentTuningStore.LookupEntry(contentTuningId)) expansion = contentTuning->ExpansionID; - int32 level = caster && caster->IsUnit() ? int32(caster->ToUnit()->GetLevel()) : 1; + int32 level = 1; + if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING)) + level = target->GetLevel(); + else if (caster && caster->IsUnit()) + level = caster->ToUnit()->GetLevel(); + value = sDB2Manager.EvaluateExpectedStat(stat, level, expansion, 0, CLASS_NONE, 0) * BasePoints / 100.0f; } @@ -2034,6 +2039,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) @@ -2153,6 +2172,10 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co Unit const* unitTarget = target->ToUnit(); + 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) { @@ -2203,6 +2226,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()) @@ -2294,7 +2321,7 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co if (HasEffect(SPELL_EFFECT_SELF_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT)) return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED; - if (HasAttribute(SPELL_ATTR8_BATTLE_RESURRECTION)) + if (HasAttribute(SPELL_ATTR8_ENFORCE_IN_COMBAT_RESSURECTION_LIMIT)) if (Map* map = caster->GetMap()) if (InstanceMap* iMap = map->ToInstanceMap()) if (InstanceScript* instance = iMap->GetInstanceScript()) |
