aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Spells/Spell.cpp59
-rw-r--r--src/server/game/Spells/Spell.h5
-rw-r--r--src/server/game/Spells/SpellDefines.h4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp22
-rw-r--r--src/server/game/Spells/SpellInfo.cpp35
-rw-r--r--src/server/game/Spells/SpellInfo.h8
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();