diff options
author | megamage <none@none> | 2009-05-20 11:44:38 -0500 |
---|---|---|
committer | megamage <none@none> | 2009-05-20 11:44:38 -0500 |
commit | 6473e943581439f57918abfa91a4d5a29e2f343c (patch) | |
tree | 01b7419dcc1ac0444375d11b22cd55d72897854a /src/game | |
parent | d1d194b4c78aec34d65a15ea68acff5c3a48687c (diff) |
*Update Naxx scripts. Now only Kelthuzad is incompleted
--HG--
branch : trunk
Diffstat (limited to 'src/game')
-rw-r--r-- | src/game/AggressorAI.cpp | 21 | ||||
-rw-r--r-- | src/game/CreatureAI.cpp | 202 | ||||
-rw-r--r-- | src/game/CreatureAI.h | 19 | ||||
-rw-r--r-- | src/game/CreatureAIImpl.h | 10 | ||||
-rw-r--r-- | src/game/InstanceData.cpp | 16 | ||||
-rw-r--r-- | src/game/InstanceData.h | 2 | ||||
-rw-r--r-- | src/game/Spell.cpp | 43 | ||||
-rw-r--r-- | src/game/SpellAuras.cpp | 21 | ||||
-rw-r--r-- | src/game/SpellEffects.cpp | 50 | ||||
-rw-r--r-- | src/game/SpellMgr.cpp | 5 | ||||
-rw-r--r-- | src/game/SpellMgr.h | 6 | ||||
-rw-r--r-- | src/game/Unit.h | 4 | ||||
-rw-r--r-- | src/game/UnitAI.cpp | 281 | ||||
-rw-r--r-- | src/game/UnitAI.h | 19 |
14 files changed, 424 insertions, 275 deletions
diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp index 218f19a1c21..d827e3e2672 100644 --- a/src/game/AggressorAI.cpp +++ b/src/game/AggressorAI.cpp @@ -85,26 +85,7 @@ void SpellAI::UpdateAI(const uint32 diff) if(uint32 spellId = events.ExecuteEvent()) { - Unit *target = NULL; - //sLog.outError("aggre %u %u", spellId, (uint32)AISpellInfo[spellId].target); - switch(AISpellInfo[spellId].target) - { - default: - case AITARGET_SELF: target = me; break; - case AITARGET_VICTIM: target = me->getVictim(); break; - case AITARGET_ENEMY: target = SelectTarget(SELECT_TARGET_RANDOM); break; - case AITARGET_ALLY: target = me; break; - case AITARGET_BUFF: target = me; break; - case AITARGET_DEBUFF: - { - const SpellEntry * spellInfo = GetSpellStore()->LookupEntry(spellId); - bool playerOnly = spellInfo->AttributesEx3 & SPELL_ATTR_EX3_PLAYERS_ONLY; - float range = GetSpellMaxRange(spellInfo, false); - target = SelectTarget(SELECT_TARGET_RANDOM, 0, range, playerOnly, -(int32)spellId); - break; - } - } - if(target) me->CastSpell(target, spellId, false); + DoCast(spellId); events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown); } else diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp index 230c1d446fc..465d67ded20 100644 --- a/src/game/CreatureAI.cpp +++ b/src/game/CreatureAI.cpp @@ -32,7 +32,7 @@ void CreatureAI::OnCharmed(bool apply) me->IsAIEnabled = false; } -AISpellInfoType * CreatureAI::AISpellInfo; +AISpellInfoType * UnitAI::AISpellInfo; TRINITY_DLL_SPEC AISpellInfoType * GetAISpellInfo(uint32 i) { return &CreatureAI::AISpellInfo[i]; } void CreatureAI::DoZoneInCombat(Creature* creature) @@ -98,6 +98,15 @@ void CreatureAI::MoveInLineOfSight(Unit *who) AttackStart(who->getVictim()); } +void CreatureAI::SelectNearestTarget(Unit *who) +{ + if(me->getVictim() && me->GetDistanceOrder(who, me->getVictim()) && me->canAttack(who)) + { + me->getThreatManager().modifyThreatPercent(me->getVictim(), -100); + me->AddThreat(who, 1000000.0f); + } +} + void CreatureAI::SetGazeOn(Unit *target) { if(me->canAttack(target)) @@ -182,197 +191,6 @@ void CreatureAI::EnterEvadeMode() Reset(); } -inline bool SelectTargetHelper(const Unit * me, const Unit * target, const bool &playerOnly, const float &dist, const int32 &aura) -{ - if(playerOnly && target->GetTypeId() != TYPEID_PLAYER) - return false; - - if(dist && !me->IsWithinCombatRange(target, dist)) - return false; - - if(aura) - { - if(aura > 0) - { - if(!target->HasAura(aura)) - return false; - } - else - { - if(target->HasAura(aura)) - return false; - } - } - - return true; -} - -struct TargetDistanceOrder : public std::binary_function<const Unit *, const Unit *, bool> -{ - const Unit * me; - TargetDistanceOrder(const Unit* Target) : me(Target) {}; - // functor for operator ">" - bool operator()(const Unit * _Left, const Unit * _Right) const - { - return (me->GetDistanceSq(_Left) < me->GetDistanceSq(_Right)); - } -}; - -Unit* CreatureAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura) -{ - if(targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) - { - std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList(); - if(position >= m_threatlist.size()) - return NULL; - - std::list<Unit*> targetList; - for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) - if(SelectTargetHelper(me, (*itr)->getTarget(), playerOnly, dist, aura)) - targetList.push_back((*itr)->getTarget()); - - if(position >= targetList.size()) - return NULL; - - targetList.sort(TargetDistanceOrder(m_creature)); - - if(targetType == SELECT_TARGET_NEAREST) - { - std::list<Unit*>::iterator i = targetList.begin(); - advance(i, position); - return *i; - } - else - { - std::list<Unit*>::reverse_iterator i = targetList.rbegin(); - advance(i, position); - return *i; - } - } - else - { - std::list<HostilReference*> m_threatlist = me->getThreatManager().getThreatList(); - std::list<HostilReference*>::iterator i; - while(position < m_threatlist.size()) - { - if(targetType == SELECT_TARGET_BOTTOMAGGRO) - { - i = m_threatlist.end(); - advance(i, - (int32)position - 1); - } - else - { - i = m_threatlist.begin(); - if(targetType == SELECT_TARGET_TOPAGGRO) - advance(i, position); - else // random - advance(i, position + rand()%(m_threatlist.size() - position)); - } - - if(SelectTargetHelper(me, (*i)->getTarget(), playerOnly, dist, aura)) - return (*i)->getTarget(); - else - m_threatlist.erase(i); - } - } - - return NULL; -} - -void CreatureAI::SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura) -{ - if(targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) - { - std::list<HostilReference*> &m_threatlist = m_creature->getThreatManager().getThreatList(); - if(m_threatlist.empty()) - return; - - for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) - if(SelectTargetHelper(me, (*itr)->getTarget(), playerOnly, dist, aura)) - targetList.push_back((*itr)->getTarget()); - - targetList.sort(TargetDistanceOrder(me)); - targetList.resize(num); - if(targetType == SELECT_TARGET_FARTHEST) - targetList.reverse(); - } - else - { - std::list<HostilReference*> m_threatlist = me->getThreatManager().getThreatList(); - std::list<HostilReference*>::iterator i; - while(!m_threatlist.empty() && num) - { - if(targetType == SELECT_TARGET_BOTTOMAGGRO) - { - i = m_threatlist.end(); - --i; - } - else - { - i = m_threatlist.begin(); - if(targetType == SELECT_TARGET_RANDOM) - advance(i, rand()%m_threatlist.size()); - } - - if(SelectTargetHelper(me, (*i)->getTarget(), playerOnly, dist, aura)) - { - targetList.push_back((*i)->getTarget()); - --num; - } - m_threatlist.erase(i); - } - } -} - -void CreatureAI::FillAISpellInfo() -{ - AISpellInfo = new AISpellInfoType[GetSpellStore()->GetNumRows()]; - - AISpellInfoType *AIInfo = AISpellInfo; - const SpellEntry * spellInfo; - - for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i, ++AIInfo) - { - spellInfo = GetSpellStore()->LookupEntry(i); - if(!spellInfo) - continue; - - if(spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD) - AIInfo->condition = AICOND_DIE; - else if(IsPassiveSpell(i) || GetSpellDuration(spellInfo) == -1) - AIInfo->condition = AICOND_AGGRO; - else - AIInfo->condition = AICOND_COMBAT; - - if(AIInfo->cooldown < spellInfo->RecoveryTime) - AIInfo->cooldown = spellInfo->RecoveryTime; - - for(uint32 j = 0; j < 3; ++j) - { - if(spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ENEMY - || spellInfo->EffectImplicitTargetA[j] == TARGET_DST_TARGET_ENEMY) - { - if(AIInfo->target < AITARGET_VICTIM) - AIInfo->target = AITARGET_VICTIM; - } - - if(spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA) - { - if(spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ENEMY) - { - if(AIInfo->target < AITARGET_DEBUFF) - AIInfo->target = AITARGET_DEBUFF; - } - else if(IsPositiveSpell(i)) - { - if(AIInfo->target < AITARGET_BUFF) - AIInfo->target = AITARGET_BUFF; - } - } - } - } -} - /*void CreatureAI::AttackedBy( Unit* attacker ) { if(!m_creature->getVictim()) diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h index fbc3e5c17b4..6a639be74bb 100644 --- a/src/game/CreatureAI.h +++ b/src/game/CreatureAI.h @@ -29,7 +29,6 @@ class Unit; class Creature; class Player; struct SpellEntry; -struct AISpellInfoType; #define TIME_INTERVAL_LOOK 5000 #define VISIBILITY_RANGE 10000 @@ -59,16 +58,6 @@ enum SelectEffect SELECT_EFFECT_AURA, //Spell applies an aura }; -//Selection method used by SelectTarget -enum SelectAggroTarget -{ - SELECT_TARGET_RANDOM = 0, //Just selects a random target - SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom - SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top - SELECT_TARGET_NEAREST, - SELECT_TARGET_FARTHEST, -}; - enum SCEquip { EQUIP_NO_CHANGE = -1, @@ -84,6 +73,8 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI bool UpdateVictim(); bool UpdateVictimByReact(); bool UpdateVictimWithGaze(); + + void SelectNearestTarget(Unit *who); public: explicit CreatureAI(Creature *c) : UnitAI((Unit*)c), me(c), m_creature(c) {} @@ -165,14 +156,8 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI // Pointer to controlled by AI creature //Creature* const m_creature; - Unit* SelectTarget(SelectAggroTarget target, uint32 position = 0, float dist = 0, bool playerOnly = false, int32 aura = 0); - void SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAggroTarget target, float dist = 0, bool playerOnly = false, int32 aura = 0); - void SetGazeOn(Unit *target); - static AISpellInfoType *AISpellInfo; - static void FillAISpellInfo(); - protected: bool _EnterEvadeMode(); }; diff --git a/src/game/CreatureAIImpl.h b/src/game/CreatureAIImpl.h index f404f0d954f..38dec554f13 100644 --- a/src/game/CreatureAIImpl.h +++ b/src/game/CreatureAIImpl.h @@ -175,5 +175,15 @@ struct AISpellInfoType TRINITY_DLL_SPEC AISpellInfoType * GetAISpellInfo(uint32 i); +//Selection method used by SelectTarget +enum SelectAggroTarget +{ + SELECT_TARGET_RANDOM = 0, //Just selects a random target + SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom + SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top + SELECT_TARGET_NEAREST, + SELECT_TARGET_FARTHEST, +}; + #endif diff --git a/src/game/InstanceData.cpp b/src/game/InstanceData.cpp index fcc708e274c..bb4bfe5e8fc 100644 --- a/src/game/InstanceData.cpp +++ b/src/game/InstanceData.cpp @@ -188,17 +188,26 @@ void InstanceData::AddMinion(Creature *minion, bool add) itr->second.bossInfo->minion.erase(minion); } -void InstanceData::SetBossState(uint32 id, EncounterState state) +bool InstanceData::SetBossState(uint32 id, EncounterState state) { if(id < bosses.size()) { BossInfo *bossInfo = &bosses[id]; if(bossInfo->state == TO_BE_DECIDED) // loading + { bossInfo->state = state; + return false; + } else { if(bossInfo->state == state) - return; + return false; + + if(state == DONE) + for(MinionSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i) + if((*i)->isWorldBoss() && (*i)->isAlive()) + return false; + bossInfo->state = state; SaveToDB(); } @@ -209,7 +218,10 @@ void InstanceData::SetBossState(uint32 id, EncounterState state) for(MinionSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i) UpdateMinionState(*i, state); + + return true; } + return false; } std::string InstanceData::LoadBossState(const char * data) diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h index 43042a7f914..133f92ecc60 100644 --- a/src/game/InstanceData.h +++ b/src/game/InstanceData.h @@ -158,7 +158,7 @@ class TRINITY_DLL_SPEC InstanceData //use HandleGameObject(GUID,boolen,NULL); in any other script void HandleGameObject(uint64 GUID, bool open, GameObject *go = NULL); - virtual void SetBossState(uint32 id, EncounterState state); + virtual bool SetBossState(uint32 id, EncounterState state); const BossBoundaryMap * GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : NULL; } protected: void SetBossNumber(uint32 number) { bosses.resize(number); } diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 424bfd097fe..b1e1549cc5b 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -1249,8 +1249,43 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) } // Prayer of Mending (jump animation), we need formal caster instead original for correct animation - if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags[1] & 0x000020)) - m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID); + if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST) + { + if(m_spellInfo->SpellFamilyFlags[1] & 0x000020) + m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID); + } + else + { + // spell is triggered with only stackamount change but no amount change + switch(m_spellInfo->Id) + { + case 28832: // Mark of Korth'azz + case 28833: // Mark of Blaumeux + case 28834: // Mark of Rivendare + case 28835: // Mark of Zeliek + { + Aura *aur = unit->GetAura(m_spellInfo->Id); + if(!aur) break; + //int8 stack = GetParentAura()->GetStackAmount(); + int8 stack = aur->GetStackAmount(); + ++stack; + int32 damage; + switch(stack) + { + case 1: damage = 0; break; + case 2: damage = 500; break; + case 3: damage = 1000; break; + case 4: damage = 1500; break; + case 5: damage = 4000; break; + case 6: damage = 12000; break; + default:damage = 20000 + 1000 * (stack - 7); break; + } + if(damage) + m_caster->CastCustomSpell(28836, SPELLVALUE_BASE_POINT0, damage, unit); + break; + } + } + } } // Set aura only when successfully applied if (unit->AddAura(Aur, false)) @@ -4610,9 +4645,9 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_AURA_FLY: { // not allow cast fly spells at old maps by players (all spells is self target) - if(m_caster->GetTypeId()==TYPEID_PLAYER) + if(m_originalCaster && m_originalCaster->GetTypeId()==TYPEID_PLAYER) { - if( !((Player*)m_caster)->IsAllowUseFlyMountsHere() ) + if( !((Player*)m_originalCaster)->IsAllowUseFlyMountsHere() ) return SPELL_FAILED_NOT_HERE; } break; diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 2145c19e6de..18abbd4c2e8 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -2351,27 +2351,6 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) if(caster) caster->CastSpell(caster,13138,true,NULL,this); return; - case 28832: // Mark of Korth'azz - case 28833: // Mark of Blaumeux - case 28834: // Mark of Rivendare - case 28835: // Mark of Zeliek - { - int8 stack = GetParentAura()->GetStackAmount(); - int32 damage; - switch(stack) - { - case 1: return; - case 2: damage = 500; break; - case 3: damage = 1000; break; - case 4: damage = 1500; break; - case 5: damage = 4000; break; - case 6: damage = 12000; break; - default:damage = 20000 + 1000 * (stack - 7); break; - } - if(caster) - caster->CastCustomSpell(28836, SPELLVALUE_BASE_POINT0, damage, m_target); - return; - } case 34026: // kill command { Unit * pet = m_target->GetGuardianPet(); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 3d1cdbf595b..ada90e881ae 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -339,14 +339,45 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) { // Positive/Negative Charge case 28062: - case 39090: case 28085: + case 39090: case 39093: - if(m_triggeredByAuraSpell && unitTarget->HasAura(m_triggeredByAuraSpell->Id)) + if(!m_triggeredByAuraSpell) + break; + if(unitTarget == m_caster) { - damage = 0; - m_caster->CastSpell(m_caster, (m_spellInfo->Id == 28062 || m_spellInfo->Id == 39090) ? 29659 : 29660, true); + uint8 count = 0; + for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + if(ihit->targetGUID != m_caster->GetGUID()) + if(Player *target = ObjectAccessor::FindPlayer(ihit->targetGUID)) + if(target->HasAura(m_triggeredByAuraSpell->Id)) + ++count; + if(count) + { + uint32 spellId; + switch(m_spellInfo->Id) + { + case 28062: spellId = 29659; break; + case 28085: spellId = 29660; break; + case 39090: spellId = 39089; break; + case 39093: spellId = 39092; break; + } + Aura *aur = m_caster->GetAura(spellId); + if(!aur) + { + m_caster->CastSpell(m_caster, spellId, true); + aur = m_caster->GetAura(spellId); + } + if(aur) + aur->SetStackAmount(count); + } } + else if(unitTarget->HasAura(m_triggeredByAuraSpell->Id)) + damage = 0; + break; + // Consumption + case 28865: + damage = (m_caster->GetMap()->IsHeroic() ? 4250 : 2750); break; // percent from health with min case 25599: // Thundercrash @@ -1023,8 +1054,15 @@ void Spell::EffectDummy(uint32 i) return; } // Polarity Shift - case 28089: spell_id = roll_chance_i(50) ? 28059 : 28084; break; - case 39096: spell_id = roll_chance_i(50) ? 39088 : 39091; break; + case 28089: + if(unitTarget) + unitTarget->CastSpell(unitTarget, roll_chance_i(50) ? 28059 : 28084, true, NULL, NULL, m_caster->GetGUID()); + break; + // Polarity Shift + case 39096: + if(unitTarget) + unitTarget->CastSpell(unitTarget, roll_chance_i(50) ? 39088 : 39091, true, NULL, NULL, m_caster->GetGUID()); + break; case 29200: // Purify Helboar Meat { if( m_caster->GetTypeId() != TYPEID_PLAYER ) diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 4c83d286620..e60931dc11e 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -3190,6 +3190,7 @@ void SpellMgr::LoadSpellCustomAttr() break; case 24340: case 26558: case 28884: // Meteor case 36837: case 38903: case 41276: // Meteor + case 57467: // Meteor case 26789: // Shard of the Fallen Star case 31436: // Malevolent Cleave case 35181: // Dive Bomb @@ -3199,8 +3200,8 @@ void SpellMgr::LoadSpellCustomAttr() mSpellCustomAttr[i] |= SPELL_ATTR_CU_SHARE_DAMAGE; break; case 27820: // Mana Detonation - case 28062: case 39090: // Positive/Negative Charge - case 28085: case 39093: + //case 28062: case 39090: // Positive/Negative Charge + //case 28085: case 39093: mSpellCustomAttr[i] |= SPELL_ATTR_CU_EXCLUDE_SELF; break; case 44978: case 45001: case 45002: // Wild Magic diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 4427e4c6abb..66874f02b57 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -184,6 +184,12 @@ inline float GetSpellMinRange(SpellEntry const *spellInfo, bool positive) ? GetSpellMinRangeForFriend(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex)) : GetSpellMinRangeForHostile(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex)); } +inline float GetSpellMaxRange(uint32 id, bool positive) +{ + SpellEntry const *spellInfo = GetSpellStore()->LookupEntry(id); + if(!spellInfo) return 0; + return GetSpellMaxRange(spellInfo, positive); +} /*struct DispelEntry { diff --git a/src/game/Unit.h b/src/game/Unit.h index 5267c272677..b22902ba40b 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -83,7 +83,9 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21 AURA_INTERRUPT_FLAG_TELEPORTED = 0x00400000, // 22 AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat - AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000 // 24 removed by any direct damage + AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000, // 24 removed by any direct damage + + AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE), }; enum SpellModOp diff --git a/src/game/UnitAI.cpp b/src/game/UnitAI.cpp index 12dc20692c6..c159861697c 100644 --- a/src/game/UnitAI.cpp +++ b/src/game/UnitAI.cpp @@ -22,17 +22,19 @@ #include "Player.h" #include "Creature.h" #include "SpellAuras.h" +#include "SpellMgr.h" +#include "CreatureAIImpl.h" void UnitAI::AttackStart(Unit *victim) { - if(!victim) - return; - - if(me->Attack(victim, true)) - { - //DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", me->GetName(), victim->GetGUIDLow()); + if(victim && me->Attack(victim, true)) me->GetMotionMaster()->MoveChase(victim); - } +} + +void UnitAI::AttackStartCaster(Unit *victim, float dist) +{ + if(victim && me->Attack(victim, false)) + me->GetMotionMaster()->MoveChase(victim, dist); } void UnitAI::DoMeleeAttackIfReady() @@ -61,6 +63,271 @@ void UnitAI::DoMeleeAttackIfReady() } } +bool UnitAI::DoSpellAttackIfReady(uint32 spell) +{ + if(me->hasUnitState(UNIT_STAT_CASTING)) + return true; + + if(me->isAttackReady()) + { + if(me->IsWithinCombatRange(me->getVictim(), GetSpellMaxRange(spell, false))) + { + me->CastSpell(me->getVictim(), spell, false); + me->resetAttackTimer(); + } + else + return false; + } + return true; +} + +inline bool SelectTargetHelper(const Unit * me, const Unit * target, const bool &playerOnly, const float &dist, const int32 &aura) +{ + if(playerOnly && target->GetTypeId() != TYPEID_PLAYER) + return false; + + if(dist && !me->IsWithinCombatRange(target, dist)) + return false; + + if(aura) + { + if(aura > 0) + { + if(!target->HasAura(aura)) + return false; + } + else + { + if(target->HasAura(aura)) + return false; + } + } + + return true; +} + +struct TargetDistanceOrder : public std::binary_function<const Unit *, const Unit *, bool> +{ + const Unit * me; + TargetDistanceOrder(const Unit* Target) : me(Target) {}; + // functor for operator ">" + bool operator()(const Unit * _Left, const Unit * _Right) const + { + return (me->GetDistanceSq(_Left) < me->GetDistanceSq(_Right)); + } +}; + +Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura) +{ + if(targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) + { + std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList(); + if(position >= m_threatlist.size()) + return NULL; + + std::list<Unit*> targetList; + for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + if(SelectTargetHelper(me, (*itr)->getTarget(), playerOnly, dist, aura)) + targetList.push_back((*itr)->getTarget()); + + if(position >= targetList.size()) + return NULL; + + targetList.sort(TargetDistanceOrder(me)); + + if(targetType == SELECT_TARGET_NEAREST) + { + std::list<Unit*>::iterator i = targetList.begin(); + advance(i, position); + return *i; + } + else + { + std::list<Unit*>::reverse_iterator i = targetList.rbegin(); + advance(i, position); + return *i; + } + } + else + { + std::list<HostilReference*> m_threatlist = me->getThreatManager().getThreatList(); + std::list<HostilReference*>::iterator i; + while(position < m_threatlist.size()) + { + if(targetType == SELECT_TARGET_BOTTOMAGGRO) + { + i = m_threatlist.end(); + advance(i, - (int32)position - 1); + } + else + { + i = m_threatlist.begin(); + if(targetType == SELECT_TARGET_TOPAGGRO) + advance(i, position); + else // random + advance(i, position + rand()%(m_threatlist.size() - position)); + } + + if(SelectTargetHelper(me, (*i)->getTarget(), playerOnly, dist, aura)) + return (*i)->getTarget(); + else + m_threatlist.erase(i); + } + } + + return NULL; +} + +void UnitAI::SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura) +{ + if(targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) + { + std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList(); + if(m_threatlist.empty()) + return; + + for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + if(SelectTargetHelper(me, (*itr)->getTarget(), playerOnly, dist, aura)) + targetList.push_back((*itr)->getTarget()); + + targetList.sort(TargetDistanceOrder(me)); + targetList.resize(num); + if(targetType == SELECT_TARGET_FARTHEST) + targetList.reverse(); + } + else + { + std::list<HostilReference*> m_threatlist = me->getThreatManager().getThreatList(); + std::list<HostilReference*>::iterator i; + while(!m_threatlist.empty() && num) + { + if(targetType == SELECT_TARGET_BOTTOMAGGRO) + { + i = m_threatlist.end(); + --i; + } + else + { + i = m_threatlist.begin(); + if(targetType == SELECT_TARGET_RANDOM) + advance(i, rand()%m_threatlist.size()); + } + + if(SelectTargetHelper(me, (*i)->getTarget(), playerOnly, dist, aura)) + { + targetList.push_back((*i)->getTarget()); + --num; + } + m_threatlist.erase(i); + } + } +} + +void UnitAI::DoCast(uint32 spellId) +{ + Unit *target = NULL; + //sLog.outError("aggre %u %u", spellId, (uint32)AISpellInfo[spellId].target); + switch(AISpellInfo[spellId].target) + { + default: + case AITARGET_SELF: target = me; break; + case AITARGET_VICTIM: target = me->getVictim(); break; + case AITARGET_ENEMY: + { + const SpellEntry * spellInfo = GetSpellStore()->LookupEntry(spellId); + bool playerOnly = spellInfo->AttributesEx3 & SPELL_ATTR_EX3_PLAYERS_ONLY; + float range = GetSpellMaxRange(spellInfo, false); + target = SelectTarget(SELECT_TARGET_RANDOM, 0, GetSpellMaxRange(spellInfo, false), playerOnly); + break; + } + case AITARGET_ALLY: target = me; break; + case AITARGET_BUFF: target = me; break; + case AITARGET_DEBUFF: + { + const SpellEntry * spellInfo = GetSpellStore()->LookupEntry(spellId); + bool playerOnly = spellInfo->AttributesEx3 & SPELL_ATTR_EX3_PLAYERS_ONLY; + float range = GetSpellMaxRange(spellInfo, false); + if(!(spellInfo->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE) + && !(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM) + && SelectTargetHelper(me, me->getVictim(), playerOnly, range, -(int32)spellId)) + target = me->getVictim(); + else + target = SelectTarget(SELECT_TARGET_RANDOM, 0, range, playerOnly, -(int32)spellId); + break; + } + } + + if(target) + me->CastSpell(target, spellId, false); +} + +void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered) +{ + if(!victim || me->hasUnitState(UNIT_STAT_CASTING) && !triggered) + return; + + me->CastSpell(victim, spellId, triggered); +} + +void UnitAI::DoCastAOE(uint32 spellId, bool triggered) +{ + if(!triggered && me->hasUnitState(UNIT_STAT_CASTING)) + return; + + me->CastSpell((Unit*)NULL, spellId, triggered); +} + +#define UPDATE_TARGET(a) {if(AIInfo->target<a) AIInfo->target=a;} + +void UnitAI::FillAISpellInfo() +{ + AISpellInfo = new AISpellInfoType[GetSpellStore()->GetNumRows()]; + + AISpellInfoType *AIInfo = AISpellInfo; + const SpellEntry * spellInfo; + + for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i, ++AIInfo) + { + spellInfo = GetSpellStore()->LookupEntry(i); + if(!spellInfo) + continue; + + if(spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD) + AIInfo->condition = AICOND_DIE; + else if(IsPassiveSpell(i) || GetSpellDuration(spellInfo) == -1) + AIInfo->condition = AICOND_AGGRO; + else + AIInfo->condition = AICOND_COMBAT; + + if(AIInfo->cooldown < spellInfo->RecoveryTime) + AIInfo->cooldown = spellInfo->RecoveryTime; + + if(!GetSpellMaxRange(spellInfo, false)) + UPDATE_TARGET(AITARGET_SELF) + else + { + for(uint32 j = 0; j < 3; ++j) + { + uint32 targetType = spellInfo->EffectImplicitTargetA[j]; + + if(targetType == TARGET_UNIT_TARGET_ENEMY + || targetType == TARGET_DST_TARGET_ENEMY) + UPDATE_TARGET(AITARGET_VICTIM) + else if(targetType == TARGET_UNIT_AREA_ENEMY_DST) + UPDATE_TARGET(AITARGET_ENEMY) + + if(spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA) + { + if(targetType == TARGET_UNIT_TARGET_ENEMY) + UPDATE_TARGET(AITARGET_DEBUFF) + else if(IsPositiveSpell(i)) + UPDATE_TARGET(AITARGET_BUFF) + } + } + } + } +} + //Enable PlayerAI when charmed void PlayerAI::OnCharmed(bool apply) { me->IsAIEnabled = apply; } diff --git a/src/game/UnitAI.h b/src/game/UnitAI.h index 04de74f480e..095edb0fc3e 100644 --- a/src/game/UnitAI.h +++ b/src/game/UnitAI.h @@ -25,6 +25,8 @@ class Unit; class Player; +struct AISpellInfoType; +enum SelectAggroTarget; class TRINITY_DLL_SPEC UnitAI { @@ -43,10 +45,23 @@ class TRINITY_DLL_SPEC UnitAI virtual void OnCharmed(bool apply) = 0; // Pass parameters between AI - virtual void DoAction(const int32 param) {} + virtual void DoAction(const int32 param = 0) {} + virtual void SetGUID(const uint64 &guid, const int32 param = 0) {} + + Unit* SelectTarget(SelectAggroTarget target, uint32 position = 0, float dist = 0, bool playerOnly = false, int32 aura = 0); + void SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAggroTarget target, float dist = 0, bool playerOnly = false, int32 aura = 0); + + void AttackStartCaster(Unit *victim, float dist); + + void DoCast(uint32 spellId); + void DoCast(Unit* victim, uint32 spellId, bool triggered = false); + void DoCastAOE(uint32 spellId, bool triggered = false); - //Do melee swing of current victim if in rnage and ready and not casting void DoMeleeAttackIfReady(); + bool DoSpellAttackIfReady(uint32 spell); + + static AISpellInfoType *AISpellInfo; + static void FillAISpellInfo(); }; class TRINITY_DLL_SPEC PlayerAI : public UnitAI |