Core/Spells: ported spell effect value calculation from cata_classic branch

This commit is contained in:
Ovahlord committed 2025-06-07 11:40:18 +02:00
1 parent 57416b873f
commit 4524c29f2c
5 files changed
+107 -161

No files matched your search

+2 -2
View File
@@ -2418,9 +2418,9 @@ Player* WorldObject::GetSpellModOwner() const
}
// function uses real base points (typically value - 1)
int32 WorldObject::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effectIndex, int32 const* basePoints) const
int32 WorldObject::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effIndex, int32 const* basePoints /*= nullptr*/) const
{
return spellProto->Effects[effectIndex].CalcValue(this, basePoints, target);
return spellProto->Effects[effIndex].CalcValue(this, basePoints, target);
}
float WorldObject::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
+18 -8
View File
@@ -532,10 +532,13 @@ void SpellCastTargets::Update(WorldObject* caster)
}
}
SpellValue::SpellValue(SpellInfo const* proto)
SpellValue::SpellValue(SpellInfo const* proto, WorldObject const* caster)
{
memset(EffectBasePoints, 0, sizeof(EffectBasePoints));
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
EffectBasePoints[i] = proto->Effects[i].BasePoints;
EffectBasePoints[i] = proto->Effects[i].CalcBaseValue(caster, nullptr);
CustomBasePointsMask = 0;
MaxAffectedTargets = proto->MaxAffectedTargets;
RadiusMod = 1.0f;
AuraStackAmount = 1;
@@ -560,7 +563,7 @@ protected:
Spell::Spell(WorldObject* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) :
m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)),
m_caster((info->HasAttribute(SPELL_ATTR6_ORIGINATE_FROM_CONTROLLER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster)
, m_spellValue(new SpellValue(m_spellInfo)), _spellEvent(nullptr)
, m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr)
{
m_customError = SPELL_CUSTOM_ERROR_NONE;
m_selfContainer = nullptr;
@@ -3037,7 +3040,10 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo&
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
hitInfo.AuraBasePoints[i] = hitInfo.AuraSpellInfo->Effects[i].BasePoints;
hitInfo.AuraBasePoints[i] = (m_spellValue->CustomBasePointsMask & (1 << i)) ?
hitInfo.AuraSpellInfo->Effects[i].BasePoints
: hitInfo.AuraSpellInfo->Effects[i].CalcBaseValue(m_originalCaster, unit);
if (m_spellInfo->Effects[i].Effect != hitInfo.AuraSpellInfo->Effects[i].Effect)
{
hitInfo.AuraSpellInfo = m_spellInfo;
@@ -6903,7 +6909,8 @@ SpellCastResult Spell::CheckMovement() const
int32 Spell::CalculateDamage(uint8 effIndex, Unit const* target) const
{
return m_caster->CalculateSpellDamage(target, m_spellInfo, effIndex, &m_spellValue->EffectBasePoints[effIndex]);
bool needRecalculateBasePoints = !(m_spellValue->CustomBasePointsMask & (1 << effIndex));
return m_caster->CalculateSpellDamage(target, m_spellInfo, effIndex, needRecalculateBasePoints ? nullptr : &m_spellValue->EffectBasePoints[effIndex]);
}
bool Spell::CanAutoCast(Unit* target)
@@ -8282,13 +8289,16 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value)
switch (mod)
{
case SPELLVALUE_BASE_POINT0:
m_spellValue->EffectBasePoints[0] = m_spellInfo->Effects[EFFECT_0].CalcBaseValue(value);
m_spellValue->EffectBasePoints[0] = value;
m_spellValue->CustomBasePointsMask |= 1 << 0;
break;
case SPELLVALUE_BASE_POINT1:
m_spellValue->EffectBasePoints[1] = m_spellInfo->Effects[EFFECT_1].CalcBaseValue(value);
m_spellValue->EffectBasePoints[1] = value;
m_spellValue->CustomBasePointsMask |= 1 << 1;
break;
case SPELLVALUE_BASE_POINT2:
m_spellValue->EffectBasePoints[2] = m_spellInfo->Effects[EFFECT_2].CalcBaseValue(value);
m_spellValue->EffectBasePoints[2] = value;
m_spellValue->CustomBasePointsMask |= 1 << 2;
break;
case SPELLVALUE_RADIUS_MOD:
m_spellValue->RadiusMod = (float)value / 10000;
+2 -1
View File
@@ -247,8 +247,9 @@ class TC_GAME_API SpellCastTargets
struct SpellValue
{
explicit SpellValue(SpellInfo const* proto);
explicit SpellValue(SpellInfo const* proto, WorldObject const* caster);
int32 EffectBasePoints[MAX_SPELL_EFFECTS];
uint32 CustomBasePointsMask;
uint32 MaxAffectedTargets;
float RadiusMod;
uint8 AuraStackAmount;
+83 -148
View File
@@ -435,29 +435,89 @@ bool SpellEffectInfo::IsUnitOwnedAuraEffect() const
return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_2;
}
int32 SpellEffectInfo::CalcValue(WorldObject const* caster, int32 const* bp, Unit const* target) const
int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/, Unit const* target /*= nullptr*/) const
{
float basePointsPerLevel = RealPointsPerLevel;
int32 basePoints = bp ? *bp : BasePoints;
float comboDamage = PointsPerComboPoint;
// TODO: this needs to be a float, not rounded
int basePoints = CalcBaseValue(caster, target);
double value = bp ? *bp : basePoints;
double comboDamage = PointsPerComboPoint;
Unit const* casterUnit = nullptr;
if (caster)
casterUnit = caster->ToUnit();
if (DieSides)
{
// roll in a range <1;EffectDieSides> as of patch 3.3.3
if (DieSides == 1)
value += DieSides;
else
value += (DieSides >= 1) ? irand(1, DieSides) : irand(DieSides, 1);
}
else if (Scaling.Variance != 0.0f)
{
float delta = fabs(Scaling.Variance * 0.5f);
double valueVariance = frand(-delta, delta);
value += double(basePoints) * valueVariance;
}
// base amount modification based on spell lvl vs caster lvl
if (Scaling.Coefficient != 0.0f)
{
int32 level = _spellInfo->SpellLevel;
if (Scaling.ComboPointsCoefficient != 0.0f)
comboDamage = Scaling.ComboPointsCoefficient * value;
}
else
{
if (casterUnit && basePointsPerLevel != 0.0)
{
int32 level = int32(casterUnit->getLevel());
if (level > int32(_spellInfo->MaxLevel) && _spellInfo->MaxLevel > 0)
level = int32(_spellInfo->MaxLevel);
// if base level is greater than spell level, reduce by base level (eg. pilgrims foods)
level -= int32(std::max(_spellInfo->BaseLevel, _spellInfo->SpellLevel));
if (level < 0)
level = 0;
value += level * basePointsPerLevel;
}
}
// random damage
if (casterUnit)
{
// bonus amount from combo points
if (comboDamage)
if (int32 comboPoints = casterUnit->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints())
value += comboDamage * comboPoints;
}
if (_spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_AFFECTS_POINTS))
if (Player const* playerCaster = caster ? caster->ToPlayer() : nullptr)
value += playerCaster->GetFloatValue(PLAYER_MASTERY) * BonusMultiplier;
if (caster)
value = casterUnit->ApplyEffectModifiers(_spellInfo, _effIndex, value);
return int32(round(value));
}
int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* target) const
{
if (Scaling.Coefficient != 0.0f)
{
uint32 level = _spellInfo->SpellLevel;
if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING))
level = target->getLevel();
else if (casterUnit)
level = casterUnit->getLevel();
else if (caster && caster->IsUnit())
level = caster->ToUnit()->getLevel();
if (_spellInfo->HasAttribute(SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING))
if (_spellInfo->BaseLevel && _spellInfo->HasAttribute(SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING))
level = _spellInfo->BaseLevel;
float value = 0.0f;
if (level > 0)
{
if (!_spellInfo->Scaling.Class)
@@ -466,143 +526,20 @@ int32 SpellEffectInfo::CalcValue(WorldObject const* caster, int32 const* bp, Uni
if (GtSpellScalingEntry const* gtScaling = sGtSpellScalingStore.LookupEntry(((_spellInfo->Scaling.Class > 0 ? _spellInfo->Scaling.Class : ((MAX_CLASSES - 1 /*last class*/) - _spellInfo->Scaling.Class)) - 1) * 100 + level - 1))
value = gtScaling->value;
if (level < _spellInfo->Scaling.CastTimeMaxLevel && _spellInfo->Scaling.CastTimeMax)
value *= float(_spellInfo->Scaling.CastTimeMin + (level - 1) * (_spellInfo->Scaling.CastTimeMax - _spellInfo->Scaling.CastTimeMin) / (_spellInfo->Scaling.CastTimeMaxLevel - 1)) / float(_spellInfo->Scaling.CastTimeMax);
if (level < _spellInfo->Scaling.NerfMaxLevel)
value *= ((((1.0 - _spellInfo->Scaling.NerfFactor) * (level - 1)) / (_spellInfo->Scaling.NerfMaxLevel - 1)) + _spellInfo->Scaling.NerfFactor);
value *= _spellInfo->GetSpellScalingMultiplier(level, false);
}
if (Scaling.ComboPointsCoefficient)
comboDamage = Scaling.ComboPointsCoefficient * value;
value *= Scaling.Coefficient;
if (value >= 0.0f && value < 1.0f)
value = 1.0f;
if (value > 0.0f)
value = std::max(value, 1.0f);
if (Scaling.Variance)
{
float delta = fabs(Scaling.Variance * value * 0.5f);
value += frand(-delta, delta);
}
basePoints = int32(round(value));
return int32(round(value));
}
else
{
if (casterUnit && basePointsPerLevel != 0.0f)
{
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))
level = int32(_spellInfo->BaseLevel);
if (!_spellInfo->IsPassive())
level -= int32(_spellInfo->SpellLevel);
basePoints += int32(level * basePointsPerLevel);
}
// roll in a range <1;EffectDieSides> as of patch 3.3.3
int32 randomPoints = int32(DieSides);
switch (randomPoints)
{
case 0: break;
case 1: basePoints += 1; break; // range 1..1
default:
{
// range can have positive (1..rand) and negative (rand..1) values, so order its for irand
int32 randvalue = (randomPoints >= 1)
? irand(1, randomPoints)
: irand(randomPoints, 1);
basePoints += randvalue;
break;
}
}
float value = BasePoints;
return int32(round(value));
}
float value = float(basePoints);
// random damage
if (casterUnit)
{
// bonus amount from combo points
if (_spellInfo->HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DAMAGE) && casterUnit->IsMovedByClient() && comboDamage != 0.f)
if (uint8 comboPoints = casterUnit->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints())
value += comboDamage * comboPoints;
value = casterUnit->ApplyEffectModifiers(_spellInfo, _effIndex, value);
if (!casterUnit->IsControlledByPlayer() &&
_spellInfo->SpellLevel && _spellInfo->SpellLevel != casterUnit->getLevel() &&
!basePointsPerLevel && _spellInfo->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL))
{
bool canEffectScale = false;
switch (Effect)
{
case SPELL_EFFECT_SCHOOL_DAMAGE:
case SPELL_EFFECT_DUMMY:
case SPELL_EFFECT_POWER_DRAIN:
case SPELL_EFFECT_HEALTH_LEECH:
case SPELL_EFFECT_HEAL:
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_POWER_BURN:
case SPELL_EFFECT_SCRIPT_EFFECT:
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
case SPELL_EFFECT_FORCE_CAST_WITH_VALUE:
case SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE:
case SPELL_EFFECT_TRIGGER_MISSILE_SPELL_WITH_VALUE:
canEffectScale = true;
break;
default:
break;
}
switch (ApplyAuraName)
{
case SPELL_AURA_PERIODIC_DAMAGE:
case SPELL_AURA_DUMMY:
case SPELL_AURA_PERIODIC_HEAL:
case SPELL_AURA_DAMAGE_SHIELD:
case SPELL_AURA_PROC_TRIGGER_DAMAGE:
case SPELL_AURA_PERIODIC_LEECH:
case SPELL_AURA_PERIODIC_MANA_LEECH:
case SPELL_AURA_SCHOOL_ABSORB:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
canEffectScale = true;
break;
default:
break;
}
if (canEffectScale)
{
GtNPCManaCostScalerEntry const* spellScaler = sGtNPCManaCostScalerStore.LookupEntry(_spellInfo->SpellLevel - 1);
GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.LookupEntry(casterUnit->getLevel() - 1);
if (spellScaler && casterScaler)
{
value *= casterScaler->ratio / spellScaler->ratio;
if (casterUnit->getLevel() > 80)
value *= (casterUnit->getLevel() - 80) * 4.4f; // Cataclysm creatures have a way higher jump in stats than previous expansions so we use this estimated value based on combat log packet research
}
}
}
}
return uint32(std::floor(value + 0.5f));
}
int32 SpellEffectInfo::CalcBaseValue(int32 value) const
{
if (DieSides == 0)
return value;
else
return value - 1;
}
float SpellEffectInfo::CalcValueMultiplier(WorldObject* caster, Spell* spell) const
@@ -3397,7 +3334,7 @@ float SpellInfo::CalculateScaledCoefficient(Unit const* caster, float coefficien
if (coefficient == 0.f || !caster || SpellScalingId == 0)
return coefficient;
return coefficient *= GetSpellScalingMultiplier(caster);
return coefficient *= GetSpellScalingMultiplier(caster->getLevel());
}
float SpellInfo::GetMinRange(bool positive) const
@@ -3693,7 +3630,7 @@ int32 SpellInfo::CalcPowerCost(WorldObject const* caster, SpellSchoolMask school
// Scaling
if (SpellScalingId)
powerCost = int32(ceilf(GetSpellScalingMultiplier(unitCaster, true) * (double)powerCost));
powerCost = int32(ceilf(GetSpellScalingMultiplier(unitCaster->getLevel(), true) * (double)powerCost));
else
{
uint32 manaCostPerLevel = invertSign ? -int32(ManaCostPerlevel) : ManaCostPerlevel;
@@ -3763,27 +3700,25 @@ int32 SpellInfo::CalcPowerCost(WorldObject const* caster, SpellSchoolMask school
return powerCost;
}
float SpellInfo::GetSpellScalingMultiplier(WorldObject const* caster, bool isPowerCostRelated /*= false*/) const
float SpellInfo::GetSpellScalingMultiplier(int32 targetLevel, bool isPowerCostRelated /*= false*/) const
{
if (!caster || !caster->IsUnit() || SpellScalingId == 0)
if (targetLevel <= 0 || Scaling.Class == 0)
return 1.f;
float multiplier = 1.f;
float scalingMultiplier = 1.f;
uint8 casterLevel = caster->ToUnit()->getLevel();
if (casterLevel < Scaling.CastTimeMaxLevel && Scaling.CastTimeMax)
if (targetLevel < Scaling.CastTimeMaxLevel && Scaling.CastTimeMax)
{
int32 castTime = Scaling.CastTimeMin + ((casterLevel - 1) * (Scaling.CastTimeMax - Scaling.CastTimeMin)) / (Scaling.CastTimeMaxLevel - 1);
multiplier = castTime / (float)Scaling.CastTimeMax;
scalingMultiplier = castTime / (float)Scaling.CastTimeMax;
int32 castTime = Scaling.CastTimeMin + ((targetLevel - 1) * (Scaling.CastTimeMax - Scaling.CastTimeMin)) / (Scaling.CastTimeMaxLevel - 1);
multiplier = castTime / static_cast<float>(Scaling.CastTimeMax);
scalingMultiplier = castTime / static_cast<float>(Scaling.CastTimeMax);
}
if (!isPowerCostRelated)
{
if (casterLevel < Scaling.NerfMaxLevel)
scalingMultiplier = ((((1.f - Scaling.NerfFactor) * (casterLevel - 1)) / (Scaling.NerfMaxLevel - 1)) + Scaling.NerfFactor) * multiplier;
if (targetLevel < Scaling.NerfMaxLevel)
scalingMultiplier = ((((1.f - Scaling.NerfFactor) * (targetLevel - 1)) / (Scaling.NerfMaxLevel - 1)) + Scaling.NerfFactor) * multiplier;
}
return scalingMultiplier;
+2 -2
View File
@@ -294,7 +294,7 @@ public:
bool IsUnitOwnedAuraEffect() const;
int32 CalcValue(WorldObject const* caster = nullptr, int32 const* basePoints = nullptr, Unit const* target = nullptr) const;
int32 CalcBaseValue(int32 value) const;
int32 CalcBaseValue(WorldObject const* caster, Unit const* target) const;
float CalcValueMultiplier(WorldObject* caster, Spell* spell = nullptr) const;
float CalcDamageMultiplier(WorldObject* caster, Spell* spell = nullptr) const;
@@ -592,7 +592,7 @@ class TC_GAME_API SpellInfo
uint32 GetRecoveryTime() const;
int32 CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const;
float GetSpellScalingMultiplier(WorldObject const* caster, bool isPowerCostRelated = false) const;
float GetSpellScalingMultiplier(int32 targetLevel, bool isPowerCostRelated = false) const;
bool IsRanked() const;
uint8 GetRank() const;