diff options
Diffstat (limited to 'src/game/PetAI.cpp')
-rw-r--r-- | src/game/PetAI.cpp | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp deleted file mode 100644 index 09ec8fae53f..00000000000 --- a/src/game/PetAI.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> - * - * 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 "PetAI.h" -#include "Errors.h" -#include "Pet.h" -#include "Player.h" -#include "DBCStores.h" -#include "Spell.h" -#include "ObjectAccessor.h" -#include "SpellMgr.h" -#include "Creature.h" -#include "World.h" -#include "Util.h" - -int PetAI::Permissible(const Creature *creature) -{ - if (creature->isPet()) - return PERMIT_BASE_SPECIAL; - - return PERMIT_BASE_NO; -} - -PetAI::PetAI(Creature *c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK) -{ - m_AllySet.clear(); - UpdateAllies(); -} - -void PetAI::EnterEvadeMode() -{ -} - -bool PetAI::_needToStop() const -{ - // This is needed for charmed creatures, as once their target was reset other effects can trigger threat - if (me->isCharmed() && me->getVictim() == me->GetCharmer()) - return true; - - return !me->canAttack(me->getVictim()); -} - -void PetAI::_stopAttack() -{ - if (!me->isAlive()) - { - DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", me->GetGUIDLow()); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - me->CombatStop(); - me->getHostileRefManager().deleteReferences(); - - return; - } - - me->AttackStop(); - me->GetCharmInfo()->SetIsCommandAttack(false); - HandleReturnMovement(); -} - -void PetAI::UpdateAI(const uint32 diff) -{ - if (!me->isAlive()) - return; - - Unit* owner = me->GetCharmerOrOwner(); - - if (m_updateAlliesTimer <= diff) - // UpdateAllies self set update timer - UpdateAllies(); - else - m_updateAlliesTimer -= diff; - - // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc. - if (me->getVictim()) - { - if (_needToStop()) - { - DEBUG_LOG("Pet AI stoped attacking [guid=%u]", me->GetGUIDLow()); - _stopAttack(); - return; - } - - DoMeleeAttackIfReady(); - } - else if (owner && me->GetCharmInfo()) //no victim - { - Unit *nextTarget = SelectNextTarget(); - - if (nextTarget) - AttackStart(nextTarget); - else - HandleReturnMovement(); - } - else if (owner && !me->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim - me->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, me->GetFollowAngle()); - - if (!me->GetCharmInfo()) - return; - - // Autocast (casted only in combat or persistent spells in any state) - if (me->GetGlobalCooldown() == 0 && !me->hasUnitState(UNIT_STAT_CASTING)) - { - typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; - TargetSpellList targetSpellStore; - - for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) - { - uint32 spellID = me->GetPetAutoSpellOnPos(i); - if (!spellID) - continue; - - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); - if (!spellInfo) - continue; - - // ignore some combinations of combat state and combat/noncombat spells - if (!me->getVictim()) - { - // ignore attacking spells, and allow only self/around spells - if (!IsPositiveSpell(spellInfo->Id)) - continue; - - // non combat spells allowed - // only pet spells have IsNonCombatSpell and not fit this reqs: - // Consume Shadows, Lesser Invisibility, so ignore checks for its - if (!IsNonCombatSpell(spellInfo)) - { - // allow only spell without spell cost or with spell cost but not duration limit - int32 duration = GetSpellDuration(spellInfo); - if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0) - continue; - - // allow only spell without cooldown > duration - int32 cooldown = GetSpellRecoveryTime(spellInfo); - if (cooldown >= 0 && duration >= 0 && cooldown > duration) - continue; - } - } - else - { - // just ignore non-combat spells - if (IsNonCombatSpell(spellInfo)) - continue; - } - - Spell *spell = new Spell(me, spellInfo, false, 0); - - // Fix to allow pets on STAY to autocast - if (me->getVictim() && _CanAttack(me->getVictim()) && spell->CanAutoCast(me->getVictim())) - { - targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(me->getVictim(), spell)); - continue; - } - else - { - bool spellUsed = false; - for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) - { - Unit* Target = ObjectAccessor::GetUnit(*me,*tar); - - //only buff targets that are in combat, unless the spell can only be cast while out of combat - if (!Target) - continue; - - if (spell->CanAutoCast(Target)) - { - targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell)); - spellUsed = true; - break; - } - } - if (!spellUsed) - delete spell; - } - } - - //found units to cast on to - if (!targetSpellStore.empty()) - { - uint32 index = urand(0, targetSpellStore.size() - 1); - - Spell* spell = targetSpellStore[index].second; - Unit* target = targetSpellStore[index].first; - - targetSpellStore.erase(targetSpellStore.begin() + index); - - SpellCastTargets targets; - targets.setUnitTarget(target); - - if (!me->HasInArc(M_PI, target)) - { - me->SetInFront(target); - if (target && target->GetTypeId() == TYPEID_PLAYER) - me->SendUpdateToPlayer(target->ToPlayer()); - - if (owner && owner->GetTypeId() == TYPEID_PLAYER) - me->SendUpdateToPlayer(owner->ToPlayer()); - } - - me->AddCreatureSpellCooldown(spell->m_spellInfo->Id); - - spell->prepare(&targets); - } - - // deleted cached Spell objects - for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) - delete itr->second; - } -} - -void PetAI::UpdateAllies() -{ - Unit* owner = me->GetCharmerOrOwner(); - Group *pGroup = NULL; - - m_updateAlliesTimer = 10*IN_MILISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance - - if (!owner) - return; - else if (owner->GetTypeId() == TYPEID_PLAYER) - pGroup = owner->ToPlayer()->GetGroup(); - - //only pet and owner/not in group->ok - if (m_AllySet.size() == 2 && !pGroup) - return; - //owner is in group; group members filled in already (no raid -> subgroupcount = whole count) - if (pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2)) - return; - - m_AllySet.clear(); - m_AllySet.insert(me->GetGUID()); - if (pGroup) //add group - { - for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* Target = itr->getSource(); - if (!Target || !pGroup->SameSubGroup((Player*)owner, Target)) - continue; - - if (Target->GetGUID() == owner->GetGUID()) - continue; - - m_AllySet.insert(Target->GetGUID()); - } - } - else //remove group - m_AllySet.insert(owner->GetGUID()); -} - -void PetAI::KilledUnit(Unit *victim) -{ - // Called from Unit::Kill() in case where pet or owner kills something - // if owner killed this victim, pet may still be attacking something else - if (me->getVictim() && me->getVictim() != victim) - return; - - // Clear target just in case. May help problem where health / focus / mana - // regen gets stuck. Also resets attack command. - // Can't use _stopAttack() because that activates movement handlers and ignores - // next target selection - me->AttackStop(); - me->GetCharmInfo()->SetIsCommandAttack(false); - - Unit *nextTarget = SelectNextTarget(); - - if (nextTarget) - AttackStart(nextTarget); - else - HandleReturnMovement(); // Return -} - -void PetAI::AttackStart(Unit *target) -{ - // Overrides Unit::AttackStart to correctly evaluate Pet states - - // Check all pet states to decide if we can attack this target - if (!_CanAttack(target)) - return; - - // We can attack, should we chase or not? - if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) - DoAttack(target,true); // FOLLOW, attack with chase - else - { - if (me->GetCharmInfo()->IsCommandAttack()) - DoAttack(target,true); // STAY or FOLLOW, player clicked "attack" so attack with chase - else - DoAttack(target,false); // STAY, target in range, attack not clicked so attack without chase - } -} - -Unit *PetAI::SelectNextTarget() -{ - // Provides next target selection after current target death - - // Passive pets don't do next target selection - if (me->HasReactState(REACT_PASSIVE)) - return NULL; - - // Check pet's attackers first to prevent dragging mobs back - // to owner - if (me->getAttackerForHelper()) - return me->getAttackerForHelper(); - - // Check owner's attackers if pet didn't have any - if (me->GetCharmerOrOwner()->getAttackerForHelper()) - return me->GetCharmerOrOwner()->getAttackerForHelper(); - - // 3.0.2 - Pets now start attacking their owners target in defensive mode as soon as the hunter does - if (me->GetCharmerOrOwner()->getVictim()) - return me->GetCharmerOrOwner()->getVictim(); - - // Default - return NULL; -} - -void PetAI::HandleReturnMovement() -{ - // Handles moving the pet back to stay or owner - - if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) - { - if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning()) - { - // Return to previous position where stay was clicked - if (!me->GetCharmInfo()->IsCommandAttack()) - { - float x,y,z; - - me->GetCharmInfo()->GetStayPosition(x, y, z); - me->GetCharmInfo()->SetIsReturning(true); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(me->GetGUIDLow(),x,y,z); - } - } - } - else // COMMAND_FOLLOW - { - if (!me->GetCharmInfo()->IsFollowing() && !me->GetCharmInfo()->IsReturning()) - { - if (!me->GetCharmInfo()->IsCommandAttack()) - { - me->GetCharmInfo()->SetIsReturning(true); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle()); - } - } - } - -} - -void PetAI::DoAttack(Unit *target, bool chase) -{ - // Handles attack with or without chase and also resets all - // PetAI flags for next update / creature kill - - // me->GetCharmInfo()->SetIsCommandAttack(false); - - // The following conditions are true if chase == true - // (Follow && (Aggressive || Defensive)) - // ((Stay || Follow) && (Passive && player clicked attack)) - - if (chase) - { - if (me->Attack(target,true)) - { - me->GetCharmInfo()->SetIsAtStay(false); - me->GetCharmInfo()->SetIsFollowing(false); - me->GetCharmInfo()->SetIsReturning(false); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveChase(target); - } - } - else // (Stay && ((Aggressive || Defensive) && In Melee Range))) - { - me->GetCharmInfo()->SetIsAtStay(true); - me->GetCharmInfo()->SetIsFollowing(false); - me->GetCharmInfo()->SetIsReturning(false); - me->Attack(target,true); - } -} - -void PetAI::MovementInform(uint32 moveType, uint32 data) -{ - // Receives notification when pet reaches stay or follow owner - switch (moveType) - { - case POINT_MOTION_TYPE: - { - // Pet is returning to where stay was clicked. data should be - // pet's GUIDLow since we set that as the waypoint ID - if (data == me->GetGUIDLow() && me->GetCharmInfo()->IsReturning()) - { - me->GetCharmInfo()->SetIsAtStay(true); - me->GetCharmInfo()->SetIsReturning(false); - me->GetCharmInfo()->SetIsFollowing(false); - me->GetCharmInfo()->SetIsCommandAttack(false); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - } - } - break; - - case TARGETED_MOTION_TYPE: - { - // If data is owner's GUIDLow then we've reached follow point, - // otherwise we're probably chasing a creature - if (me->GetCharmerOrOwner() && me->GetCharmInfo() && data == me->GetCharmerOrOwner()->GetGUIDLow() && me->GetCharmInfo()->IsReturning()) - { - me->GetCharmInfo()->SetIsAtStay(false); - me->GetCharmInfo()->SetIsReturning(false); - me->GetCharmInfo()->SetIsFollowing(true); - me->GetCharmInfo()->SetIsCommandAttack(false); - me->addUnitState(UNIT_STAT_FOLLOW); - } - } - break; - - default: - break; - } -} - -bool PetAI::_CanAttack(Unit *target) -{ - // Evaluates wether a pet can attack a specific - // target based on CommandState, ReactState and other flags - - // Returning - check first since pets returning ignore attacks - if (me->GetCharmInfo()->IsReturning()) - return false; - - // Passive - check now so we don't have to worry about passive in later checks - if (me->HasReactState(REACT_PASSIVE)) - return me->GetCharmInfo()->IsCommandAttack(); - - // Pets commanded to attack should not stop their approach if attacked by another creature - if (me->getVictim() && (me->getVictim() != target)) - return !me->GetCharmInfo()->IsCommandAttack(); - - // From this point on, pet will always be either aggressive or defensive - - // Stay - can attack if target is within range or commanded to - if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) - return (me->IsWithinMeleeRange(target, MIN_MELEE_REACH) || me->GetCharmInfo()->IsCommandAttack()); - - // Follow - if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) - return true; - - // default, though we shouldn't ever get here - return false; -} |