*Update Naxx scripts. Now only Kelthuzad is incompleted
--HG-- branch : trunk
This commit is contained in:
@@ -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
|
||||
|
||||
+10
-192
@@ -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())
|
||||
|
||||
+2
-17
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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); }
|
||||
|
||||
+39
-4
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+274
-7
@@ -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; }
|
||||
|
||||
|
||||
+17
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user