aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2021-03-02 23:14:50 +0100
committerShauren <shauren.trinity@gmail.com>2021-03-02 23:14:50 +0100
commit0e3666bfbc88bdcc9344dd3ebcfed0f205d122b2 (patch)
tree86d0517833425705efe438c0107edc83ac0e4750
parent624881bef5c90a91e4c59e5bf404d8775c2ca55d (diff)
Core/Spells: Fixed calculation spell optional power cost when any modifiers are applied
-rw-r--r--src/server/game/Spells/SpellInfo.cpp306
-rw-r--r--src/server/game/Spells/SpellInfo.h2
2 files changed, 171 insertions, 137 deletions
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index b569df6b710..0a9ab171e8d 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -3737,46 +3737,49 @@ uint32 SpellInfo::GetRecoveryTime() const
return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime;
}
-std::vector<SpellPowerCost> SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell) const
+Optional<SpellPowerCost> SpellInfo::CalcPowerCost(Powers powerType, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const
{
- std::vector<SpellPowerCost> costs;
- costs.reserve(MAX_POWERS_PER_SPELL);
- int32 healthCost = 0;
-
- for (SpellPowerEntry const* power : PowerCosts)
+ auto itr = std::find_if(PowerCosts.cbegin(), PowerCosts.cend(), [powerType](SpellPowerEntry const* spellPowerEntry)
{
- if (!power)
- continue;
+ return spellPowerEntry->PowerType == powerType;
+ });
+ if (itr == PowerCosts.cend())
+ return {};
- if (power->RequiredAuraSpellID && !caster->HasAura(power->RequiredAuraSpellID))
- continue;
+ return CalcPowerCost(*itr, optionalCost, caster, schoolMask, spell);
+}
- // Spell drain all exist power on cast (Only paladin lay of Hands)
- if (HasAttribute(SPELL_ATTR1_DRAIN_ALL_POWER))
+Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const
+{
+ // Spell drain all exist power on cast (Only paladin lay of Hands)
+ if (HasAttribute(SPELL_ATTR1_DRAIN_ALL_POWER))
+ {
+ // If power type - health drain all
+ if (power->PowerType == POWER_HEALTH)
{
- // If power type - health drain all
- if (power->PowerType == POWER_HEALTH)
- {
- healthCost = caster->GetHealth();
- continue;
- }
- // Else drain all power
- if (power->PowerType < MAX_POWERS)
- {
- SpellPowerCost cost;
- cost.Power = Powers(power->PowerType);
- cost.Amount = caster->GetPower(cost.Power);
- costs.push_back(cost);
- continue;
- }
-
- TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id);
- continue;
+ SpellPowerCost cost;
+ cost.Power = POWER_HEALTH;
+ cost.Amount = caster->GetHealth();
+ return cost;
+ }
+ // Else drain all power
+ if (power->PowerType < MAX_POWERS)
+ {
+ SpellPowerCost cost;
+ cost.Power = Powers(power->PowerType);
+ cost.Amount = caster->GetPower(cost.Power);
+ return cost;
}
- // Base powerCost
- int32 powerCost = power->ManaCost;
- bool initiallyNegative = powerCost < 0;
+ TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id);
+ return {};
+ }
+
+ // Base powerCost
+ int32 powerCost = 0;
+ if (!optionalCost)
+ {
+ powerCost = power->ManaCost;
// PCT cost from total amount
if (power->PowerCostPct)
{
@@ -3784,161 +3787,190 @@ std::vector<SpellPowerCost> SpellInfo::CalcPowerCost(Unit const* caster, SpellSc
{
// health as power used
case POWER_HEALTH:
- powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostPct));
+ if (G3D::fuzzyEq(power->PowerCostPct, 0.0f))
+ powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostMaxPct));
+ else
+ powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostPct));
break;
case POWER_MANA:
powerCost += int32(CalculatePct(caster->GetCreateMana(), power->PowerCostPct));
break;
- case POWER_RAGE:
- case POWER_FOCUS:
- case POWER_ENERGY:
- powerCost += int32(CalculatePct(caster->GetMaxPower(Powers(power->PowerType)), power->PowerCostPct));
- break;
- case POWER_RUNES:
- case POWER_RUNIC_POWER:
- TC_LOG_DEBUG("spells", "CalculateManaCost: Not implemented yet!");
- break;
+ case POWER_ALTERNATE_POWER:
+ TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id);
+ return {};
default:
- TC_LOG_ERROR("spells", "CalculateManaCost: Unknown power type '%d' in spell %d", power->PowerType, Id);
- continue;
+ {
+ if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(Powers(power->PowerType)))
+ {
+ powerCost += int32(CalculatePct(powerTypeEntry->MaxBasePower, power->PowerCostPct));
+ break;
+ }
+
+ TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id);
+ return {};
+ }
}
}
-
- if (power->PowerCostMaxPct)
- healthCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostMaxPct));
-
- int32 optionalCost = int32(power->OptionalCost);
- optionalCost += caster->GetTotalAuraModifier(SPELL_AURA_MOD_ADDITIONAL_POWER_COST, [this, power](AuraEffect const* aurEff) -> bool
+ }
+ else
+ {
+ powerCost = int32(power->OptionalCost);
+ powerCost += caster->GetTotalAuraModifier(SPELL_AURA_MOD_ADDITIONAL_POWER_COST, [this, power](AuraEffect const* aurEff) -> bool
{
return aurEff->GetMiscValue() == power->PowerType
&& aurEff->IsAffectingSpell(this);
});
+ }
+
+ bool initiallyNegative = powerCost < 0;
- if (optionalCost)
+ // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
+ if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST))
+ {
+ uint32 speed = 0;
+ if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm()))
+ speed = ss->CombatRoundTime;
+ else
{
- int32 remainingPower = caster->GetPower(Powers(power->PowerType)) - powerCost;
- powerCost += RoundToInterval<int32>(remainingPower, 0, optionalCost);
+ WeaponAttackType slot = BASE_ATTACK;
+ if (!HasAttribute(SPELL_ATTR3_MAIN_HAND) && HasAttribute(SPELL_ATTR3_REQ_OFFHAND))
+ slot = OFF_ATTACK;
+
+ speed = caster->GetBaseAttackTime(slot);
}
- if (power->PowerType != POWER_HEALTH)
+ powerCost += speed / 100;
+ }
+
+ if (power->PowerType != POWER_HEALTH)
+ {
+ if (!optionalCost)
{
// Flat mod from caster auras by spell school and power type
- int32 flatMod = 0;
- Unit::AuraEffectList const& auras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL);
- for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
+ for (AuraEffect const* aura : caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL))
{
- if (!((*i)->GetMiscValue() & schoolMask))
+ if (!(aura->GetMiscValue() & schoolMask))
continue;
- if (!((*i)->GetMiscValueB() & (1 << power->PowerType)))
+ if (!(aura->GetMiscValueB() & (1 << power->PowerType)))
continue;
- flatMod += (*i)->GetAmount();
+ powerCost += aura->GetAmount();
}
-
- powerCost += flatMod;
}
- // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
- if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST))
+ // PCT mod from user auras by spell school and power type
+ for (auto schoolCostPct : caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT))
{
- uint32 speed = 0;
- if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm()))
- speed = ss->CombatRoundTime;
- else
- {
- WeaponAttackType slot = BASE_ATTACK;
- if (!HasAttribute(SPELL_ATTR3_MAIN_HAND) && HasAttribute(SPELL_ATTR3_REQ_OFFHAND))
- slot = OFF_ATTACK;
+ if (!(schoolCostPct->GetMiscValue() & schoolMask))
+ continue;
- speed = caster->GetBaseAttackTime(slot);
- }
+ if (!(schoolCostPct->GetMiscValueB() & (1 << power->PowerType)))
+ continue;
- powerCost += speed / 100;
+ powerCost += CalculatePct(powerCost, schoolCostPct->GetAmount());
}
+ }
- // Apply cost mod by spell
- if (Player* modOwner = caster->GetSpellModOwner())
+ // Apply cost mod by spell
+ if (Player* modOwner = caster->GetSpellModOwner())
+ {
+ Optional<SpellModOp> mod;
+ switch (power->OrderIndex)
{
- switch (power->OrderIndex)
- {
- case 0:
- modOwner->ApplySpellMod(this, SPELLMOD_COST, powerCost, spell);
- break;
- case 1:
- modOwner->ApplySpellMod(this, SPELLMOD_SPELL_COST2, powerCost, spell);
- break;
- case 2:
- modOwner->ApplySpellMod(this, SPELLMOD_SPELL_COST3, powerCost, spell);
- break;
- default:
- break;
- }
+ case 0:
+ mod = SPELLMOD_COST;
+ break;
+ case 1:
+ mod = SPELLMOD_SPELL_COST2;
+ break;
+ case 2:
+ mod = SPELLMOD_SPELL_COST3;
+ break;
+ default:
+ break;
}
- if (!caster->IsControlledByPlayer() && G3D::fuzzyEq(power->PowerCostPct, 0.0f) && SpellLevel && power->PowerType == POWER_MANA)
+ if (mod)
{
- if (HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION))
+ if (!optionalCost)
+ modOwner->ApplySpellMod(this, *mod, powerCost, spell);
+ else
{
- GtNpcManaCostScalerEntry const* spellScaler = sNpcManaCostScalerGameTable.GetRow(SpellLevel);
- GtNpcManaCostScalerEntry const* casterScaler = sNpcManaCostScalerGameTable.GetRow(caster->getLevel());
- if (spellScaler && casterScaler)
- powerCost *= casterScaler->Scaler / spellScaler->Scaler;
+ // optional cost ignores flat modifiers
+ int32 flatMod = 0;
+ float pctMod = 1.0f;
+ modOwner->GetSpellModValues(this, *mod, spell, powerCost, &flatMod, &pctMod);
+ powerCost = int32(powerCost * pctMod);
}
}
+ }
- // PCT mod from user auras by spell school and power type
- Unit::AuraEffectList const& aurasPct = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT);
- for (Unit::AuraEffectList::const_iterator i = aurasPct.begin(); i != aurasPct.end(); ++i)
+ if (!caster->IsControlledByPlayer() && G3D::fuzzyEq(power->PowerCostPct, 0.0f) && SpellLevel && power->PowerType == POWER_MANA)
+ {
+ if (HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION))
{
- if (!((*i)->GetMiscValue() & schoolMask))
- continue;
+ GtNpcManaCostScalerEntry const* spellScaler = sNpcManaCostScalerGameTable.GetRow(SpellLevel);
+ GtNpcManaCostScalerEntry const* casterScaler = sNpcManaCostScalerGameTable.GetRow(caster->getLevel());
+ if (spellScaler && casterScaler)
+ powerCost *= casterScaler->Scaler / spellScaler->Scaler;
+ }
+ }
- if (!((*i)->GetMiscValueB() & (1 << power->PowerType)))
- continue;
+ if (power->PowerType == POWER_MANA)
+ powerCost = float(powerCost) * (1.0f + caster->m_unitData->ManaCostMultiplier);
- powerCost += CalculatePct(powerCost, (*i)->GetAmount());
- }
+ // power cost cannot become negative if initially positive
+ if (initiallyNegative != (powerCost < 0))
+ powerCost = 0;
+
+ SpellPowerCost cost;
+ cost.Power = Powers(power->PowerType);
+ cost.Amount = powerCost;
+ return cost;
+}
- if (power->PowerType == POWER_MANA)
- powerCost = float(powerCost) * (1.0f + caster->m_unitData->ManaCostMultiplier);
- else if (power->PowerType == POWER_HEALTH)
+std::vector<SpellPowerCost> SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell) const
+{
+ std::vector<SpellPowerCost> costs;
+ costs.reserve(MAX_POWERS_PER_SPELL);
+
+ auto getOrCreatePowerCost = [&](Powers powerType) -> SpellPowerCost&
+ {
+ auto itr = std::find_if(costs.begin(), costs.end(), [powerType](SpellPowerCost const& cost)
{
- healthCost += powerCost;
+ return cost.Power == powerType;
+ });
+ if (itr != costs.end())
+ return *itr;
+
+ SpellPowerCost cost;
+ cost.Power = powerType;
+ cost.Amount = 0;
+ costs.push_back(cost);
+ return costs.back();
+ };
+
+ for (SpellPowerEntry const* power : PowerCosts)
+ {
+ if (!power)
continue;
- }
- // power cost cannot become negative if initially positive
- if (initiallyNegative != (powerCost < 0))
- powerCost = 0;
+ if (power->RequiredAuraSpellID && !caster->HasAura(power->RequiredAuraSpellID))
+ continue;
- bool found = false;
- for (SpellPowerCost& cost : costs)
- {
- if (cost.Power == Powers(power->PowerType))
- {
- cost.Amount += powerCost;
- found = true;
- }
- }
+ if (Optional<SpellPowerCost> cost = CalcPowerCost(power, false, caster, schoolMask, spell))
+ getOrCreatePowerCost(cost->Power).Amount += cost->Amount;
- if (!found)
+ if (Optional<SpellPowerCost> optionalCost = CalcPowerCost(power, true, caster, schoolMask, spell))
{
- SpellPowerCost cost;
- cost.Power = Powers(power->PowerType);
- cost.Amount = powerCost;
- costs.push_back(cost);
+ SpellPowerCost& cost = getOrCreatePowerCost(optionalCost->Power);
+ int32 remainingPower = caster->GetPower(optionalCost->Power) - cost.Amount;
+ if (remainingPower > 0)
+ cost.Amount += std::min(optionalCost->Amount, remainingPower);
}
}
- if (healthCost > 0)
- {
- SpellPowerCost cost;
- cost.Power = POWER_HEALTH;
- cost.Amount = healthCost;
- costs.push_back(cost);
- }
-
return costs;
}
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index be0dda67508..8d9e9695752 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -653,6 +653,8 @@ class TC_GAME_API SpellInfo
uint32 CalcCastTime(Spell* spell = nullptr) const;
uint32 GetRecoveryTime() const;
+ Optional<SpellPowerCost> CalcPowerCost(Powers powerType, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const;
+ Optional<SpellPowerCost> CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const;
std::vector<SpellPowerCost> CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const;
float CalcProcPPM(Unit* caster, int32 itemLevel) const;