diff options
Diffstat (limited to 'src/game/PetHandler.cpp')
-rw-r--r-- | src/game/PetHandler.cpp | 262 |
1 files changed, 184 insertions, 78 deletions
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index e4269ddafd6..e5ffd0235e5 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -106,7 +106,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) } // only place where pet can be player pet->clearUnitState(UNIT_STAT_FOLLOW); - uint64 selguid = _player->GetSelection(); + const uint64& selguid = _player->GetSelection(); Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid); if(!TargetUnit) return; @@ -118,27 +118,35 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) //if(!pet->IsWithinLOSInMap(TargetUnit)) // return; - if(pet->GetTypeId() != TYPEID_PLAYER) + // This is true if pet has no target or has target but targets differs. + if(pet->getVictim() != TargetUnit) { - if (((Creature*)pet)->AI()) - ((Creature*)pet)->AI()->AttackStart(TargetUnit); + if (pet->getVictim()) + pet->AttackStop(); - //10% chance to play special pet attack talk, else growl - if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) - pet->SendPetTalk((uint32)PET_TALK_ATTACK); - else + if(pet->GetTypeId() != TYPEID_PLAYER) { - // 90% chance for pet and 100% chance for charmed creature - pet->SendPetAIReaction(guid1); + pet->GetMotionMaster()->Clear(); + if (((Creature*)pet)->AI()) + ((Creature*)pet)->AI()->AttackStart(TargetUnit); + + //10% chance to play special pet attack talk, else growl + if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) + pet->SendPetTalk((uint32)PET_TALK_ATTACK); + else + { + // 90% chance for pet and 100% chance for charmed creature + pet->SendPetAIReaction(guid1); + } } - } - else // charmed player - { - if(pet->getVictim() && pet->getVictim() != TargetUnit) - pet->AttackStop(); + else // charmed player + { + if(pet->getVictim() && pet->getVictim() != TargetUnit) + pet->AttackStop(); - pet->Attack(TargetUnit,true); - pet->SendPetAIReaction(guid1); + pet->Attack(TargetUnit,true); + pet->SendPetAIReaction(guid1); + } } break; } @@ -174,19 +182,17 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) break; } break; - case ACT_DISABLED: //0x8100 spell (disabled), ignore - case ACT_CAST: //0x0100 - case ACT_ENABLED: //0xc100 spell + case ACT_DISABLED: // 0x8100 spell (disabled), ignore + case ACT_PASSIVE: // 0x0100 + case ACT_ENABLED: // 0xC100 spell { - Unit* unit_target; - if(guid2) - unit_target = ObjectAccessor::GetUnit(*_player,guid2); - else - unit_target = NULL; - + Unit* unit_target = NULL; if (((Creature*)pet)->GetGlobalCooldown() > 0) return; + if(guid2) + unit_target = ObjectAccessor::GetUnit(*_player,guid2); + // do not cast unknown spells SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid ); if(!spellInfo) @@ -211,7 +217,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) int16 result = spell->PetCanCast(unit_target); - //auto turn to target unless possessed + //auto turn to target unless possessed if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed()) { pet->SetInFront(unit_target); @@ -242,12 +248,15 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed()) { - pet->clearUnitState(UNIT_STAT_FOLLOW); - if(pet->getVictim()) - pet->AttackStop(); - pet->GetMotionMaster()->Clear(); - if (((Creature*)pet)->AI()) - ((Creature*)pet)->AI()->AttackStart(unit_target); + // This is true if pet has no target or has target but targets differs. + if (pet->getVictim() != unit_target) + { + if (pet->getVictim()) + pet->AttackStop(); + pet->GetMotionMaster()->Clear(); + if (((Creature*)pet)->AI()) + ((Creature*)pet)->AI()->AttackStart(unit_target); + } } spell->prepare(&(spell->m_targets)); @@ -257,15 +266,12 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if(pet->isPossessed()) { WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint32(spellid) << uint8(2) << uint8(result); + data << uint8(0) << uint32(spellid) << uint8(result); switch (result) { case SPELL_FAILED_REQUIRES_SPELL_FOCUS: data << uint32(spellInfo->RequiresSpellFocus); break; - case SPELL_FAILED_REQUIRES_AREA: - data << uint32(spellInfo->AreaId); - break; } SendPacket(&data); } @@ -302,7 +308,7 @@ void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) { - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid); if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) return; @@ -344,7 +350,7 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) if(ObjectAccessor::FindPlayer(petguid)) return; - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid); if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) { @@ -369,7 +375,7 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state); //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add - if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_CAST) && spell_id && !pet->HasSpell(spell_id))) + if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id))) { //sign for autocast if(act_state == ACT_ENABLED && spell_id) @@ -481,7 +487,7 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) ); // pet/charmed - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, guid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if(pet) { if(pet->isPet()) @@ -510,11 +516,11 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outDetail("CMSG_PET_UNLEARN"); uint64 guid; - recvPacket >> guid; + recvPacket >> guid; // Pet guid Pet* pet = _player->GetPet(); - if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) + if(!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0) return; if(guid != pet->GetGUID()) @@ -529,38 +535,7 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); return; } - - uint32 cost = pet->resetTalentsCost(); - - if (GetPlayer()->GetMoney() < cost) - { - GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); - return; - } - - for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();) - { - uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell - ++itr; - pet->removeSpell(spell_id); - } - - pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1)); - - for(uint8 i = 0; i < 10; i++) - { - if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) - charmInfo->GetActionBarEntry(i)->SpellOrAction = 0; - } - - // relearn pet passives - pet->LearnPetPassives(); - - pet->m_resetTalentsTime = time(NULL); - pet->m_resetTalentsCost = cost; - GetPlayer()->ModifyMoney(-(int32)cost); - - GetPlayer()->PetSpellInitialize(); + pet->resetTalents(); } void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) @@ -580,7 +555,7 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) if(ObjectAccessor::FindPlayer(guid)) return; - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) { @@ -616,11 +591,15 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); - CHECK_PACKET_SIZE(recvPacket,8+4); + CHECK_PACKET_SIZE(recvPacket,8+1+4+1); uint64 guid; uint32 spellid; + uint8 cast_count; + uint8 unk_flags; // flags (if 0x02 - some additional data are received) + + recvPacket >> guid >> cast_count >> spellid >> unk_flags; - recvPacket >> guid >> spellid; + sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags); // This opcode is also sent from charmed and possessed units (players and creatures) if(!_player->GetPet() && !_player->GetCharm()) @@ -655,6 +634,7 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) caster->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(caster, spellInfo, false); + spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; int16 result = spell->PetCanCast(NULL); @@ -713,3 +693,129 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec data << uint8(0); SendPacket(&data); } + +void WorldSession::HandlePetLearnTalent( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_PET_LEARN_TALENT"); + recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 8+4+4); + + uint64 guid; + uint32 talent_id, requested_rank; + recv_data >> guid >> talent_id >> requested_rank; + + Pet *pet = _player->GetPet(); + + if(!pet) + return; + + if(guid != pet->GetGUID()) + return; + + uint32 CurTalentPoints = pet->GetFreeTalentPoints(); + + if(CurTalentPoints == 0) + return; + + if (requested_rank > 4) + return; + + TalentEntry const *talentInfo = sTalentStore.LookupEntry(talent_id); + + if(!talentInfo) + return; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); + + if(!talentTabInfo) + return; + + CreatureInfo const *ci = pet->GetCreatureInfo(); + + if(!ci) + return; + + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family); + + if(!pet_family) + return; + + if(pet_family->petTalentType < 0) // not hunter pet + return; + + // prevent learn talent for different family (cheating) + if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) + return; + + // prevent skip talent ranks (cheating) + if(requested_rank > 0 && !pet->HasSpell(talentInfo->RankID[requested_rank-1])) + return; + + // Check if it requires another talent + if (talentInfo->DependsOn > 0) + { + if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) + { + bool hasEnoughRank = false; + for (int i = talentInfo->DependsOnRank; i <= 4; i++) + { + if (depTalentInfo->RankID[i] != 0) + if (pet->HasSpell(depTalentInfo->RankID[i])) + hasEnoughRank = true; + } + if (!hasEnoughRank) + return; + } + } + + // Find out how many points we have in this field + uint32 spentPoints = 0; + + uint32 tTab = talentInfo->TalentTab; + if (talentInfo->Row > 0) + { + unsigned int numRows = sTalentStore.GetNumRows(); + for (unsigned int i = 0; i < numRows; i++) // Loop through all talents. + { + // Someday, someone needs to revamp + const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i); + if (tmpTalent) // the way talents are tracked + { + if (tmpTalent->TalentTab == tTab) + { + for (int j = 0; j <= 4; j++) + { + if (tmpTalent->RankID[j] != 0) + { + if (pet->HasSpell(tmpTalent->RankID[j])) + { + spentPoints += j + 1; + } + } + } + } + } + } + } + + // not have required min points spent in talent tree + if(spentPoints < (talentInfo->Row * 3)) + return; + + // spell not set in talent.dbc + uint32 spellid = talentInfo->RankID[requested_rank]; + if( spellid == 0 ) + { + sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talent_id, requested_rank); + return; + } + + // already known + if(pet->HasSpell(spellid)) + return; + + // learn! (other talent ranks will unlearned at learning) + pet->learnSpell(spellid); + sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id, requested_rank, spellid); +} |