aboutsummaryrefslogtreecommitdiff
path: root/src/game/PetHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/PetHandler.cpp')
-rw-r--r--src/game/PetHandler.cpp262
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);
+}