diff options
author | Shauren <shauren.trinity@gmail.com> | 2021-03-02 23:14:50 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-03-02 23:14:50 +0100 |
commit | 0e3666bfbc88bdcc9344dd3ebcfed0f205d122b2 (patch) | |
tree | 86d0517833425705efe438c0107edc83ac0e4750 | |
parent | 624881bef5c90a91e4c59e5bf404d8775c2ca55d (diff) |
Core/Spells: Fixed calculation spell optional power cost when any modifiers are applied
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 306 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 2 |
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; |