diff options
author | Neo2003 <none@none> | 2008-10-02 16:23:55 -0500 |
---|---|---|
committer | Neo2003 <none@none> | 2008-10-02 16:23:55 -0500 |
commit | 9b1c0e006f20091f28f3f468cfcab1feb51286bd (patch) | |
tree | b5d1ba94a656e6679f8737f9ea6bed1239b73b14 /src/game/PetAI.cpp |
[svn] * Proper SVN structureinit
--HG--
branch : trunk
Diffstat (limited to 'src/game/PetAI.cpp')
-rw-r--r-- | src/game/PetAI.cpp | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp new file mode 100644 index 00000000000..c9aeaf66ec9 --- /dev/null +++ b/src/game/PetAI.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.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 "Database/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) : i_pet(c), i_victimGuid(0), i_tracker(TIME_INTERVAL_LOOK) +{ + m_AllySet.clear(); + UpdateAllies(); +} + +void PetAI::MoveInLineOfSight(Unit *u) +{ + if( !i_pet.getVictim() && i_pet.GetCharmInfo() && + i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE) && + u->isTargetableForAttack() && i_pet.IsHostileTo( u ) && + u->isInAccessablePlaceFor(&i_pet)) + { + float attackRadius = i_pet.GetAttackDistance(u); + if(i_pet.IsWithinDistInMap(u, attackRadius) && i_pet.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) + { + if(i_pet.IsWithinLOSInMap(u)) + { + AttackStart(u); + u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + } + } + } +} + +void PetAI::AttackStart(Unit *u) +{ + if( i_pet.getVictim() || !u || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET ) + return; + + if(i_pet.Attack(u,true)) + { + i_pet.clearUnitState(UNIT_STAT_FOLLOW); + // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function + // thus with the following clear the original TMG gets invalidated and crash, doh + // hope it doesn't start to leak memory without this :-/ + //i_pet->Clear(); + i_victimGuid = u->GetGUID(); + i_pet.GetMotionMaster()->MoveChase(u); + } +} + +void PetAI::EnterEvadeMode() +{ +} + +bool PetAI::IsVisible(Unit *pl) const +{ + return _isVisible(pl); +} + +bool PetAI::_needToStop() const +{ + if(!i_pet.getVictim() || !i_pet.isAlive()) + return true; + + // This is needed for charmed creatures, as once their target was reset other effects can trigger threat + if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer()) + return true; + + return !i_pet.getVictim()->isTargetableForAttack(); +} + +void PetAI::_stopAttack() +{ + if( !i_victimGuid ) + return; + + Unit* victim = ObjectAccessor::GetUnit(i_pet, i_victimGuid ); + + if ( !victim ) + return; + + assert(!i_pet.getVictim() || i_pet.getVictim() == victim); + + if( !i_pet.isAlive() ) + { + DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow()); + i_pet.StopMoving(); + i_pet.GetMotionMaster()->Clear(); + i_pet.GetMotionMaster()->MoveIdle(); + i_victimGuid = 0; + i_pet.CombatStop(); + i_pet.getHostilRefManager().deleteReferences(); + + return; + } + else if( !victim ) + { + DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_pet.GetGUIDLow()); + } + else if( !victim->isAlive() ) + { + DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_pet.GetGUIDLow()); + } + else if( victim->HasStealthAura() ) + { + DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_pet.GetGUIDLow()); + } + else if( victim->isInFlight() ) + { + DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_pet.GetGUIDLow()); + } + else + { + DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_pet.GetGUIDLow()); + } + + Unit* owner = i_pet.GetCharmerOrOwner(); + + if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + { + i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + } + else + { + i_pet.clearUnitState(UNIT_STAT_FOLLOW); + i_pet.GetMotionMaster()->Clear(); + i_pet.GetMotionMaster()->MoveIdle(); + } + i_victimGuid = 0; + i_pet.AttackStop(); +} + +void PetAI::UpdateAI(const uint32 diff) +{ + // update i_victimGuid if i_pet.getVictim() !=0 and changed + if(i_pet.getVictim()) + i_victimGuid = i_pet.getVictim()->GetGUID(); + + Unit* owner = i_pet.GetCharmerOrOwner(); + + if(m_updateAlliesTimer <= diff) + // UpdateAllies self set update timer + UpdateAllies(); + else + m_updateAlliesTimer -= diff; + + // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. + if( i_victimGuid ) + { + if( _needToStop() ) + { + DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow()); + _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now + return; + } + else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE)) + { + // required to be stopped cases + if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) ) + { + if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) + i_pet.InterruptNonMeleeSpells(false); + else + return; + } + // not required to be stopped case + else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) ) + { + i_pet.AttackerStateUpdate(i_pet.getVictim()); + + i_pet.resetAttackTimer(); + + if ( !i_pet.getVictim() ) + return; + + //if pet misses its target, it will also be the first in threat list + i_pet.getVictim()->AddThreat(&i_pet,0.0f); + + if( _needToStop() ) + _stopAttack(); + } + } + } + else if(owner && i_pet.GetCharmInfo()) + { + if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY))) + { + AttackStart(owner->getAttackerForHelper()); + } + else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + { + if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) + { + i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + } + } + } + + //Autocast + HM_NAMESPACE::hash_map<uint32, Unit*> targetMap; + targetMap.clear(); + SpellCastTargets NULLtargets; + + for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++) + { + uint32 spellID = i_pet.GetPetAutoSpellOnPos(i); + if (!spellID) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); + if (!spellInfo) + continue; + + Spell *spell = new Spell(&i_pet, spellInfo, false, 0); + + if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim())) + targetMap[spellID] = i_pet.getVictim(); + else + { + spell->m_targets = NULLtargets; + for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) + { + Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar); + + //only buff targets that are in combat, unless the spell can only be cast while out of combat + if(!Target || (!Target->isInCombat() && !IsNonCombatSpell(spellInfo))) + continue; + if(spell->CanAutoCast(Target)) + targetMap[spellID] = Target; + } + } + + delete spell; + } + + //found units to cast on to + if(!targetMap.empty()) + { + uint32 index = urand(1, targetMap.size()); + HM_NAMESPACE::hash_map<uint32, Unit*>::iterator itr; + uint32 i; + for(itr = targetMap.begin(), i = 1; i < index; ++itr, ++i); + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + + Spell *spell = new Spell(&i_pet, spellInfo, false); + + SpellCastTargets targets; + targets.setUnitTarget( itr->second ); + + if(!i_pet.HasInArc(M_PI, itr->second)) + { + i_pet.SetInFront(itr->second); + if( itr->second->GetTypeId() == TYPEID_PLAYER ) + i_pet.SendUpdateToPlayer( (Player*)itr->second ); + + if(owner && owner->GetTypeId() == TYPEID_PLAYER) + i_pet.SendUpdateToPlayer( (Player*)owner ); + } + + i_pet.AddCreatureSpellCooldown(itr->first); + if(i_pet.isPet()) + ((Pet*)&i_pet)->CheckLearning(itr->first); + + spell->prepare(&targets); + } + targetMap.clear(); +} + +bool PetAI::_isVisible(Unit *u) const +{ + //return false; //( ((Creature*)&i_pet)->GetDistanceSq(u) * 1.0<= sWorld.getConfig(CONFIG_SIGHT_GUARDER) && !u->m_stealth && u->isAlive()); + return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_GUARDER) + && u->isVisibleForOrDetect(&i_pet,true); +} + +void PetAI::UpdateAllies() +{ + Unit* owner = i_pet.GetCharmerOrOwner(); + Group *pGroup = NULL; + + m_updateAlliesTimer = 10000; //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(i_pet.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::AttackedBy(Unit *attacker) +{ + //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway + if(!i_pet.getVictim() && i_pet.GetCharmInfo() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) && + (!i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) || i_pet.canReachWithAttack(attacker))) + AttackStart(attacker); +} |