aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells/Spell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Spells/Spell.cpp')
-rw-r--r--src/server/game/Spells/Spell.cpp328
1 files changed, 215 insertions, 113 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 5016e5ebc2b..510be691d18 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -19,7 +19,7 @@
*/
#include "Common.h"
-#include "Database/DatabaseEnv.h"
+#include "DatabaseEnv.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "GridNotifiers.h"
@@ -41,7 +41,6 @@
#include "MapManager.h"
#include "ObjectAccessor.h"
#include "CellImpl.h"
-#include "Policies/SingletonImp.h"
#include "SharedDefines.h"
#include "LootMgr.h"
#include "VMapFactory.h"
@@ -53,7 +52,7 @@
#include "ScriptMgr.h"
#include "ConditionMgr.h"
-#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILISECONDS)
+#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS)
extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
@@ -149,6 +148,15 @@ void SpellCastTargets::setItemTarget(Item* item)
m_targetMask |= TARGET_FLAG_ITEM;
}
+void SpellCastTargets::setTradeItemTarget(Player* caster)
+{
+ m_itemTargetGUID = uint64(TRADE_SLOT_NONTRADED);
+ m_itemTargetEntry = 0;
+ m_targetMask |= TARGET_FLAG_TRADE_ITEM;
+
+ Update(caster);
+}
+
void SpellCastTargets::setCorpseTarget(Corpse* corpse)
{
m_CorpseTargetGUID = corpse->GetGUID();
@@ -164,12 +172,13 @@ void SpellCastTargets::Update(Unit* caster)
m_itemTarget = NULL;
if (caster->GetTypeId() == TYPEID_PLAYER)
{
+ Player *player = caster->ToPlayer();
if (m_targetMask & TARGET_FLAG_ITEM)
- m_itemTarget = caster->ToPlayer()->GetItemByGuid(m_itemTargetGUID);
+ m_itemTarget = player->GetItemByGuid(m_itemTargetGUID);
else if (m_targetMask & TARGET_FLAG_TRADE_ITEM)
- if (m_itemTargetGUID == TRADE_SLOT_NONTRADED) // here it is not guid but slot. Also prevent hacking slots
- if (Player* pTrader = caster->ToPlayer()->GetTrader())
- m_itemTarget = pTrader->GetItemByTradeSlot(m_itemTargetGUID);
+ if (m_itemTargetGUID == TRADE_SLOT_NONTRADED) // here it is not guid but slot. Also prevents hacking slots
+ if (TradeData* pTrade = player->GetTradeData())
+ m_itemTarget = pTrade->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED);
if (m_itemTarget)
m_itemTargetEntry = m_itemTarget->GetEntry();
@@ -390,13 +399,12 @@ Spell::Spell(Unit* Caster, SpellEntry const *info, bool triggered, uint64 origin
}
for (int i=0; i <3; ++i)
- m_currentBasePoints[i] = m_spellInfo->CalculateSimpleValue(i);
+ m_currentBasePoints[i] = m_spellInfo->EffectBasePoints[i];
m_spellState = SPELL_STATE_NULL;
m_TriggerSpells.clear();
m_IsTriggeredSpell = triggered;
- //m_AreaAura = false;
m_CastItem = NULL;
unitTarget = NULL;
@@ -1336,7 +1344,15 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
// Now Reduce spell duration using data received at spell hit
int32 duration = m_spellAura->GetMaxDuration();
int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup,aurSpellInfo);
- unit->ApplyDiminishingToDuration(m_diminishGroup, duration, m_originalCaster, m_diminishLevel,limitduration);
+ float diminishMod = unit->ApplyDiminishingToDuration(m_diminishGroup, duration, m_originalCaster, m_diminishLevel,limitduration);
+
+ // unit is immune to aura if it was diminished to 0 duration
+ if (diminishMod == 0.0f)
+ {
+ m_spellAura->Remove();
+ return SPELL_MISS_IMMUNE;
+ }
+
((UnitAura*)m_spellAura)->SetDiminishGroup(m_diminishGroup);
bool positive = IsPositiveSpell(m_spellAura->GetId());
@@ -1350,12 +1366,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
if (IsChanneledSpell(m_spellInfo))
m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this);
- if (duration == 0 && !positive)
- {
- m_spellAura->Remove();
- return SPELL_MISS_IMMUNE;
- }
-
if (duration != m_spellAura->GetMaxDuration())
{
m_spellAura->SetMaxDuration(duration);
@@ -1989,7 +1999,6 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
if (cur == TARGET_DST_TARGET_ENEMY || cur == TARGET_DEST_TARGET_ANY)
{
m_targets.setDst(target);
- AddUnitTarget(target, i);
break;
}
@@ -2018,9 +2027,6 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
Position pos;
target->GetNearPosition(pos, dist, angle);
m_targets.setDst(&pos);
- // Teleports use this as destination
- if (m_spellInfo->Effect[i] != SPELL_EFFECT_TELEPORT_UNITS)
- AddUnitTarget(target, i);
break;
}
@@ -2416,6 +2422,117 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
if (!unitList.empty())
{
+ // Special target selection for smart heals and energizes
+ uint32 maxSize = 0;
+ int32 power = -1;
+ switch (m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ switch (m_spellInfo->Id)
+ {
+ case 52759: // Ancestral Awakening
+ case 71610: // Echoes of Light (Althor's Abacus normal version)
+ case 71641: // Echoes of Light (Althor's Abacus heroic version)
+ maxSize = 1;
+ power = POWER_HEALTH;
+ break;
+ case 54968: // Glyph of Holy Light
+ maxSize = m_spellInfo->MaxAffectedTargets;
+ power = POWER_HEALTH;
+ break;
+ case 57669: // Replenishment
+ // In arenas Replenishment may only affect the caster
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->InArena())
+ {
+ unitList.clear();
+ unitList.push_back(m_caster);
+ break;
+ }
+ maxSize = 10;
+ power = POWER_MANA;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SPELLFAMILY_PRIEST:
+ if (m_spellInfo->SpellFamilyFlags[0] == 0x10000000) // Circle of Healing
+ {
+ maxSize = m_caster->HasAura(55675) ? 6 : 5; // Glyph of Circle of Healing
+ power = POWER_HEALTH;
+ }
+ else if (m_spellInfo->Id == 64844) // Divine Hymn
+ {
+ maxSize = 3;
+ power = POWER_HEALTH;
+ }
+ else if (m_spellInfo->Id == 64904) // Hymn of Hope
+ {
+ maxSize = 3;
+ power = POWER_MANA;
+ }
+ else
+ break;
+
+ // Remove targets outside caster's raid
+ for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
+ {
+ if (!(*itr)->IsInRaidWith(m_caster))
+ itr = unitList.erase(itr);
+ else
+ ++itr;
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ if (m_spellInfo->SpellFamilyFlags[1] == 0x04000000) // Wild Growth
+ {
+ maxSize = m_caster->HasAura(62970) ? 6 : 5; // Glyph of Wild Growth
+ power = POWER_HEALTH;
+ }
+ else
+ break;
+
+ // Remove targets outside caster's raid
+ for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
+ {
+ if (!(*itr)->IsInRaidWith(m_caster))
+ itr = unitList.erase(itr);
+ else
+ ++itr;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (maxSize && power != -1)
+ {
+ if (power == POWER_HEALTH)
+ {
+ if (unitList.size() > maxSize)
+ {
+ unitList.sort(Trinity::HealthPctOrderPred());
+ unitList.resize(maxSize);
+ }
+ }
+ else
+ {
+ for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
+ {
+ if ((*itr)->getPowerType() != (Powers)power)
+ itr = unitList.erase(itr);
+ else
+ ++itr;
+ }
+ if (unitList.size() > maxSize)
+ {
+ unitList.sort(Trinity::PowerPctOrderPred((Powers)power));
+ unitList.resize(maxSize);
+ }
+ }
+ }
+
+ // Other special target selection goes here
if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
{
Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
@@ -2438,56 +2555,6 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
case 59725: // Improved Spell Reflection - aoe aura
unitList.remove(m_caster);
break;
- case 57669: //Replenishment (special target selection) 10 targets with lowest mana
- {
- for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
- {
- if ((*itr)->getPowerType() != POWER_MANA)
- itr = unitList.erase(itr);
- else
- ++itr;
- }
- if (unitList.size() > 10)
- {
- unitList.sort(Trinity::PowerPctOrderPred(POWER_MANA));
- unitList.resize(10);
- }
- break;
- }
- case 52759: // Ancestral Awakening
- {
- if (unitList.size() > 1)
- {
- unitList.sort(Trinity::HealthPctOrderPred());
- unitList.resize(1);
- }
- break;
- }
- }
- if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_TARGET_ANY
- && m_spellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ALLY_DST)// Wild Growth, Circle of Healing, Glyph of holy light target special selection
- {
- for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
- {
- if (!(*itr)->IsInRaidWith(m_targets.getUnitTarget()))
- itr = unitList.erase(itr);
- else
- ++itr;
- }
-
- uint32 maxsize = 5;
-
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x04000000) // Wild Growth
- maxsize += m_caster->HasAura(62970) ? 1 : 0; // Glyph of Wild Growth
-
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags[0] & 0x10000000 && m_spellInfo->SpellIconID == 2214) // Circle of Healing
- maxsize += m_caster->HasAura(55675) ? 1 : 0; // Glyph of Circle of Healing
-
- if (unitList.size() > maxsize)
- {
- unitList.sort(Trinity::HealthPctOrderPred());
- unitList.resize(maxsize);
- }
}
// Death Pact
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000)
@@ -2844,6 +2911,32 @@ void Spell::cast(bool skipCheck)
SetExecutedCurrently(false);
return;
}
+
+ // additional check after cast bar completes (must not be in CheckCast)
+ // if trade not complete then remember it in trade data
+ if (m_targets.getTargetMask() & TARGET_FLAG_TRADE_ITEM)
+ {
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if (TradeData* my_trade = m_caster->ToPlayer()->GetTradeData())
+ {
+ if (!my_trade->IsInAcceptProcess())
+ {
+ // Spell will be casted at completing the trade. Silently ignore at this place
+ my_trade->SetSpell(m_spellInfo->Id, m_CastItem);
+ SendCastResult(SPELL_FAILED_DONT_REPORT);
+ SendInterrupted(0);
+ m_caster->ToPlayer()->RestoreSpellMods(this);
+ // cleanup after mod system
+ // triggered spell pointer can be not removed in some cases
+ m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
+ finish(false);
+ SetExecutedCurrently(false);
+ return;
+ }
+ }
+ }
+ }
}
SelectSpellTargets();
@@ -2909,6 +3002,12 @@ void Spell::cast(bool skipCheck)
TakePower();
TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
}
+ else if (Item* targetItem = m_targets.getItemTarget())
+ {
+ /// Not own traded item (in trader trade slot) req. reagents including triggered spell case
+ if (targetItem->GetOwnerGUID() != m_caster->GetGUID())
+ TakeReagents();
+ }
// are there any spells need to be triggered after hit?
// handle SPELL_AURA_ADD_TARGET_TRIGGER auras
@@ -2938,8 +3037,9 @@ void Spell::cast(bool skipCheck)
switch(m_spellInfo->Effect[i])
{
case SPELL_EFFECT_CHARGE:
+ case SPELL_EFFECT_CHARGE_DEST:
case SPELL_EFFECT_JUMP:
- case SPELL_EFFECT_JUMP2:
+ case SPELL_EFFECT_JUMP_DEST:
case SPELL_EFFECT_LEAP_BACK:
case SPELL_EFFECT_ACTIVATE_RUNE:
HandleEffects(NULL,NULL,NULL,i);
@@ -4274,7 +4374,12 @@ void Spell::TakeRunePower()
void Spell::TakeReagents()
{
if (m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed.
- return;
+ {
+ Item* targetItem = m_targets.getItemTarget();
+ /// Not own traded item (in trader trade slot) req. reagents including triggered spell case
+ if (!(targetItem && targetItem->GetOwnerGUID() != m_caster->GetGUID()))
+ return;
+ }
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
@@ -4669,7 +4774,7 @@ SpellCastResult Spell::CheckCast(bool strict)
// - with greater than 10 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag
// - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag
if ((m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) ||
- GetSpellRecoveryTime(m_spellInfo) > 10 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA))
+ GetSpellRecoveryTime(m_spellInfo) > 10 * MINUTE * IN_MILLISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA))
if (MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
if (mapEntry->IsBattleArena())
return SPELL_FAILED_NOT_IN_ARENA;
@@ -5212,24 +5317,6 @@ SpellCastResult Spell::CheckCast(bool strict)
if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_caster->ToPlayer()->IsInFeralForm())
return SPELL_FAILED_ONLY_SHAPESHIFT;
break;
- // Wild Growth
- case 48438:
- case 53248:
- case 53249:
- case 53251:
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return SPELL_FAILED_DONT_REPORT;
-
- Unit* target = m_targets.getUnitTarget();
- if (!target || target->GetTypeId() != TYPEID_PLAYER)
- return SPELL_FAILED_BAD_TARGETS;
-
- if (!m_caster->ToPlayer()->IsInSameRaidWith(target->ToPlayer()))
- return SPELL_FAILED_TARGET_NOT_IN_RAID;
-
- break;
- }
case 1515:
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
@@ -5304,6 +5391,9 @@ SpellCastResult Spell::CheckCast(bool strict)
if (target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->IsVehicle())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+ if (target->IsMounted())
+ return SPELL_FAILED_CANT_BE_CHARMED;
+
if (target->GetCharmerGUID())
return SPELL_FAILED_CHARMED;
@@ -5357,23 +5447,12 @@ SpellCastResult Spell::CheckCast(bool strict)
// allow always ghost flight spells
if (m_originalCaster && m_originalCaster->GetTypeId() == TYPEID_PLAYER && m_originalCaster->isAlive())
{
- // 4197 = Wintergrasp || 4395 = Dalaran && 4564 = Krasus Landing
- if (m_originalCaster->GetZoneId() == 4197 || m_originalCaster->GetZoneId() == 4395 && m_originalCaster->GetAreaId() != 4564)
- return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE;
+ if (AreaTableEntry const* pArea = GetAreaEntryByAreaID(m_originalCaster->GetAreaId()))
+ if (pArea->flags & AREA_FLAG_NO_FLY_ZONE)
+ return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE;
}
break;
}
- case SPELL_AURA_RANGED_AP_ATTACKER_CREATURES_BONUS:
- {
- if (!m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT)
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
-
- // can be casted at non-friendly unit or own pet/charm
- if (m_caster->IsFriendlyTo(m_targets.getUnitTarget()))
- return SPELL_FAILED_TARGET_FRIENDLY;
-
- break;
- }
case SPELL_AURA_PERIODIC_MANA_LEECH:
{
if (!m_targets.getUnitTarget())
@@ -5392,6 +5471,26 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
+ // check trade slot case (last, for allow catch any another cast problems)
+ if (m_targets.getTargetMask() & TARGET_FLAG_TRADE_ITEM)
+ {
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_NOT_TRADING;
+
+ TradeData* my_trade = m_caster->ToPlayer()->GetTradeData();
+
+ if (!my_trade)
+ return SPELL_FAILED_NOT_TRADING;
+
+ TradeSlots slot = TradeSlots(m_targets.getItemTargetGUID());
+ if (slot != TRADE_SLOT_NONTRADED)
+ return SPELL_FAILED_ITEM_NOT_READY;
+
+ if (!m_IsTriggeredSpell)
+ if (my_trade->GetSpell())
+ return SPELL_FAILED_ITEM_ALREADY_ENCHANTED;
+ }
+
// all ok
return SPELL_CAST_OK;
}
@@ -5892,7 +5991,7 @@ SpellCastResult Spell::CheckItems()
else if (!(p_caster->HasItemCount(m_spellInfo->EffectItemType[i],1)))
return SPELL_FAILED_TOO_MANY_OF_ITEM;
else
- p_caster->CastSpell(m_caster,m_spellInfo->CalculateSimpleValue(1),false); // move this to anywhere
+ p_caster->CastSpell(m_caster,SpellMgr::CalculateSpellEffectAmount(m_spellInfo, 1),false); // move this to anywhere
return SPELL_FAILED_DONT_REPORT;
}
}
@@ -6346,6 +6445,8 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
case SPELL_AURA_AOE_CHARM:
if (target->GetTypeId() == TYPEID_UNIT && target->IsVehicle())
return false;
+ if (target->IsMounted())
+ return false;
if (target->GetCharmerGUID())
return false;
if (int32 damage = CalculateDamage(eff, target))
@@ -6687,8 +6788,9 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul
{
if (IsAreaEffectTarget[m_spellInfo->EffectImplicitTargetA[i]] || IsAreaEffectTarget[m_spellInfo->EffectImplicitTargetB[i]])
{
- if (int32 reducedPct = unit->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE))
- m_damage = m_damage * (100 + reducedPct) / 100;
+ m_damage = float(m_damage) * unit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask);
+ if (m_caster->GetTypeId() == TYPEID_UNIT)
+ m_damage = float(m_damage) * unit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask);
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -6748,8 +6850,8 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk
if (skillId != SKILL_NONE)
{
// skill bonus provided by casting spell (mostly item spells)
- // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
- uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]);
+ // add the damage modifier from the spell casted (cheat lock / skeleton key etc.)
+ uint32 spellSkillBonus = uint32(CalculateDamage(effIndex, NULL));
reqSkillValue = lockInfo->Skill[j];
// castitem check: rogue using skeleton keys. the skill values should not be added in this case.
@@ -6778,15 +6880,15 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value)
switch(mod)
{
case SPELLVALUE_BASE_POINT0:
- m_spellValue->EffectBasePoints[0] = value - int32(1);
+ m_spellValue->EffectBasePoints[0] = SpellMgr::CalculateSpellEffectBaseAmount(value);
m_currentBasePoints[0] = m_spellValue->EffectBasePoints[0]; //this should be removed in the future
break;
case SPELLVALUE_BASE_POINT1:
- m_spellValue->EffectBasePoints[1] = value - int32(1);
+ m_spellValue->EffectBasePoints[1] = SpellMgr::CalculateSpellEffectBaseAmount(value);
m_currentBasePoints[1] = m_spellValue->EffectBasePoints[1];
break;
case SPELLVALUE_BASE_POINT2:
- m_spellValue->EffectBasePoints[2] = value - int32(1);
+ m_spellValue->EffectBasePoints[2] = SpellMgr::CalculateSpellEffectBaseAmount(value);
m_currentBasePoints[2] = m_spellValue->EffectBasePoints[2];
break;
case SPELLVALUE_RADIUS_MOD: