diff options
| author | Shauren <shauren.trinity@gmail.com> | 2015-03-08 13:31:57 +0100 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2015-03-08 13:31:57 +0100 |
| commit | 0ba2e0d5ee416a2daf89d53877984fb0cf27ca9b (patch) | |
| tree | 0cb086bb038bf7d3164b9c25dab9ecd1bf93cad6 /src/server/game/Spells | |
| parent | 9ffeb58d094ddba9bffb33a79b33ade9af9f5c00 (diff) | |
Core/Spells: Implemented multiple spell power costs
Diffstat (limited to 'src/server/game/Spells')
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 32 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 44 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 1 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 267 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.h | 6 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 15 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 270 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.h | 16 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellMgr.cpp | 4 |
9 files changed, 305 insertions, 350 deletions
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 6112975a1b2..f7a87dddebe 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -6239,30 +6239,11 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo()); - bool haveCastItem = !GetBase()->GetCastItemGUID().IsEmpty(); - - // Health Funnel - // damage caster for heal amount - if (target != caster && GetSpellInfo()->AttributesEx2 & SPELL_ATTR2_HEALTH_FUNNEL && GetSpellInfo()->Id != 755) - { - uint32 funnelDamage = GetSpellInfo()->ManaPerSecond; // damage is not affected by spell power - - if ((int32)funnelDamage > gain && gain > 0) - funnelDamage = gain; - - uint32 funnelAbsorb = 0; - caster->DealDamageMods(caster, funnelDamage, &funnelAbsorb); - caster->SendSpellNonMeleeDamageLog(caster, GetId(), funnelDamage, GetSpellInfo()->GetSchoolMask(), funnelAbsorb, 0, false, 0, false); - - CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - caster->DealDamage(caster, funnelDamage, &cleanDamage, NODAMAGE, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true); - } - uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_HOT; // ignore item heals - if (!haveCastItem) + if (GetBase()->GetCastItemGUID().IsEmpty()) caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); } @@ -6286,17 +6267,6 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con // ignore negative values (can be result apply spellmods to aura damage int32 drainAmount = std::max(m_amount, 0); - // Special case: draining x% of mana (up to a maximum of 2*x% of the caster's maximum mana) - // It's mana percent cost spells, m_amount is percent drain from target - if (m_spellInfo->ManaCostPercentage) - { - // max value - int32 maxmana = CalculatePct(caster->GetMaxPower(powerType), drainAmount * 2.0f); - ApplyPct(drainAmount, target->GetMaxPower(powerType)); - if (drainAmount > maxmana) - drainAmount = maxmana; - } - TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s power leech of %s for %u dmg inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), drainAmount, GetId()); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 6fa4936ffa0..9f09242b37c 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -369,7 +369,12 @@ m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr) { - if (m_spellInfo->ManaPerSecond) + std::vector<SpellPowerEntry const*> powers = sDB2Manager.GetSpellPowers(GetId(), caster ? caster->GetMap()->GetDifficultyID() : DIFFICULTY_NONE); + for (SpellPowerEntry const* power : powers) + if (power->ManaCostPerSecond != 0 || power->ManaCostPercentagePerSecond > 0.0f) + m_periodicCosts.push_back(power); + + if (!m_periodicCosts.empty()) m_timeCla = 1 * IN_MILLISECONDS; m_maxDuration = CalcMaxDuration(caster); @@ -737,22 +742,37 @@ void Aura::Update(uint32 diff, Unit* caster) m_timeCla -= diff; else if (caster) { - if (int32 manaPerSecond = m_spellInfo->ManaPerSecond) + if (!m_periodicCosts.empty()) { m_timeCla += 1000 - diff; - Powers powertype = Powers(m_spellInfo->PowerType); - if (powertype == POWER_HEALTH) + for (SpellPowerEntry const* power : m_periodicCosts) { - if (int32(caster->GetHealth()) > manaPerSecond) - caster->ModifyHealth(-manaPerSecond); + if (power->RequiredAura && !caster->HasAura(power->RequiredAura)) + continue; + + int32 manaPerSecond = power->ManaCostPerSecond; + Powers powertype = Powers(power->PowerType); + if (powertype != POWER_HEALTH) + manaPerSecond += int32(CalculatePct(caster->GetMaxPower(powertype), power->ManaCostPercentagePerSecond)); else - Remove(); + manaPerSecond += int32(CalculatePct(caster->GetMaxHealth(), power->ManaCostPercentagePerSecond)); + + if (manaPerSecond) + { + if (powertype == POWER_HEALTH) + { + if (int32(caster->GetHealth()) > manaPerSecond) + caster->ModifyHealth(-manaPerSecond); + else + Remove(); + } + else if (int32(caster->GetPower(powertype)) >= manaPerSecond) + caster->ModifyPower(powertype, -manaPerSecond); + else + Remove(); + } } - else if (int32(caster->GetPower(powertype)) >= manaPerSecond) - caster->ModifyPower(powertype, -manaPerSecond); - else - Remove(); } } } @@ -809,7 +829,7 @@ void Aura::RefreshDuration(bool withMods) else SetDuration(GetMaxDuration()); - if (m_spellInfo->ManaPerSecond) + if (!m_periodicCosts.empty()) m_timeCla = 1 * IN_MILLISECONDS; } diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 8710f312dda..adab5db5843 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -301,6 +301,7 @@ class Aura int32 m_maxDuration; // Max aura duration int32 m_duration; // Current time int32 m_timeCla; // Timer for power per sec calcultion + std::vector<SpellPowerEntry const*> m_periodicCosts;// Periodic costs int32 m_updateTargetMapInterval; // Timer for UpdateTargetMapOfEffect uint8 const m_casterLevel; // Aura level (store caster level for correct show level dep amount) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 44012185f11..ef32ce4275b 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -655,7 +655,6 @@ m_spellValue(new SpellValue(caster->GetMap()->GetDifficultyID(), m_spellInfo)), m_autoRepeat = m_spellInfo->IsAutoRepeatRangedSpell(); m_runesState = 0; - m_powerCost = 0; // setup to correct value in Spell::prepare, must not be used before. m_casttime = 0; // setup to correct value in Spell::prepare, must not be used before. m_timer = 0; // will set to castime in prepare m_channeledDuration = 0; // will be setup in Spell::handle_immediate @@ -2966,7 +2965,8 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); // Fill cost data (not use power for item casts - m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); + if (!m_CastItem) + m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); @@ -3504,9 +3504,6 @@ void Spell::_handle_finish_phase() // Real add combo points from effects if (m_comboPointGain) m_caster->m_movedPlayer->GainSpellComboPoints(m_comboPointGain); - - if (m_spellInfo->PowerType == POWER_HOLY_POWER && m_caster->m_movedPlayer->getClass() == CLASS_PALADIN) - HandleHolyPower(m_caster->m_movedPlayer); } if (m_caster->m_extraAttacks && HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) @@ -3856,10 +3853,11 @@ void Spell::SendSpellStart() if ((m_caster->GetTypeId() == TYPEID_PLAYER || (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) - && m_spellInfo->PowerType != POWER_HEALTH) + && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellInfo::CostData const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end()) castFlags |= CAST_FLAG_POWER_LEFT_SELF; - if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNES) + if (m_spellInfo->RuneCostID && + std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellInfo::CostData const& cost) { return cost.Power == POWER_RUNES; }) != m_powerCost.end()) castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it WorldPackets::Spells::SpellStart packet; @@ -3880,11 +3878,13 @@ void Spell::SendSpellStart() if (castFlags & CAST_FLAG_POWER_LEFT_SELF) { - /// @todo Implement multiple power types - WorldPackets::Spells::SpellPowerData powerData; - powerData.Type = m_spellInfo->PowerType; - powerData.Cost = m_caster->GetPower((Powers)m_spellInfo->PowerType); - castData.RemainingPower.push_back(powerData); + for (SpellInfo::CostData const& cost : m_powerCost) + { + WorldPackets::Spells::SpellPowerData powerData; + powerData.Type = cost.Power; + powerData.Cost = m_caster->GetPower(cost.Power); + castData.RemainingPower.push_back(powerData); + } } if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list @@ -3936,68 +3936,6 @@ void Spell::SendSpellStart() castData.Predict.Type = 0; }**/ - /*WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2)); - if (m_CastItem) - data << m_CastItem->GetPackGUID(); - else - data << m_caster->GetPackGUID(); - - data << m_caster->GetPackGUID(); - data << uint8(m_cast_count); // pending spell cast? - data << uint32(m_spellInfo->Id); // spellId - data << uint32(castFlags); // cast flags - data << uint32(m_timer); // delay? - data << uint32(m_casttime); - - m_targets.Write(data); - - if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - data << uint32(m_caster->GetPower((Powers)m_spellInfo->PowerType)); - - if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list - { - //TODO: There is a crash caused by a spell with CAST_FLAG_RUNE_LIST casted by a creature - //The creature is the mover of a player, so HandleCastSpellOpcode uses it as the caster - if (Player* player = m_caster->ToPlayer()) - { - data << uint8(m_runesState); // runes state before - data << uint8(player->GetRunesState()); // runes state after - for (uint8 i = 0; i < MAX_RUNES; ++i) - { - // float casts ensure the division is performed on floats as we need float result - float baseCd = float(player->GetRuneBaseCooldown(i)); - data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed - } - } - else - { - data << uint8(0); - data << uint8(0); - for (uint8 i = 0; i < MAX_RUNES; ++i) - data << uint8(0); - } - } - - if (castFlags & CAST_FLAG_PROJECTILE) - { - data << uint32(0); // Ammo display ID - data << uint32(0); // Inventory Type - } - - if (castFlags & CAST_FLAG_IMMUNITY) - { - data << uint32(0); - data << uint32(0); - } - - if (castFlags & CAST_FLAG_HEAL_PREDICTION) - { - data << uint32(0); - data << uint8(0); // unkByte - // if (unkByte == 2) - // data.append(0); - }*/ - m_caster->SendMessageToSet(packet.Write(), true); } @@ -4017,13 +3955,13 @@ void Spell::SendSpellGo() if ((m_caster->GetTypeId() == TYPEID_PLAYER || (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) - && m_spellInfo->PowerType != POWER_HEALTH) + && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellInfo::CostData const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end()) castFlags |= CAST_FLAG_POWER_LEFT_SELF; // should only be sent to self, but the current messaging doesn't make that possible if ((m_caster->GetTypeId() == TYPEID_PLAYER) && (m_caster->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->RuneCostID - && m_spellInfo->PowerType == POWER_RUNES + && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellInfo::CostData const& cost) { return cost.Power == POWER_RUNES; }) != m_powerCost.end() && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) { castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it @@ -4059,11 +3997,13 @@ void Spell::SendSpellGo() if (castFlags & CAST_FLAG_POWER_LEFT_SELF) { - /// @todo Implement multiple power types - WorldPackets::Spells::SpellPowerData powerData; - powerData.Type = m_spellInfo->PowerType; - powerData.Cost = m_caster->GetPower((Powers)m_spellInfo->PowerType); - castData.RemainingPower.push_back(powerData); + for (SpellInfo::CostData const& cost : m_powerCost) + { + WorldPackets::Spells::SpellPowerData powerData; + powerData.Type = cost.Power; + powerData.Cost = m_caster->GetPower(cost.Power); + castData.RemainingPower.push_back(powerData); + } } if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list @@ -4449,64 +4389,67 @@ void Spell::TakePower() return; } - Powers powerType = Powers(m_spellInfo->PowerType); - bool hit = true; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + for (SpellInfo::CostData& cost : m_powerCost) { - if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNES) + Powers powerType = Powers(cost.Power); + bool hit = true; + if (m_caster->GetTypeId() == TYPEID_PLAYER) { - ObjectGuid targetGUID = m_targets.GetUnitTargetGUID(); - if (!targetGUID.IsEmpty()) + if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNES) { - for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + ObjectGuid targetGUID = m_targets.GetUnitTargetGUID(); + if (!targetGUID.IsEmpty()) { - if (ihit->targetGUID == targetGUID) + for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { - if (ihit->missCondition != SPELL_MISS_NONE) + if (ihit->targetGUID == targetGUID) { - hit = false; - //lower spell cost on fail (by talent aura) - if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); + if (ihit->missCondition != SPELL_MISS_NONE) + { + hit = false; + //lower spell cost on fail (by talent aura) + if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, cost.Amount); + } + break; } - break; } } } } - } - if (powerType == POWER_RUNES) - { - TakeRunePower(hit); - return; - } + if (powerType == POWER_RUNES) + { + TakeRunePower(hit); + continue; + } - if (!m_powerCost) - return; + if (!cost.Amount) + continue; - // health as power used - if (powerType == POWER_HEALTH) - { - m_caster->ModifyHealth(-(int32)m_powerCost); - return; - } + // health as power used + if (powerType == POWER_HEALTH) + { + m_caster->ModifyHealth(-cost.Amount); + continue; + } - if (powerType >= MAX_POWERS) - { - TC_LOG_ERROR("spells", "Spell::TakePower: Unknown power type '%d'", powerType); - return; - } + if (powerType >= MAX_POWERS) + { + TC_LOG_ERROR("spells", "Spell::TakePower: Unknown power type '%d'", powerType); + continue; + } - if (hit) - m_caster->ModifyPower(powerType, -m_powerCost); - else - m_caster->ModifyPower(powerType, -irand(0, m_powerCost/4)); + if (hit) + m_caster->ModifyPower(powerType, -cost.Amount); + else + m_caster->ModifyPower(powerType, -irand(0, cost.Amount / 4)); + } } SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) { - if (m_spellInfo->PowerType != POWER_RUNES || !runeCostID) + if (std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellInfo::CostData const& cost) { return cost.Power == POWER_RUNES; }) == m_powerCost.end() || !runeCostID) return SPELL_CAST_OK; Player* player = m_caster->ToPlayer(); @@ -4714,42 +4657,6 @@ void Spell::HandleThreatSpells() TC_LOG_DEBUG("spells", "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, m_spellInfo->_IsPositiveSpell() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size())); } -void Spell::HandleHolyPower(Player* caster) -{ - if (!caster) - return; - - bool hit = true; - Player* modOwner = caster->GetSpellModOwner(); - - m_powerCost = caster->GetPower(POWER_HOLY_POWER); // Always use all the holy power we have - - if (!m_powerCost || !modOwner) - return; - - ObjectGuid targetGUID = m_targets.GetUnitTargetGUID(); - if (!targetGUID.IsEmpty()) - { - for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->targetGUID == targetGUID) - { - if (ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS) - hit = false; - - break; - } - } - - // The spell did hit the target, apply aura cost mods if there are any. - if (hit) - { - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, m_powerCost); - m_caster->ModifyPower(POWER_HOLY_POWER, -m_powerCost); - } - } -} - void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOTarget, uint32 i, SpellEffectHandleMode mode) { effectHandleMode = mode; @@ -5961,34 +5868,36 @@ SpellCastResult Spell::CheckPower() if (m_CastItem) return SPELL_CAST_OK; - // health as power used - need check health amount - if (m_spellInfo->PowerType == POWER_HEALTH) + for (SpellInfo::CostData const& cost : m_powerCost) { - if (int32(m_caster->GetHealth()) <= m_powerCost) - return SPELL_FAILED_CASTER_AURASTATE; - return SPELL_CAST_OK; - } - // Check valid power type - if (m_spellInfo->PowerType >= MAX_POWERS) - { - TC_LOG_ERROR("spells", "Spell::CheckPower: Unknown power type '%d'", m_spellInfo->PowerType); - return SPELL_FAILED_UNKNOWN; - } + // health as power used - need check health amount + if (cost.Power == POWER_HEALTH) + { + if (int32(m_caster->GetHealth()) <= cost.Amount) + return SPELL_FAILED_CASTER_AURASTATE; + continue; + } + // Check valid power type + if (cost.Power >= MAX_POWERS) + { + TC_LOG_ERROR("spells", "Spell::CheckPower: Unknown power type '%d'", cost.Power); + return SPELL_FAILED_UNKNOWN; + } - //check rune cost only if a spell has PowerType == POWER_RUNES - if (m_spellInfo->PowerType == POWER_RUNES) - { - SpellCastResult failReason = CheckRuneCost(m_spellInfo->RuneCostID); - if (failReason != SPELL_CAST_OK) - return failReason; + //check rune cost only if a spell has PowerType == POWER_RUNES + if (cost.Power == POWER_RUNES) + { + SpellCastResult failReason = CheckRuneCost(m_spellInfo->RuneCostID); + if (failReason != SPELL_CAST_OK) + return failReason; + } + + // Check power amount + if (int32(m_caster->GetPower(cost.Power)) < cost.Amount) + return SPELL_FAILED_NO_POWER; } - // Check power amount - Powers powerType = Powers(m_spellInfo->PowerType); - if (int32(m_caster->GetPower(powerType)) < m_powerCost) - return SPELL_FAILED_NO_POWER; - else - return SPELL_CAST_OK; + return SPELL_CAST_OK; } SpellCastResult Spell::CheckItems() diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 42c5258f4f8..113ac31b461 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -468,7 +468,6 @@ class Spell void SendChannelStart(uint32 duration); void SendResurrectRequest(Player* target); - void HandleHolyPower(Player* caster); void HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOTarget, uint32 i, SpellEffectHandleMode mode); void HandleThreatSpells(); @@ -517,7 +516,7 @@ class Spell Unit* GetCaster() const { return m_caster; } Unit* GetOriginalCaster() const { return m_originalCaster; } SpellInfo const* GetSpellInfo() const { return m_spellInfo; } - int32 GetPowerCost() const { return m_powerCost; } + std::vector<SpellInfo::CostData> GetPowerCost() const { return m_powerCost; } bool UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc) @@ -560,7 +559,8 @@ class Spell //Spell data SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example) WeaponAttackType m_attackType; // For weapon based attack - int32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare + + std::vector<SpellInfo::CostData> m_powerCost; // Calculated spell cost initialized only in Spell::prepare int32 m_casttime; // Calculated spell cast time initialized only in Spell::prepare int32 m_channeledDuration; // Calculated channeled spell duration in order to calculate correct pushback. bool m_canReflect; // can reflect this spell? diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index db332f2fc49..e43d4ec3eab 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4747,13 +4747,18 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id); if (spellInfo) { - mana += spellInfo->ManaCost; - mana += int32(CalculatePct(m_caster->GetCreateMana(), spellInfo->ManaCostPercentage)); + std::vector<SpellInfo::CostData> costs = spellInfo->CalcPowerCost(m_caster, spellInfo->GetSchoolMask()); + auto m = std::find_if(costs.begin(), costs.end(), [](SpellInfo::CostData const& cost) { return cost.Power == POWER_MANA; }); + if (m != costs.end()) + mana += m->Amount; } + totem->ToTotem()->UnSummon(); } } + ApplyPct(mana, damage); + if (mana) m_caster->CastCustomSpell(m_caster, 39104, &mana, NULL, NULL, true); } @@ -5621,11 +5626,7 @@ void Spell::EffectCastButtons(SpellEffIndex /*effIndex*/) if (!spellInfo->HasAttribute(SPELL_ATTR9_SUMMON_PLAYER_TOTEM)) continue; - int32 cost = spellInfo->CalcPowerCost(m_caster, spellInfo->GetSchoolMask()); - if (m_caster->GetPower(POWER_MANA) < cost) - continue; - - TriggerCastFlags triggerFlags = TriggerCastFlags(TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); + TriggerCastFlags triggerFlags = TriggerCastFlags(TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY | TRIGGERED_DONT_REPORT_CAST_ERROR); m_caster->CastSpell(m_caster, spell_id, triggerFlags); } } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index ebc4cf53b98..65b43a436d7 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -942,7 +942,7 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] = {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 244 SPELL_EFFECT_244 }; -SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& effectsMap) +SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& effectsMap) : _hasPowerDifficultyData(false) { Id = spellEntry->ID; @@ -1084,12 +1084,7 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& ef SpellLevel = _levels ? _levels->SpellLevel : 0; // SpellPowerEntry - SpellPowerEntry const* _power = GetSpellPower(); - ManaCost = _power ? _power->ManaCost : 0; - ManaCostPerlevel = _power ? _power->ManaCostPerLevel : 0; - ManaCostPercentage = _power ? _power->ManaCostPercentage : 0; - ManaPerSecond = _power ? _power->ManaCostPerSecond : 0; - PowerType = _power ? _power->PowerType : 0; + PowerCosts = sDB2Manager.GetSpellPowers(Id, DIFFICULTY_NONE, &_hasPowerDifficultyData); // SpellReagentsEntry SpellReagentsEntry const* _reagents = GetSpellReagents(); @@ -1393,8 +1388,7 @@ bool SpellInfo::IsStackableWithRanks() const { if (IsPassive()) return false; - if (PowerType != POWER_MANA && PowerType != POWER_HEALTH) - return false; + if (IsProfessionOrRiding()) return false; @@ -1423,6 +1417,7 @@ bool SpellInfo::IsStackableWithRanks() const break; } } + return true; } @@ -2524,109 +2519,178 @@ uint32 SpellInfo::GetRecoveryTime() const return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime; } -int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const +std::vector<SpellInfo::CostData> SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const { - // Spell drain all exist power on cast (Only paladin lay of Hands) - if (AttributesEx & SPELL_ATTR1_DRAIN_ALL_POWER) + std::vector<CostData> costs; + auto collector = [this, caster, schoolMask, &costs](std::vector<SpellPowerEntry const*> const& powers) { - // If power type - health drain all - if (PowerType == POWER_HEALTH) - return caster->GetHealth(); - // Else drain all power - if (PowerType < MAX_POWERS) - return caster->GetPower(Powers(PowerType)); - TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", PowerType, Id); - return 0; - } + costs.reserve(powers.size()); + int32 healthCost = 0; - // Base powerCost - int32 powerCost = ManaCost; - // PCT cost from total amount - if (ManaCostPercentage) - { - switch (PowerType) + + for (SpellPowerEntry const* power : powers) { - // health as power used - case POWER_HEALTH: - powerCost += int32(CalculatePct(caster->GetCreateHealth(), ManaCostPercentage)); - break; - case POWER_MANA: - powerCost += int32(CalculatePct(caster->GetCreateMana(), ManaCostPercentage)); - break; - case POWER_RAGE: - case POWER_FOCUS: - case POWER_ENERGY: - powerCost += int32(CalculatePct(caster->GetMaxPower(Powers(PowerType)), ManaCostPercentage)); - break; - case POWER_RUNES: - case POWER_RUNIC_POWER: - TC_LOG_DEBUG("spells", "CalculateManaCost: Not implemented yet!"); - break; - default: - TC_LOG_ERROR("spells", "CalculateManaCost: Unknown power type '%d' in spell %d", PowerType, Id); - return 0; - } - } + if (power->RequiredAura && !caster->HasAura(power->RequiredAura)) + continue; - // Flat mod from caster auras by spell school and power type - Unit::AuraEffectList const& auras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); - for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) - { - if (!((*i)->GetMiscValue() & schoolMask)) - continue; - if (!((*i)->GetMiscValueB() & (1 << PowerType))) - continue; - powerCost += (*i)->GetAmount(); - } + // Spell drain all exist power on cast (Only paladin lay of Hands) + if (AttributesEx & SPELL_ATTR1_DRAIN_ALL_POWER) + { + // If power type - health drain all + if (power->PowerType == POWER_HEALTH) + { + healthCost = caster->GetHealth(); + continue; + } + // Else drain all power + if (power->PowerType < MAX_POWERS) + { + CostData cost; + cost.Power = Powers(power->PowerType); + cost.Amount = caster->GetPower(cost.Power); + costs.push_back(cost); + continue; + } - // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) - if (AttributesEx4 & SPELL_ATTR4_SPELL_VS_EXTEND_COST) - { - uint32 speed = 0; -/* REVIEW - MERGE - if (SpellShapeshiftEntry const* ss = sSpellShapeshiftStore.LookupEntry(caster->GetShapeshiftForm())) - speed = ss->attackSpeed; - else -*/ - { - WeaponAttackType slot = BASE_ATTACK; - if (AttributesEx3 & SPELL_ATTR3_REQ_OFFHAND) - slot = OFF_ATTACK; + TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id); + continue; + } - speed = caster->GetAttackTime(slot); - } + // Base powerCost + int32 powerCost = power->ManaCost; + // PCT cost from total amount + if (power->ManaCostPercentage) + { + switch (power->PowerType) + { + // health as power used + case POWER_HEALTH: + powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->ManaCostPercentage)); + break; + case POWER_MANA: + case POWER_RAGE: + case POWER_FOCUS: + case POWER_ENERGY: + powerCost += int32(CalculatePct(caster->GetMaxPower(Powers(power->PowerType)), power->ManaCostPercentage)); + break; + case POWER_RUNES: + case POWER_RUNIC_POWER: + TC_LOG_DEBUG("spells", "CalculateManaCost: Not implemented yet!"); + break; + default: + TC_LOG_ERROR("spells", "CalculateManaCost: Unknown power type '%d' in spell %d", power->PowerType, Id); + continue; + } + } - powerCost += speed / 100; - } + if (power->HealthCostPercentage) + healthCost += int32(CalculatePct(caster->GetMaxHealth(), power->HealthCostPercentage)); - // Apply cost mod by spell - if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost); + // Flat mod from caster auras by spell school and power type + Unit::AuraEffectList const& auras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); + for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) + { + if (!((*i)->GetMiscValue() & schoolMask)) + continue; - if (!caster->IsControlledByPlayer()) - { - if (Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION) + if (!((*i)->GetMiscValueB() & (1 << power->PowerType))) + continue; + + powerCost += (*i)->GetAmount(); + } + + // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) + if (AttributesEx4 & SPELL_ATTR4_SPELL_VS_EXTEND_COST) + { + uint32 speed = 0; + if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm())) + speed = ss->CombatRoundTime; + else + { + WeaponAttackType slot = BASE_ATTACK; + if (AttributesEx3 & SPELL_ATTR3_REQ_OFFHAND) + slot = OFF_ATTACK; + + speed = caster->GetAttackTime(slot); + } + + powerCost += speed / 100; + } + + // Apply cost mod by spell + if (Player* modOwner = caster->GetSpellModOwner()) + { + if (power->PowerIndex == 0) + modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost); + else if (power->PowerIndex == 1) + modOwner->ApplySpellMod(Id, SPELLMOD_SPELL_COST2, powerCost); + } + + if (!caster->IsControlledByPlayer()) + { + if (Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION) + { + GtNPCManaCostScalerEntry const* spellScaler = sGtNPCManaCostScalerStore.EvaluateTable(SpellLevel - 1, 0); + GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.EvaluateTable(caster->getLevel() - 1, 0); + if (spellScaler && casterScaler) + powerCost *= casterScaler->ratio / spellScaler->ratio; + } + } + + // 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 (!((*i)->GetMiscValue() & schoolMask)) + continue; + + if (!((*i)->GetMiscValueB() & (1 << power->PowerType))) + continue; + + powerCost += CalculatePct(powerCost, (*i)->GetAmount()); + } + + if (power->PowerType == POWER_HEALTH) + { + healthCost += powerCost; + continue; + } + + bool found = false; + for (CostData& cost : costs) + { + if (cost.Power == Powers(power->PowerType)) + { + cost.Amount += powerCost; + found = true; + } + } + + if (!found) + { + CostData cost; + cost.Power = Powers(power->PowerType); + cost.Amount = powerCost; + costs.push_back(cost); + } + } + + if (healthCost > 0) { - GtNPCManaCostScalerEntry const* spellScaler = sGtNPCManaCostScalerStore.EvaluateTable(SpellLevel - 1, 0); - GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.EvaluateTable(caster->getLevel() - 1, 0); - if (spellScaler && casterScaler) - powerCost *= casterScaler->ratio / spellScaler->ratio; + CostData cost; + cost.Power = POWER_HEALTH; + cost.Amount = healthCost; + costs.push_back(cost); } - } + }; - // 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 (!((*i)->GetMiscValue() & schoolMask)) - continue; - if (!((*i)->GetMiscValueB() & (1 << PowerType))) - continue; - powerCost += CalculatePct(powerCost, (*i)->GetAmount()); - } - if (powerCost < 0) - powerCost = 0; - return powerCost; + if (_hasPowerDifficultyData) // optimization - use static data for 99.5% cases (4753 of 4772 in build 6.1.0.19702) + collector(PowerCosts); + else + collector(sDB2Manager.GetSpellPowers(Id, caster->GetMap()->GetDifficultyID())); + + std::remove_if(costs.begin(), costs.end(), [](CostData const& cost) { return cost.Amount <= 0; }); + return costs; } bool SpellInfo::IsRanked() const @@ -3080,14 +3144,6 @@ SpellLevelsEntry const* SpellInfo::GetSpellLevels() const return SpellLevelsId ? sSpellLevelsStore.LookupEntry(SpellLevelsId) : NULL; } -SpellPowerEntry const* SpellInfo::GetSpellPower() const -{ - auto itr = sSpellPowerBySpellIDStore.find(Id); - if (itr != sSpellPowerBySpellIDStore.end()) - return itr->second; - return NULL; -} - SpellReagentsEntry const* SpellInfo::GetSpellReagents() const { return SpellReagentsId ? sSpellReagentsStore.LookupEntry(SpellReagentsId) : NULL; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 9e39e88d9d3..62381372657 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -370,11 +370,7 @@ public: uint32 BaseLevel; uint32 SpellLevel; SpellDurationEntry const* DurationEntry; - uint32 PowerType; - uint32 ManaCost; - uint32 ManaCostPerlevel; - uint32 ManaPerSecond; - uint32 ManaCostPercentage; + std::vector<SpellPowerEntry const*> PowerCosts; uint32 RuneCostID; SpellRangeEntry const* RangeEntry; float Speed; @@ -443,7 +439,6 @@ public: SpellEquippedItemsEntry const* GetSpellEquippedItems() const; SpellInterruptsEntry const* GetSpellInterrupts() const; SpellLevelsEntry const* GetSpellLevels() const; - SpellPowerEntry const* GetSpellPower() const; SpellReagentsEntry const* GetSpellReagents() const; SpellScalingEntry const* GetSpellScaling() const; SpellShapeshiftEntry const* GetSpellShapeshift() const; @@ -551,7 +546,13 @@ public: uint32 CalcCastTime(uint8 level = 0, Spell* spell = NULL) const; uint32 GetRecoveryTime() const; - int32 CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const; + struct CostData + { + Powers Power; + int32 Amount; + }; + + std::vector<CostData> CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const; bool IsRanked() const; uint8 GetRank() const; @@ -580,6 +581,7 @@ public: SpellEffectInfo const* GetEffect(WorldObject const* obj, uint32 index) const { return GetEffect(obj->GetMap()->GetDifficultyID(), index); } SpellEffectInfoMap _effects; + bool _hasPowerDifficultyData; }; #endif // _SPELLINFO_H diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 0d9fcc141bb..518463a9539 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3701,10 +3701,6 @@ void SpellMgr::LoadSpellInfoCorrections() case 40167: // Introspection spellInfo->Attributes |= SPELL_ATTR0_NEGATIVE_1; break; - case 2378: // Minor Fortitude - spellInfo->ManaCost = 0; - spellInfo->ManaPerSecond = 0; - break; // Stonecore spells case 95284: // Teleport (from entrance to Slabhide) case 95285: // Teleport (from Slabhide to entrance) |
