Exemplo n.º 1
0
void WorldSession::HandlePetAction(WorldPacket& recv_data)
{
    ObjectGuid petGuid;
    uint32 data;
    ObjectGuid targetGuid;
    recv_data >> petGuid;
    recv_data >> data;
    recv_data >> targetGuid;

    uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
    uint8 flag = UNIT_ACTION_BUTTON_TYPE(data);             // delete = 0x07 CastSpell = C1

    DETAIL_LOG("HandlePetAction: %s flag is %u, spellid is %u, target %s.", petGuid.GetString().c_str(), uint32(flag), spellid, targetGuid.GetString().c_str());

    // used also for charmed creature/player
    Unit* pet = _player->GetMap()->GetUnit(petGuid);
    if (!pet)
    {
        sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str());
        return;
    }

    if (GetPlayer()->GetObjectGuid() != pet->GetCharmerOrOwnerGuid())
    {
        sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str());
        return;
    }

    if (!pet->isAlive())
        return;

    if (pet->GetTypeId() == TYPEID_PLAYER)
    {
        // controller player can only do melee attack
        if (!(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
            return;
    }
    else if (((Creature*)pet)->IsPet())
    {
        // pet can have action bar disabled
        if (((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)
            return;
    }

    CharmInfo* charmInfo = pet->GetCharmInfo();
    if (!charmInfo)
    {
        sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
        return;
    }

    switch (flag)
    {
        case ACT_COMMAND:                                   // 0x07
            switch (spellid)
            {
                case COMMAND_STAY:                          // flat=1792  // STAY
                {
                    pet->StopMoving();
                    pet->AttackStop();
                    pet->GetMotionMaster()->Clear(false);
                    pet->GetMotionMaster()->MoveIdle();
                    ((Pet*)pet)->SetStayPosition();
                    ((Pet*)pet)->SetIsRetreating();
                    ((Pet*)pet)->SetSpellOpener();
                    charmInfo->SetCommandState(COMMAND_STAY);
                    break;
                }
                case COMMAND_FOLLOW:                        // spellid=1792  // FOLLOW
                {
                    ((Pet*)pet)->ClearStayPosition();
                    pet->AttackStop();
                    pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
                    ((Pet*)pet)->SetIsRetreating(true);
                    ((Pet*)pet)->SetSpellOpener();
                    charmInfo->SetCommandState(COMMAND_FOLLOW);
                    break;
                }
                case COMMAND_ATTACK:                        // spellid=1792  // ATTACK
                {
                    Unit* targetUnit = _player->GetMap()->GetUnit(targetGuid);
                    if (!targetUnit)
                        return;

                    // not let attack friendly units.
                    if (GetPlayer()->IsFriendlyTo(targetUnit))
                        return;

                    ((Pet*)pet)->SetIsRetreating();
                    ((Pet*)pet)->SetSpellOpener();

                    // This is true if pet has no target or has target but targets differs.
                    if (pet->getVictim() != targetUnit)
                        pet->AttackStop();

                    pet->GetMotionMaster()->Clear();

                    if (((Creature*)pet)->AI())
                    {
                        ((Creature*)pet)->AI()->AttackStart(targetUnit);
                        // 10% chance to play special warlock pet attack talk, else growl
                        if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != targetUnit && roll_chance_i(10))
                            pet->SendPetTalk((uint32)PET_TALK_ATTACK);

                        pet->SendPetAIReaction();
                    }
                    else
                        pet->Attack(targetUnit, true);

                    break;
                }
                case COMMAND_ABANDON:                       // abandon (hunter pet) or dismiss (summoned pet)
                {
                    Creature* petC = (Creature*)pet;
                    if (petC->IsPet())

                    {
                        Pet* p = (Pet*)petC;
                        if (p->getPetType() == HUNTER_PET)
                            p->Unsummon(PET_SAVE_AS_DELETED, _player);
                        else
                            // dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
                            p->SetDeathState(CORPSE);
                    }
                    else                                    // charmed
                        _player->Uncharm();

                    if (petC->IsTemporarySummon()) // special case when pet was temporary summon through DoSummonPossesed
                    {
                        petC->ForcedDespawn();
                        return;
                    }
                    ((Pet*)pet)->ClearStayPosition();

                    break;
                }
                default:
                    sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
            }
            break;
        case ACT_REACTION:                                  // 0x6
            switch (spellid)
            {
                case REACT_PASSIVE:                         // passive
                {
                    pet->AttackStop();
                    ((Pet*)pet)->SetSpellOpener();
                    if (!charmInfo->GetCommandState() == COMMAND_STAY)
                        pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
                }
                case REACT_DEFENSIVE:                       // recovery
                case REACT_AGGRESSIVE:                      // activete
                {
                    charmInfo->SetReactState(ReactStates(spellid));
                    break;
                }
            }
            break;
        case ACT_DISABLED:                                  // 0x81    spell (disabled), ignore
        case ACT_PASSIVE:                                   // 0x01
        case ACT_ENABLED:                                   // 0xC1    spell
        {
            Unit* unit_target = nullptr;
            if (targetGuid)
                unit_target = _player->GetMap()->GetUnit(targetGuid);

            // do not cast unknown spells
            SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid);
            if (!spellInfo)
            {
                sLog.outError("WORLD: unknown PET spell id %i", spellid);
                return;
            }

            if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
                return;

            for (int i = 0; i < MAX_EFFECT_INDEX; ++i)
            {
                if (spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA
                    || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT
                    || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED)
                    return;
            }

            // do not cast not learned spells
            if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo))
                return;

            pet->clearUnitState(UNIT_STAT_MOVING);

            Spell* spell = new Spell(pet, spellInfo, false);

            SpellCastResult result = spell->CheckPetCast(unit_target);

            const SpellRangeEntry* sRange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);

            if (unit_target && !(pet->IsWithinDistInMap(unit_target, sRange->maxRange) && pet->IsWithinLOSInMap(unit_target)) 
                && !(GetPlayer()->IsFriendlyTo(unit_target) || pet->HasAuraType(SPELL_AURA_MOD_POSSESS)))
            {
                ((Pet*)pet)->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange);
                spell->finish(false);
                delete spell;

                pet->AttackStop();
                pet->GetMotionMaster()->Clear();

                ((Creature*)pet)->AI()->AttackStart(unit_target);
                 // 10% chance to play special warlock pet attack talk, else growl
                if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10))
                    pet->SendPetTalk((uint32)PET_TALK_ATTACK);

                pet->SendPetAIReaction();

                return;
            }

            // auto turn to target unless possessed
            if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
            {
                if (unit_target)
                {
                    pet->SetInFront(unit_target);
                    if (unit_target->GetTypeId() == TYPEID_PLAYER)
                        pet->SendCreateUpdateToPlayer((Player*)unit_target);
                }
                else if (Unit* unit_target2 = spell->m_targets.getUnitTarget())
                {
                    pet->SetInFront(unit_target2);
                    if (unit_target2->GetTypeId() == TYPEID_PLAYER)
                        pet->SendCreateUpdateToPlayer((Player*)unit_target2);
                }
                if (Unit* powner = pet->GetCharmerOrOwner())
                    if (powner->GetTypeId() == TYPEID_PLAYER)
                        pet->SendCreateUpdateToPlayer((Player*)powner);
                result = SPELL_CAST_OK;
            }

            if (result == SPELL_CAST_OK)
            {
                ((Creature*)pet)->AddCreatureSpellCooldown(spellid);
                if (((Creature*)pet)->IsPet())
                    ((Pet*)pet)->CheckLearning(spellid);

                unit_target = spell->m_targets.getUnitTarget();

                if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
                {
                    // This is true if pet has no target or has target but targets differs.
                    if (pet->getVictim() != unit_target)
                    {
                        pet->AttackStop();
                        pet->GetMotionMaster()->Clear();

                        if (((Creature*)pet)->AI())
                        {
                            ((Creature*)pet)->AI()->AttackStart(unit_target);
                             // 10% chance to play special warlock pet attack talk, else growl
                            if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10))
                                pet->SendPetTalk((uint32)PET_TALK_ATTACK);

                            pet->SendPetAIReaction();
                        }

                        else
                            pet->Attack(unit_target, true);
                    }
                }

                ((Pet*)pet)->SetSpellOpener();
                spell->SpellStart(&(spell->m_targets));
            }
            else
            {
                if (pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
                    Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
                else
                {
                    Unit* owner = pet->GetCharmerOrOwner();
                    if (owner && owner->GetTypeId() == TYPEID_PLAYER)
                        Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true);
                }

                if (!((Creature*)pet)->HasSpellCooldown(spellid))
                    GetPlayer()->SendClearCooldown(spellid, pet);

                ((Pet*)pet)->SetSpellOpener();
                spell->finish(false);
                delete spell;
            }
            break;
        }
        default:
            sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
    }
}