diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 59 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 5 | ||||
-rw-r--r-- | src/server/game/Spells/SpellDefines.h | 4 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 22 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 35 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 8 |
6 files changed, 127 insertions, 6 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 50483d7b714..ff2c0128e07 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2620,6 +2620,23 @@ void Spell::AddDestTarget(SpellDestination const& dest, uint32 effIndex) m_destTargets[effIndex] = dest; } +int32 Spell::GetUnitTargetIndexForEffect(ObjectGuid const& target, SpellEffIndex effect) const +{ + int32 index = 0; + for (TargetInfo const& uniqueTargetInfo : m_UniqueTargetInfo) + { + if (uniqueTargetInfo.MissCondition == SPELL_MISS_NONE && uniqueTargetInfo.EffectMask & (1 << effect)) + { + if (uniqueTargetInfo.TargetGUID == target) + break; + + ++index; + } + } + + return index; +} + int64 Spell::GetUnitTargetCountForEffect(SpellEffIndex effect) const { return std::count_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effect](TargetInfo const& targetInfo) @@ -8297,16 +8314,44 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, Spe if (m_originalCaster && m_damage > 0) { - if (spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) || m_spellInfo->HasAttribute(SPELL_ATTR5_TREAT_AS_AREA_EFFECT)) + bool isAoeTarget = spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA); + if (isAoeTarget || m_spellInfo->HasAttribute(SPELL_ATTR5_TREAT_AS_AREA_EFFECT)) { m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_originalCaster->GetGUID()); if (m_originalCaster->GetTypeId() == TYPEID_PLAYER) { + int64 targetCount = !isAoeTarget && m_spellValue->ParentSpellTargetCount ? *m_spellValue->ParentSpellTargetCount : GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex); + int32 targetIndex = !isAoeTarget && m_spellValue->ParentSpellTargetIndex ? *m_spellValue->ParentSpellTargetIndex : GetUnitTargetIndexForEffect(targetInfo.TargetGUID, spellEffectInfo.EffectIndex); + + // sqrt target cap damage calculation + if (m_spellInfo->SqrtDamageAndHealingDiminishing.MaxTargets + && targetCount > m_spellInfo->SqrtDamageAndHealingDiminishing.MaxTargets + && targetIndex >= m_spellInfo->SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets) + m_damage = m_damage * std::sqrt(float(m_spellInfo->SqrtDamageAndHealingDiminishing.MaxTargets) / std::min(AOE_DAMAGE_TARGET_CAP, targetCount)); + // cap damage of player AOE - int64 targetAmount = GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex); - if (targetAmount > 20) - m_damage = m_damage * 20 / targetAmount; + if (targetCount > AOE_DAMAGE_TARGET_CAP) + m_damage = m_damage * AOE_DAMAGE_TARGET_CAP / targetCount; + } + } + } + + if (m_originalCaster && m_healing > 0) + { + bool isAoeTarget = spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA); + if (isAoeTarget || m_spellInfo->HasAttribute(SPELL_ATTR5_TREAT_AS_AREA_EFFECT)) + { + if (m_originalCaster->GetTypeId() == TYPEID_PLAYER) + { + int64 targetCount = !isAoeTarget && m_spellValue->ParentSpellTargetCount ? *m_spellValue->ParentSpellTargetCount : GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex); + int32 targetIndex = !isAoeTarget && m_spellValue->ParentSpellTargetIndex ? *m_spellValue->ParentSpellTargetIndex : GetUnitTargetIndexForEffect(targetInfo.TargetGUID, spellEffectInfo.EffectIndex); + + // sqrt target cap healing calculation + if (m_spellInfo->SqrtDamageAndHealingDiminishing.MaxTargets + && targetCount > m_spellInfo->SqrtDamageAndHealingDiminishing.MaxTargets + && targetIndex >= m_spellInfo->SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets) + m_healing = m_healing * std::sqrt(float(m_spellInfo->SqrtDamageAndHealingDiminishing.MaxTargets) / std::min(AOE_DAMAGE_TARGET_CAP, targetCount)); } } } @@ -8426,6 +8471,12 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value) case SPELLVALUE_DURATION: m_spellValue->Duration = value; break; + case SPELLVALUE_PARENT_SPELL_TARGET_COUNT: + m_spellValue->ParentSpellTargetCount = value; + break; + case SPELLVALUE_PARENT_SPELL_TARGET_INDEX: + m_spellValue->ParentSpellTargetIndex = value; + break; default: break; } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 6494d4c3d70..c0c9ede5f87 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -73,6 +73,7 @@ enum WeaponAttackType : uint8; #define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS) #define MAX_SPELL_RANGE_TOLERANCE 3.0f #define TRAJECTORY_MISSILE_SIZE 3.0f +#define AOE_DAMAGE_TARGET_CAP SI64LIT(20) enum SpellCastFlags { @@ -213,6 +214,8 @@ struct SpellValue float DurationMul; float CriticalChance; Optional<int32> Duration; + Optional<int32> ParentSpellTargetCount; + Optional<int32> ParentSpellTargetIndex; }; enum SpellState @@ -818,6 +821,8 @@ class TC_GAME_API Spell SpellDestination m_destTargets[MAX_SPELL_EFFECTS]; + int32 GetUnitTargetIndexForEffect(ObjectGuid const& target, SpellEffIndex effect) const; + void AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid = true, bool implicit = true, Position const* losPosition = nullptr); void AddGOTarget(GameObject* target, uint32 effectMask); void AddItemTarget(Item* item, uint32 effectMask); diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 4cfedbe9de4..0810537733b 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -228,7 +228,9 @@ enum SpellValueMod : uint8 SPELLVALUE_AURA_STACK, SPELLVALUE_CRIT_CHANCE, SPELLVALUE_DURATION_PCT, - SPELLVALUE_DURATION + SPELLVALUE_DURATION, + SPELLVALUE_PARENT_SPELL_TARGET_COUNT, + SPELLVALUE_PARENT_SPELL_TARGET_INDEX }; enum SpellFacingFlags diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index c60832f784c..578a8d635b8 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -643,11 +643,15 @@ void Spell::EffectTriggerSpell() } SpellCastTargets targets; + Optional<int32> targetCount; + Optional<int32> targetIndex; if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET) { if (!spellInfo->NeedsToBeTriggeredByCaster(m_spellInfo)) return; targets.SetUnitTarget(unitTarget); + targetCount = GetUnitTargetCountForEffect(effectInfo->EffectIndex); + targetIndex = GetUnitTargetIndexForEffect(unitTarget->GetGUID(), effectInfo->EffectIndex); } else //if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH) { @@ -673,7 +677,7 @@ void Spell::EffectTriggerSpell() delay = Milliseconds(effectInfo->MiscValue); m_caster->m_Events.AddEventAtOffset([caster = m_caster, targets, originalCaster = m_originalCasterGUID, castItemGuid = m_castItemGUID, originalCastId = m_castId, - spellEffectInfo = effectInfo, value = damage, itemLevel = m_castItemLevel]() mutable + spellEffectInfo = effectInfo, value = damage, itemLevel = m_castItemLevel, targetCount, targetIndex]() mutable { targets.Update(caster); // refresh pointers stored in targets @@ -691,6 +695,12 @@ void Spell::EffectTriggerSpell() for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), value); + if (targetCount) + args.AddSpellMod(SPELLVALUE_PARENT_SPELL_TARGET_COUNT, *targetCount); + + if (targetIndex) + args.AddSpellMod(SPELLVALUE_PARENT_SPELL_TARGET_INDEX, *targetIndex); + caster->CastSpell(std::move(targets), spellEffectInfo->TriggerSpell, args); }, delay); } @@ -717,11 +727,15 @@ void Spell::EffectTriggerMissileSpell() } SpellCastTargets targets; + Optional<int32> targetCount; + Optional<int32> targetIndex; if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET) { if (!spellInfo->NeedsToBeTriggeredByCaster(m_spellInfo)) return; targets.SetUnitTarget(unitTarget); + targetCount = GetUnitTargetCountForEffect(effectInfo->EffectIndex); + targetIndex = GetUnitTargetIndexForEffect(unitTarget->GetGUID(), effectInfo->EffectIndex); } else //if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT) { @@ -745,6 +759,12 @@ void Spell::EffectTriggerMissileSpell() for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), damage); + if (targetCount) + args.AddSpellMod(SPELLVALUE_PARENT_SPELL_TARGET_COUNT, *targetCount); + + if (targetIndex) + args.AddSpellMod(SPELLVALUE_PARENT_SPELL_TARGET_INDEX, *targetIndex); + // original caster guid only for GO cast m_caster->CastSpell(std::move(targets), spellInfo->Id, args); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index f43fbf07847..7020e63b246 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -3568,6 +3568,41 @@ void SpellInfo::_LoadImmunityInfo() } } +void SpellInfo::_LoadSqrtTargetLimit(int32 maxTargets, int32 numNonDiminishedTargets, Optional<SpellEffIndex> maxTargetsEffectValueHolder, + Optional<SpellEffIndex> numNonDiminishedTargetsEffectValueHolder) +{ + SqrtDamageAndHealingDiminishing.MaxTargets = maxTargets; + SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets = numNonDiminishedTargets; + + if (maxTargetsEffectValueHolder) + { + if (maxTargetsEffectValueHolder < GetEffects().size()) + { + SpellEffectInfo const& valueHolder = GetEffect(*maxTargetsEffectValueHolder); + int32 expectedValue = valueHolder.CalcBaseValue(nullptr, nullptr, 0, -1); + if (maxTargets != expectedValue) + TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} has different value in effect {} than expected, recheck target caps (expected {}, got {})", + Id, AsUnderlyingType(*maxTargetsEffectValueHolder), maxTargets, expectedValue); + } + else + TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} does not have effect {}", Id, AsUnderlyingType(*maxTargetsEffectValueHolder)); + } + + if (numNonDiminishedTargetsEffectValueHolder) + { + if (numNonDiminishedTargetsEffectValueHolder < GetEffects().size()) + { + SpellEffectInfo const& valueHolder = GetEffect(*numNonDiminishedTargetsEffectValueHolder); + int32 expectedValue = valueHolder.CalcBaseValue(nullptr, nullptr, 0, -1); + if (numNonDiminishedTargets != expectedValue) + TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} has different value in effect {} than expected, recheck target caps (expected {}, got {})", + Id, AsUnderlyingType(*numNonDiminishedTargetsEffectValueHolder), numNonDiminishedTargets, expectedValue); + } + else + TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} does not have effect {}", Id, AsUnderlyingType(*numNonDiminishedTargetsEffectValueHolder)); + } +} + void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const& spellEffectInfo, bool apply) const { SpellEffectInfo::ImmunityInfo const* immuneInfo = spellEffectInfo.GetImmunityInfo(); diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 506d77d6d65..ab932a61b07 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -424,6 +424,11 @@ class TC_GAME_API SpellInfo uint32 ExplicitTargetMask = 0; SpellChainNode const* ChainEntry = nullptr; + struct + { + int32 MaxTargets = 0; // The amount of targets after the damage decreases by the Square Root AOE formula + int32 NumNonDiminishedTargets = 0; // The amount of targets that still take the full amount before the damage decreases by the Square Root AOE formula + } SqrtDamageAndHealingDiminishing; explicit SpellInfo(SpellNameEntry const* spellName, ::Difficulty difficulty, SpellInfoLoadHelper const& data); explicit SpellInfo(SpellNameEntry const* spellName, ::Difficulty difficulty, std::vector<SpellEffectEntry> const& effects); @@ -599,6 +604,9 @@ class TC_GAME_API SpellInfo void _LoadAuraState(); void _LoadSpellDiminishInfo(); void _LoadImmunityInfo(); + void _LoadSqrtTargetLimit(int32 maxTargets, int32 numNonDiminishedTargets, + Optional<SpellEffIndex> maxTargetsEffectValueHolder, + Optional<SpellEffIndex> numNonDiminishedTargetsEffectValueHolder); // unloading helpers void _UnloadImplicitTargetConditionLists(); |