Core/Spells: ported spell effect value calculation from cata_classic branch
This commit is contained in:
1 parent
57416b873f
commit
4524c29f2c
5 files changed
+107
-161
No files matched your search
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user