diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp index 784ea7d35..7ffda5eab 100644 --- a/src/game/CreatureAI.cpp +++ b/src/game/CreatureAI.cpp @@ -28,3 +28,90 @@ void CreatureAI::AttackedBy( Unit* attacker ) if(!m_creature->getVictim()) AttackStart(attacker); } + +CanCastResult CreatureAI::CanCastSpell(Unit* pTarget, const SpellEntry *pSpell, bool isTriggered) +{ + // If not triggered, we check + if (!isTriggered) + { + // State does not allow + if (m_creature->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_DIED)) + return CAST_FAIL_STATE; + + if (pSpell->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return CAST_FAIL_STATE; + + if (pSpell->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) + return CAST_FAIL_STATE; + + // Check for power (also done by Spell::CheckCast()) + if (m_creature->GetPower((Powers)pSpell->powerType) < pSpell->manaCost) + return CAST_FAIL_POWER; + } + + if (const SpellRangeEntry *pSpellRange = sSpellRangeStore.LookupEntry(pSpell->rangeIndex)) + { + if (pTarget != m_creature) + { + // pTarget is out of range of this spell (also done by Spell::CheckCast()) + float fDistance = m_creature->GetCombatDistance(pTarget); + + if (fDistance > (m_creature->IsHostileTo(pTarget) ? pSpellRange->maxRange : pSpellRange->maxRangeFriendly)) + return CAST_FAIL_TOO_FAR; + + float fMinRange = m_creature->IsHostileTo(pTarget) ? pSpellRange->minRange : pSpellRange->minRangeFriendly; + + if (fMinRange && fDistance < fMinRange) + return CAST_FAIL_TOO_CLOSE; + } + + return CAST_OK; + } + else + return CAST_FAIL_OTHER; +} + +CanCastResult CreatureAI::DoCastSpellIfCan(Unit* pTarget, uint32 uiSpell, uint32 uiCastFlags, uint64 uiOriginalCasterGUID) +{ + Unit* pCaster = m_creature; + + if (uiCastFlags & CAST_FORCE_TARGET_SELF) + pCaster = pTarget; + + // Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered + if (!pCaster->IsNonMeleeSpellCasted(false) || (uiCastFlags & (CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS))) + { + if (const SpellEntry* pSpell = sSpellStore.LookupEntry(uiSpell)) + { + // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them + if (uiCastFlags & CAST_AURA_NOT_PRESENT) + { + if (pTarget->HasAura(uiSpell)) + return CAST_FAIL_TARGET_AURA; + } + + // Check if cannot cast spell + if (!(uiCastFlags & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST))) + { + CanCastResult castResult = CanCastSpell(pTarget, pSpell, uiCastFlags & CAST_TRIGGERED); + + if (castResult != CAST_OK) + return castResult; + } + + // Interrupt any previous spell + if (uiCastFlags & CAST_INTURRUPT_PREVIOUS && pCaster->IsNonMeleeSpellCasted(false)) + pCaster->InterruptNonMeleeSpells(false); + + pCaster->CastSpell(pTarget, pSpell, uiCastFlags & CAST_TRIGGERED, NULL, NULL, uiOriginalCasterGUID); + return CAST_OK; + } + else + { + sLog.outErrorDb("DoCastSpellIfCan by creature entry %u attempt to cast spell %u but spell does not exist.", m_creature->GetEntry(), uiSpell); + return CAST_FAIL_OTHER; + } + } + else + return CAST_FAIL_IS_CASTING; +} diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h index 4038b675e..f049b5964 100644 --- a/src/game/CreatureAI.h +++ b/src/game/CreatureAI.h @@ -34,6 +34,18 @@ struct SpellEntry; #define TIME_INTERVAL_LOOK 5000 #define VISIBILITY_RANGE 10000 +enum CanCastResult +{ + CAST_OK = 0, + CAST_FAIL_IS_CASTING = 1, + CAST_FAIL_OTHER = 2, + CAST_FAIL_TOO_FAR = 3, + CAST_FAIL_TOO_CLOSE = 4, + CAST_FAIL_POWER = 5, + CAST_FAIL_STATE = 6, + CAST_FAIL_TARGET_AURA = 7 +}; + enum CastFlags { CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting @@ -68,6 +80,10 @@ class MANGOS_DLL_SPEC CreatureAI // Called at any heal cast/item used (call non implemented) virtual void HealBy(Unit * /*healer*/, uint32 /*amount_healed*/) {} + // Helper functions for cast spell + CanCastResult DoCastSpellIfCan(Unit* pTarget, uint32 uiSpell, uint32 uiCastFlags = 0, uint64 uiOriginalCasterGUID = 0); + virtual CanCastResult CanCastSpell(Unit* pTarget, const SpellEntry *pSpell, bool isTriggered); + // Called at any Damage to any victim (before damage apply) virtual void DamageDeal(Unit * /*done_to*/, uint32 & /*damage*/) {} diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 34f25d33a..f9f61ecdc 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9061" + #define REVISION_NR "9062" #endif // __REVISION_NR_H__