diff options
author | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
---|---|---|
committer | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
commit | e585187b248f48b3c6e9247b49fa07c6565d65e5 (patch) | |
tree | 637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src/game/PetAI.cpp | |
parent | 26b5e033ffde3d161382fc9addbfa99738379641 (diff) |
*Backed out changeset 3be01fb200a5
--HG--
branch : trunk
Diffstat (limited to 'src/game/PetAI.cpp')
-rw-r--r-- | src/game/PetAI.cpp | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 64cf01b383d..534500e51ea 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -17,6 +17,7 @@ * 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" @@ -28,27 +29,34 @@ #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(m_creature->isCharmed() && m_creature->getVictim() == m_creature->GetCharmer()) return true; + return !m_creature->canAttack(m_creature->getVictim()); } + void PetAI::_stopAttack() { if( !m_creature->isAlive() ) @@ -58,20 +66,25 @@ void PetAI::_stopAttack() m_creature->GetMotionMaster()->MoveIdle(); m_creature->CombatStop(); m_creature->getHostilRefManager().deleteReferences(); + return; } + m_creature->AttackStop(); me->GetCharmInfo()->SetIsCommandAttack(false); HandleReturnMovement(); } + void PetAI::UpdateAI(const uint32 diff) { Unit* owner = m_creature->GetCharmerOrOwner(); + if(m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; + // m_creature->getVictim() can't be used for check in case stop fighting, m_creature->getVictim() clear at Unit death etc. if( m_creature->getVictim() ) { @@ -81,11 +94,13 @@ void PetAI::UpdateAI(const uint32 diff) _stopAttack(); return; } + DoMeleeAttackIfReady(); } else if(owner && m_creature->GetCharmInfo()) //no victim { Unit *nextTarget = SelectNextTarget(); + if (nextTarget) AttackStart(nextTarget); else @@ -93,28 +108,35 @@ void PetAI::UpdateAI(const uint32 diff) } else if (owner && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); + if(!me->GetCharmInfo()) return; + bool inCombat = me->getVictim(); + // Autocast (casted only in combat or persistent spells in any state) if (m_creature->GetGlobalCooldown() == 0 && !m_creature->hasUnitState(UNIT_STAT_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; + for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->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 (!inCombat) { // 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 @@ -124,6 +146,7 @@ void PetAI::UpdateAI(const uint32 diff) 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) @@ -136,7 +159,9 @@ void PetAI::UpdateAI(const uint32 diff) if (IsNonCombatSpell(spellInfo)) continue; } + Spell *spell = new Spell(m_creature, spellInfo, false, 0); + // Fix to allow pets on STAY to autocast if (inCombat && _CanAttack(me->getVictim()) && spell->CanAutoCast(me->getVictim())) { @@ -149,9 +174,11 @@ void PetAI::UpdateAI(const uint32 diff) for(std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = ObjectAccessor::GetUnit(*m_creature,*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)); @@ -163,46 +190,60 @@ void PetAI::UpdateAI(const uint32 diff) 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( !m_creature->HasInArc(M_PI, target) ) { m_creature->SetInFront(target); if( target->GetTypeId() == TYPEID_PLAYER ) m_creature->SendUpdateToPlayer( (Player*)target ); + if(owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendUpdateToPlayer( (Player*)owner ); } + m_creature->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 = m_creature->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 = ((Player*)owner)->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(m_creature->GetGUID()); if(pGroup) //add group @@ -212,38 +253,47 @@ void PetAI::UpdateAllies() 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 @@ -255,28 +305,36 @@ void PetAI::AttackStart(Unit *target) 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()) @@ -285,6 +343,7 @@ void PetAI::HandleReturnMovement() if (!me->GetCharmInfo()->IsCommandAttack()) { float x,y,z; + me->GetCharmInfo()->GetStayPosition(x, y, z); me->GetCharmInfo()->SetIsReturning(true); me->GetMotionMaster()->Clear(); @@ -304,15 +363,20 @@ void PetAI::HandleReturnMovement() } } } + } + 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)) @@ -332,6 +396,7 @@ void PetAI::DoAttack(Unit *target, bool chase) me->Attack(target,true); } } + void PetAI::MovementInform(uint32 moveType, uint32 data) { // Receives notification when pet reaches stay or follow owner @@ -352,6 +417,7 @@ void PetAI::MovementInform(uint32 moveType, uint32 data) } } break; + case TARGETED_MOTION_TYPE: { // If data is owner's GUIDLow then we've reached follow point, @@ -366,30 +432,39 @@ void PetAI::MovementInform(uint32 moveType, uint32 data) } } 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; } |