All pastes #948086 Raw Edit

Zeug

public cpp v1 · immutable
·published 2008-03-18 21:50 UTC
rendered paste body
/* * 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 "TargetedMovementGenerator.h"#include "Database/DBCStores.h"#include "Spell.h"#include "ObjectAccessor.h"#include "SpellMgr.h"#include "Creature.h"#include "World.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.isPet() || i_pet.isCharmed()) && 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)) return;            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))    {        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();		//but then we have to clear the FOLLOW stat manually		i_pet.clearUnitState(UNIT_STAT_FOLLOW);         i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator<Creature>(*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()->Idle();        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()->Clear();        i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator<Creature>(*owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE));    }    else if (i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY))	{	        i_pet.GetMotionMaster()->Clear();        i_pet.GetMotionMaster()->Idle();		i_pet.GetMotionMaster()->TargetedHome();    }	else //should not happen	{		i_pet.addUnitState(UNIT_STAT_STOPPED);        i_pet.GetMotionMaster()->Clear();        i_pet.GetMotionMaster()->Idle();    }    	i_victimGuid = 0;    i_pet.AttackStop();	i_pet.InterruptNonMeleeSpells(false);}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) )        {            AttackStart(owner->getAttackerForHelper());        }        else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))        {            if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) )            {                i_pet.GetMotionMaster()->Clear();                i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator<Creature>(*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.GePetAutoSpellOnPos(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(spellID)))                    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.GetDistanceSq(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);}