/* * Copyright (C) 2005-2009 MaNGOS * * Copyright (C) 2008-2009 Trinity * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "CombatAI.h" #include "SpellMgr.h" int AggressorAI::Permissible(const Creature *creature) { // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight if( !creature->isCivilian() && !creature->IsNeutralToAll() ) return PERMIT_BASE_PROACTIVE; return PERMIT_BASE_NO; } void AggressorAI::UpdateAI(const uint32 /*diff*/) { if(!UpdateVictim()) return; DoMeleeAttackIfReady(); } // some day we will delete these useless things int CombatAI::Permissible(const Creature *creature) { return PERMIT_BASE_NO; } int ArchorAI::Permissible(const Creature *creature) { return PERMIT_BASE_NO; } int TurretAI::Permissible(const Creature *creature) { return PERMIT_BASE_NO; } int AOEAI::Permissible(const Creature *creature) { return PERMIT_BASE_NO; } void CombatAI::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]); CreatureAI::InitializeAI(); } void CombatAI::Reset() { events.Reset(); } void CombatAI::JustDied(Unit *killer) { for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i) if(AISpellInfo[*i].condition == AICOND_DIE) me->CastSpell(killer, *i, true); } void CombatAI::EnterCombat(Unit *who) { for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i) { if(AISpellInfo[*i].condition == AICOND_AGGRO) me->CastSpell(who, *i, false); else if(AISpellInfo[*i].condition == AICOND_COMBAT) events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown); } } void CombatAI::UpdateAI(const uint32 diff) { if(!UpdateVictim()) return; events.Update(diff); if(me->hasUnitState(UNIT_STAT_CASTING)) return; if(uint32 spellId = events.ExecuteEvent()) { DoCast(spellId); events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown); } else DoMeleeAttackIfReady(); } ///////////////// //CasterAI ///////////////// void CasterAI::InitializeAI() { CombatAI::InitializeAI(); float m_attackDist = 30.0f; for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr) if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange) m_attackDist = GetAISpellInfo(*itr)->maxRange; if (m_attackDist == 30.0f) m_attackDist = MELEE_RANGE; } void CasterAI::EnterCombat(Unit *who) { if (spells.empty()) return; uint32 spell = rand()%spells.size(); uint32 count = 0; for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count) { if(AISpellInfo[*itr].condition == AICOND_AGGRO) me->CastSpell(who, *itr, false); else if (AISpellInfo[*itr].condition == AICOND_COMBAT) { uint32 cooldown = GetAISpellInfo(*itr)->realCooldown; if (count == spell) { DoCast(spells[spell]); cooldown += me->GetCurrentSpellCastTime(*itr); } events.ScheduleEvent(*itr, cooldown); } } } void CasterAI::UpdateAI(const uint32 diff) { if(!UpdateVictim()) return; events.Update(diff); if(me->hasUnitState(UNIT_STAT_CASTING)) return; if(uint32 spellId = events.ExecuteEvent()) { DoCast(spellId); uint32 casttime = me->GetCurrentSpellCastTime(spellId); events.ScheduleEvent(spellId, (casttime ? casttime : 500) + GetAISpellInfo(spellId)->realCooldown); } } ////////////// //ArchorAI ////////////// ArchorAI::ArchorAI(Creature *c) : CreatureAI(c) { ASSERT(me->m_spells[0]); m_minRange = GetSpellMinRange(me->m_spells[0], false); if(!m_minRange) m_minRange = MELEE_RANGE; me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false); me->m_SightDistance = me->m_CombatDistance; } void ArchorAI::AttackStart(Unit *who) { if(!who) return; if(me->IsWithinCombatRange(who, m_minRange)) { if(me->Attack(who, true) && !who->IsFlying()) me->GetMotionMaster()->MoveChase(who); } else { if(me->Attack(who, false) && !who->IsFlying()) me->GetMotionMaster()->MoveChase(who, me->m_CombatDistance); } if(who->IsFlying()) me->GetMotionMaster()->MoveIdle(); } void ArchorAI::UpdateAI(const uint32 diff) { if(!UpdateVictim()) return; if(!me->IsWithinCombatRange(me->getVictim(), m_minRange)) DoSpellAttackIfReady(me->m_spells[0]); else DoMeleeAttackIfReady(); } ////////////// //TurretAI ////////////// TurretAI::TurretAI(Creature *c) : CreatureAI(c) { ASSERT(me->m_spells[0]); m_minRange = GetSpellMinRange(me->m_spells[0], false); me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false); me->m_SightDistance = me->m_CombatDistance; } bool TurretAI::CanAIAttack(const Unit *who) const { // TODO: use one function to replace it if(!me->IsWithinCombatRange(me->getVictim(), me->m_CombatDistance) || m_minRange && me->IsWithinCombatRange(me->getVictim(), m_minRange)) return false; return true; } void TurretAI::AttackStart(Unit *who) { if(who) me->Attack(who, false); } void TurretAI::UpdateAI(const uint32 diff) { if(!UpdateVictim()) return; DoSpellAttackIfReady(me->m_spells[0]); //if(!DoSpellAttackIfReady(me->m_spells[0])) //if(HostilReference *ref = me->getThreatManager().getCurrentVictim()) //ref->removeReference(); } ////////////// //AOEAI ////////////// AOEAI::AOEAI(Creature *c) : CreatureAI(c) { ASSERT(me->m_spells[0]); me->SetVisibility(VISIBILITY_ON);//visible to see all spell anims me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);//can't be targeted me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_1);//can't be damaged me->SetDisplayId(11686);//invisible model,around a size of a player } bool AOEAI::CanAIAttack(const Unit *who) const { return false; } void AOEAI::AttackStart(Unit *who) { } void AOEAI::UpdateAI(const uint32 diff) { if(!me->HasAura(me->m_spells[0])) me->CastSpell(me, me->m_spells[0],false); }