aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bindings/scripts/include/sc_creature.cpp32
-rw-r--r--src/bindings/scripts/include/sc_creature.h27
-rw-r--r--src/game/AggressorAI.cpp125
-rw-r--r--src/game/AggressorAI.h34
-rw-r--r--src/game/CreatureAI.cpp38
-rw-r--r--src/game/CreatureAIRegistry.cpp1
-rw-r--r--src/game/CreatureAISelector.cpp12
-rw-r--r--src/game/SpellMgr.cpp3
-rw-r--r--src/game/Unit.cpp16
-rw-r--r--src/game/UnitAI.cpp7
10 files changed, 205 insertions, 90 deletions
diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp
index 42ee15074ed..e5a6eb958e0 100644
--- a/src/bindings/scripts/include/sc_creature.cpp
+++ b/src/bindings/scripts/include/sc_creature.cpp
@@ -55,6 +55,7 @@ void SummonList::DespawnAll()
else
{
erase(begin());
+ summon->SetVisibility(VISIBILITY_OFF);
summon->setDeathState(JUST_DIED);
summon->RemoveCorpse();
}
@@ -634,6 +635,37 @@ void Scripted_NoMovementAI::AttackStart(Unit* who)
}
}
+void BossAI::_Reset()
+{
+ events.Reset();
+ summons.DespawnAll();
+ instance->SetBossState(bossId, NOT_STARTED);
+}
+
+void BossAI::_JustDied()
+{
+ events.Reset();
+ summons.DespawnAll();
+ instance->SetBossState(bossId, DONE);
+}
+
+void BossAI::_EnterCombat()
+{
+ DoZoneInCombat();
+ instance->SetBossState(bossId, IN_PROGRESS);
+}
+
+void BossAI::JustSummoned(Creature *summon)
+{
+ summons.Summon(summon);
+ DoZoneInCombat(summon);
+}
+
+void BossAI::SummonedCreatureDespawn(Creature *summon)
+{
+ summons.Despawn(summon);
+}
+
#define GOBJECT(x) (const_cast<GameObjectInfo*>(GetGameObjectInfo(x)))
void LoadOverridenSQLData()
diff --git a/src/bindings/scripts/include/sc_creature.h b/src/bindings/scripts/include/sc_creature.h
index 8336df545b6..26bf5fb400b 100644
--- a/src/bindings/scripts/include/sc_creature.h
+++ b/src/bindings/scripts/include/sc_creature.h
@@ -12,6 +12,8 @@
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
+class ScriptedInstance;
+
class SummonList : private std::list<uint64>
{
public:
@@ -216,5 +218,30 @@ struct TRINITY_DLL_DECL NullCreatureAI : public ScriptedAI
void UpdateAI(const uint32) {}
};
+struct TRINITY_DLL_DECL BossAI : public ScriptedAI
+{
+ BossAI(Creature *c, uint32 id) : ScriptedAI(c), bossId(id)
+ , summons(me), instance((ScriptedInstance*)c->GetInstanceData())
+ {}
+
+ uint32 bossId;
+ EventMap events;
+ SummonList summons;
+ ScriptedInstance *instance;
+
+ void JustSummoned(Creature *summon);
+ void SummonedCreatureDespawn(Creature *summon);
+
+ void UpdateAI(const uint32 diff) = 0;
+
+ void _Reset();
+ void _EnterCombat();
+ void _JustDied();
+
+ void Reset() { _Reset(); }
+ void EnterCombat(Unit *who) { _EnterCombat(); }
+ void JustDied(Unit *killer) { _JustDied(); }
+};
+
#endif
diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp
index a637e7e40f0..286138fc991 100644
--- a/src/game/AggressorAI.cpp
+++ b/src/game/AggressorAI.cpp
@@ -19,16 +19,9 @@
*/
#include "AggressorAI.h"
-#include "Errors.h"
-#include "Creature.h"
-#include "ObjectAccessor.h"
-#include "VMapFactory.h"
-#include "World.h"
+#include "SpellMgr.h"
-#include <list>
-
-int
-AggressorAI::Permissible(const Creature *creature)
+int AggressorAI::Permissible(const Creature *creature)
{
// have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
if( !creature->isCivilian() && !creature->IsNeutralToAll() )
@@ -37,79 +30,83 @@ AggressorAI::Permissible(const Creature *creature)
return PERMIT_BASE_NO;
}
-AggressorAI::AggressorAI(Creature *c) : CreatureAI(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
+void AggressorAI::UpdateAI(const uint32 /*diff*/)
{
+ if(!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
}
-void AggressorAI::EnterEvadeMode()
+int SpellAI::Permissible(const Creature *creature)
{
- if( !m_creature->isAlive() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", m_creature->GetGUIDLow());
- i_victimGuid = 0;
- m_creature->CombatStop(true);
- m_creature->DeleteThreatList();
- return;
- }
+ return PERMIT_BASE_NO;
+}
- Unit* victim = ObjectAccessor::GetUnit(*m_creature, i_victimGuid );
+void SpellAI::InitializeAI()
+{
+ for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ if(me->m_spells[i] && GetSpellStore()->LookupEntry(me->m_spells[i]))
+ spells.push_back(me->m_spells[i]);
+}
- if( !victim )
- {
- DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", m_creature->GetGUIDLow());
- }
- else if( !victim->isAlive() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", m_creature->GetGUIDLow());
- }
- else if( victim->HasStealthAura() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", m_creature->GetGUIDLow());
- }
- else if( victim->isInFlight() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", m_creature->GetGUIDLow());
- }
- else
- {
- DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", m_creature->GetGUIDLow());
- //i_state = STATE_LOOK_AT_VICTIM;
- //i_tracker.Reset(TIME_INTERVAL_LOOK);
- }
+void SpellAI::Reset()
+{
+ events.Reset();
+}
- if(!m_creature->GetCharmerOrOwner())
- {
- m_creature->RemoveAllAuras();
+void SpellAI::JustDied(Unit *killer)
+{
+ for(SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
+ if(AISpellInfo[*i].condition == AICOND_DIE)
+ me->CastSpell(killer, *i, true);
+}
- // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
- if( m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
- m_creature->GetMotionMaster()->MoveTargetedHome();
+void SpellAI::EnterCombat(Unit *who)
+{
+ for(SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
+ {
+ if(AISpellInfo[*i].condition == AICOND_AGGRO)
+ me->CastSpell(who, *i, true);
+ else if(AISpellInfo[*i].condition == AICOND_COMBAT)
+ events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown);
}
- else if (m_creature->GetOwner() && m_creature->GetOwner()->isAlive())
- m_creature->GetMotionMaster()->MoveFollow(m_creature->GetOwner(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
-
- m_creature->DeleteThreatList();
- i_victimGuid = 0;
- m_creature->CombatStop(true);
- m_creature->SetLootRecipient(NULL);
}
-void
-AggressorAI::UpdateAI(const uint32 /*diff*/)
+void SpellAI::UpdateAI(const uint32 diff)
{
- // update i_victimGuid if m_creature->getVictim() !=0 and changed
if(!UpdateVictim())
return;
- i_victimGuid = m_creature->getVictim()->GetGUID();
+ events.Update(diff);
- if( m_creature->isAttackReady() )
+ if(me->hasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ if(uint32 spellId = events.ExecuteEvent())
{
- if( m_creature->IsWithinMeleeRange(m_creature->getVictim()))
+ Unit *target = NULL;
+ //sLog.outError("aggre %u %u", spellId, (uint32)AISpellInfo[spellId].target);
+ switch(AISpellInfo[spellId].target)
{
- m_creature->AttackerStateUpdate(m_creature->getVictim());
- m_creature->resetAttackTimer();
+ 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;
+ }
}
+ me->CastSpell(target, spellId, false);
+ events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown);
}
+ else
+ DoMeleeAttackIfReady();
}
-
diff --git a/src/game/AggressorAI.h b/src/game/AggressorAI.h
index 983498716e6..94a6b0b3bf1 100644
--- a/src/game/AggressorAI.h
+++ b/src/game/AggressorAI.h
@@ -22,31 +22,35 @@
#define TRINITY_AGGRESSORAI_H
#include "CreatureAI.h"
-#include "Timer.h"
+#include "CreatureAIImpl.h"
class Creature;
class TRINITY_DLL_DECL AggressorAI : public CreatureAI
{
- enum AggressorState
- {
- STATE_NORMAL = 1,
- STATE_LOOK_AT_VICTIM = 2
- };
-
public:
-
- explicit AggressorAI(Creature *c);
-
- void EnterEvadeMode();
+ explicit AggressorAI(Creature *c) : CreatureAI(c) {}
void UpdateAI(const uint32);
static int Permissible(const Creature *);
+};
+
+typedef std::vector<uint32> SpellVct;
+class TRINITY_DLL_DECL SpellAI : public CreatureAI
+{
+ public:
+ explicit SpellAI(Creature *c) : CreatureAI(c) {}
+
+ void InitializeAI();
+ void Reset();
+ void EnterCombat(Unit* who);
+ void JustDied(Unit *killer);
+ void UpdateAI(const uint32 diff);
+ static int Permissible(const Creature *);
private:
- uint64 i_victimGuid;
- AggressorState i_state;
- TimeTracker i_tracker;
+ EventMap events;
+ SpellVct spells;
};
-#endif
+#endif
diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp
index 011ffab22e5..123fa1182a1 100644
--- a/src/game/CreatureAI.cpp
+++ b/src/game/CreatureAI.cpp
@@ -21,9 +21,8 @@
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "Creature.h"
-#include "Player.h"
-#include "Pet.h"
#include "World.h"
+#include "SpellMgr.h"
//Disable CreatureAI when charmed
void CreatureAI::OnCharmed(bool apply)
@@ -261,16 +260,47 @@ void CreatureAI::FillAISpellInfo()
{
AISpellInfo = new AISpellInfoType[GetSpellStore()->GetNumRows()];
+ AISpellInfoType *AIInfo = AISpellInfo;
const SpellEntry * spellInfo;
- for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i, ++AIInfo)
{
spellInfo = GetSpellStore()->LookupEntry(i);
- if (!spellInfo)
+ 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;
+ }
+ }
}
}
}
diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp
index fc7130b0913..162ebd9b47c 100644
--- a/src/game/CreatureAIRegistry.cpp
+++ b/src/game/CreatureAIRegistry.cpp
@@ -46,6 +46,7 @@ namespace AIRegistry
(new CreatureAIFactory<PetAI>("PetAI"))->RegisterSelf();
(new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf();
(new CreatureAIFactory<OutdoorPvPObjectiveAI>("OutdoorPvPObjectiveAI"))->RegisterSelf();
+ (new CreatureAIFactory<SpellAI>("SpellAI"))->RegisterSelf();
(new CreatureAIFactory<CreatureEventAI>("EventAI"))->RegisterSelf();
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp
index b270de289c7..bac4f3a4973 100644
--- a/src/game/CreatureAISelector.cpp
+++ b/src/game/CreatureAISelector.cpp
@@ -69,6 +69,18 @@ namespace FactorySelector
ai_factory = ai_registry.GetRegistryItem("CritterAI");
}
+ if(!ai_factory)
+ {
+ for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ {
+ if(creature->m_spells[i])
+ {
+ ai_factory = ai_registry.GetRegistryItem("SpellAI");
+ break;
+ }
+ }
+ }
+
// select by permit check
if(!ai_factory)
{
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
index a23f32d1a52..9b1cb798849 100644
--- a/src/game/SpellMgr.cpp
+++ b/src/game/SpellMgr.cpp
@@ -27,6 +27,7 @@
#include "Chat.h"
#include "Spell.h"
#include "BattleGroundMgr.h"
+#include "CreatureAI.h"
bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
@@ -2520,6 +2521,8 @@ void SpellMgr::LoadSpellCustomAttr()
SummonPropertiesEntry *properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121));
properties->Type = SUMMON_TYPE_TOTEM;
+
+ CreatureAI::FillAISpellInfo();
}
void SpellMgr::LoadSpellLinked()
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index 3f09eec9351..28d2fbd619b 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -11607,11 +11607,14 @@ void CharmInfo::InitPossessCreateSpells()
{
for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
{
- uint32 spellid = ((Creature*)m_unit)->m_spells[i];
- if(IsPassiveSpell(spellid))
- m_unit->CastSpell(m_unit, spellid, true);
+ uint32 spellId = ((Creature*)m_unit)->m_spells[i];
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(spellInfo && spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD)
+ spellId = 0;
+ if(IsPassiveSpell(spellId))
+ m_unit->CastSpell(m_unit, spellId, true);
else
- AddSpellToAB(0, spellid, ACT_DISABLED);
+ AddSpellToAB(0, spellId, ACT_DISABLED);
}
}
}
@@ -11629,6 +11632,10 @@ void CharmInfo::InitCharmCreateSpells()
for(uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
{
uint32 spellId = ((Creature*)m_unit)->m_spells[x];
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(spellInfo && spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD)
+ spellId = 0;
+
m_charmspells[x].spellId = spellId;
if(!spellId)
@@ -11643,7 +11650,6 @@ void CharmInfo::InitCharmCreateSpells()
{
ActiveStates newstate;
bool onlyselfcast = true;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo) onlyselfcast = false;
for(uint32 i = 0;i<3 && onlyselfcast;++i) //non existent spell will not make any problems as onlyselfcast would be false -> break right away
diff --git a/src/game/UnitAI.cpp b/src/game/UnitAI.cpp
index 4914bd36b7c..c991c75603d 100644
--- a/src/game/UnitAI.cpp
+++ b/src/game/UnitAI.cpp
@@ -35,8 +35,11 @@ void UnitAI::AttackStart(Unit *victim)
void UnitAI::DoMeleeAttackIfReady()
{
+ if(me->hasUnitState(UNIT_STAT_CASTING))
+ return;
+
//Make sure our attack is ready and we aren't currently casting before checking distance
- if (me->isAttackReady() && !me->hasUnitState(UNIT_STAT_CASTING))
+ if (me->isAttackReady())
{
//If we are within range melee the target
if (me->IsWithinMeleeRange(me->getVictim()))
@@ -45,7 +48,7 @@ void UnitAI::DoMeleeAttackIfReady()
me->resetAttackTimer();
}
}
- if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK) && !me->hasUnitState(UNIT_STAT_CASTING))
+ if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK))
{
//If we are within range melee the target
if (me->IsWithinMeleeRange(me->getVictim()))