2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "WorldPacket.h"
21 #include "WorldSession.h"
23 #include "ObjectMgr.h"
28 #include "ObjectAccessor.h"
29 #include "MapManager.h"
30 #include "CreatureAI.h"
35 void WorldSession::HandlePetAction( WorldPacket
& recv_data
)
37 CHECK_PACKET_SIZE(recv_data
, 8+2+2+8);
43 recv_data
>> guid1
; //pet guid
45 recv_data
>> flag
; //delete = 0x0700 CastSpell = C100
46 recv_data
>> guid2
; //tag guid
48 // used also for charmed creature
49 Unit
* pet
= ObjectAccessor::GetUnit(*_player
, guid1
);
50 sLog
.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1
)), flag
, spellid
, uint32(GUID_LOPART(guid2
)) );
53 sLog
.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1
)) );
57 if(pet
!= GetPlayer()->GetPet() && pet
!= GetPlayer()->GetCharm())
59 sLog
.outError("HandlePetAction.Pet %u isn't pet of player %s.\n", uint32(GUID_LOPART(guid1
)), GetPlayer()->GetName() );
66 if(pet
->GetTypeId() == TYPEID_PLAYER
&& !(flag
== ACT_COMMAND
&& spellid
== COMMAND_ATTACK
))
69 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
72 sLog
.outError("WorldSession::HandlePetAction: object "I64FMTD
" is considered pet-like but doesn't have a charminfo!", pet
->GetGUID());
78 case ACT_COMMAND
: //0x0700
81 case COMMAND_STAY
: //flat=1792 //STAY
83 pet
->GetMotionMaster()->Clear();
84 pet
->GetMotionMaster()->MoveIdle();
85 charmInfo
->SetCommandState( COMMAND_STAY
);
87 case COMMAND_FOLLOW
: //spellid=1792 //FOLLOW
89 pet
->GetMotionMaster()->MoveFollow(_player
,PET_FOLLOW_DIST
,PET_FOLLOW_ANGLE
);
90 charmInfo
->SetCommandState( COMMAND_FOLLOW
);
92 case COMMAND_ATTACK
: //spellid=1792 //ATTACK
94 const uint64
& selguid
= _player
->GetSelection();
95 Unit
*TargetUnit
= ObjectAccessor::GetUnit(*_player
, selguid
);
99 // not let attack friendly units.
100 if(GetPlayer()->IsFriendlyTo(TargetUnit
))
102 // Not let attack through obstructions
103 if(!pet
->IsWithinLOSInMap(TargetUnit
))
106 // This is true if pet has no target or has target but targets differs.
107 if(pet
->getVictim() != TargetUnit
)
109 if (pet
->getVictim())
112 if(pet
->GetTypeId() != TYPEID_PLAYER
)
114 pet
->GetMotionMaster()->Clear();
115 if (((Creature
*)pet
)->AI())
116 ((Creature
*)pet
)->AI()->AttackStart(TargetUnit
);
118 //10% chance to play special pet attack talk, else growl
119 if(((Creature
*)pet
)->isPet() && ((Pet
*)pet
)->getPetType() == SUMMON_PET
&& pet
!= TargetUnit
&& urand(0, 100) < 10)
120 pet
->SendPetTalk((uint32
)PET_TALK_ATTACK
);
123 // 90% chance for pet and 100% chance for charmed creature
124 pet
->SendPetAIReaction(guid1
);
127 else // charmed player
129 pet
->Attack(TargetUnit
,true);
130 pet
->SendPetAIReaction(guid1
);
135 case COMMAND_ABANDON
: // abandon (hunter pet) or dismiss (summoned pet)
136 if(((Creature
*)pet
)->isPet())
139 if(p
->getPetType() == HUNTER_PET
)
140 _player
->RemovePet(p
,PET_SAVE_AS_DELETED
);
142 //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
143 p
->setDeathState(CORPSE
);
149 sLog
.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag
, spellid
);
152 case ACT_REACTION
: // 0x600
155 case REACT_PASSIVE
: //passive
156 case REACT_DEFENSIVE
: //recovery
157 case REACT_AGGRESSIVE
: //activete
158 charmInfo
->SetReactState( ReactStates(spellid
) );
162 case ACT_DISABLED
: // 0x8100 spell (disabled), ignore
163 case ACT_PASSIVE
: // 0x0100
164 case ACT_ENABLED
: // 0xC100 spell
166 Unit
* unit_target
= NULL
;
167 if (((Creature
*)pet
)->GetGlobalCooldown() > 0)
171 unit_target
= ObjectAccessor::GetUnit(*_player
,guid2
);
173 // do not cast unknown spells
174 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellid
);
177 sLog
.outError("WORLD: unknown PET spell id %i\n", spellid
);
181 for(uint32 i
= 0; i
< 3;i
++)
183 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
)
187 // do not cast not learned spells
188 if(!pet
->HasSpell(spellid
) || IsPassiveSpell(spellid
))
191 pet
->clearUnitState(UNIT_STAT_FOLLOW
);
193 Spell
*spell
= new Spell(pet
, spellInfo
, false);
195 int16 result
= spell
->PetCanCast(unit_target
);
197 //auto turn to target unless possessed
198 if(result
== SPELL_FAILED_UNIT_NOT_INFRONT
&& !pet
->HasAuraType(SPELL_AURA_MOD_POSSESS
))
200 pet
->SetInFront(unit_target
);
201 if( unit_target
->GetTypeId() == TYPEID_PLAYER
)
202 pet
->SendUpdateToPlayer( (Player
*)unit_target
);
203 if(Unit
* powner
= pet
->GetCharmerOrOwner())
204 if(powner
->GetTypeId() == TYPEID_PLAYER
)
205 pet
->SendUpdateToPlayer((Player
*)powner
);
211 ((Creature
*)pet
)->AddCreatureSpellCooldown(spellid
);
212 if (((Creature
*)pet
)->isPet())
213 ((Pet
*)pet
)->CheckLearning(spellid
);
215 unit_target
= spell
->m_targets
.getUnitTarget();
217 //10% chance to play special pet attack talk, else growl
218 //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
219 if(((Creature
*)pet
)->isPet() && (((Pet
*)pet
)->getPetType() == SUMMON_PET
) && (pet
!= unit_target
) && (urand(0, 100) < 10))
220 pet
->SendPetTalk((uint32
)PET_TALK_SPECIAL_SPELL
);
223 pet
->SendPetAIReaction(guid1
);
226 if( unit_target
&& !GetPlayer()->IsFriendlyTo(unit_target
) && !pet
->HasAuraType(SPELL_AURA_MOD_POSSESS
))
228 // This is true if pet has no target or has target but targets differs.
229 if (pet
->getVictim() != unit_target
)
231 if (pet
->getVictim())
233 pet
->GetMotionMaster()->Clear();
234 if (((Creature
*)pet
)->AI())
235 ((Creature
*)pet
)->AI()->AttackStart(unit_target
);
239 spell
->prepare(&(spell
->m_targets
));
243 if(pet
->HasAuraType(SPELL_AURA_MOD_POSSESS
))
245 WorldPacket
data(SMSG_CAST_FAILED
, (4+1+1));
246 data
<< uint8(0) << uint32(spellid
) << uint8(result
);
249 case SPELL_FAILED_REQUIRES_SPELL_FOCUS
:
250 data
<< uint32(spellInfo
->RequiresSpellFocus
);
256 pet
->SendPetCastFail(spellid
, result
);
258 if(!((Creature
*)pet
)->HasSpellCooldown(spellid
))
259 pet
->SendPetClearCooldown(spellid
);
261 spell
->finish(false);
267 sLog
.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag
, spellid
);
271 void WorldSession::HandlePetNameQuery( WorldPacket
& recv_data
)
273 CHECK_PACKET_SIZE(recv_data
,4+8);
275 sLog
.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" );
280 recv_data
>> petnumber
;
281 recv_data
>> petguid
;
283 SendPetNameQuery(petguid
,petnumber
);
286 void WorldSession::SendPetNameQuery( uint64 petguid
, uint32 petnumber
)
288 Creature
* pet
= ObjectAccessor::GetCreatureOrPetOrVehicle(*_player
, petguid
);
289 if(!pet
|| !pet
->GetCharmInfo() || pet
->GetCharmInfo()->GetPetNumber() != petnumber
)
292 std::string name
= pet
->GetName();
294 WorldPacket
data(SMSG_PET_NAME_QUERY_RESPONSE
, (4+4+name
.size()+1));
295 data
<< uint32(petnumber
);
296 data
<< name
.c_str();
297 data
<< uint32(pet
->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
));
299 if( pet
->isPet() && ((Pet
*)pet
)->GetDeclinedNames() )
302 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
303 data
<< ((Pet
*)pet
)->GetDeclinedNames()->name
[i
];
308 _player
->GetSession()->SendPacket(&data
);
311 void WorldSession::HandlePetSetAction( WorldPacket
& recv_data
)
313 CHECK_PACKET_SIZE(recv_data
, 8+4+2+2);
315 sLog
.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" );
323 recv_data
>> petguid
;
325 // FIXME: charmed case
326 //Pet* pet = ObjectAccessor::Instance().GetPet(petguid);
327 if(ObjectAccessor::FindPlayer(petguid
))
330 Creature
* pet
= ObjectAccessor::GetCreatureOrPetOrVehicle(*_player
, petguid
);
332 if(!pet
|| (pet
!= _player
->GetPet() && pet
!= _player
->GetCharm()))
334 sLog
.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" );
338 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
341 sLog
.outError("WorldSession::HandlePetSetAction: object "I64FMTD
" is considered pet-like but doesn't have a charminfo!", pet
->GetGUID());
345 count
= (recv_data
.size() == 24) ? 2 : 1;
346 for(uint8 i
= 0; i
< count
; i
++)
348 recv_data
>> position
;
349 recv_data
>> spell_id
;
350 recv_data
>> act_state
;
352 sLog
.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player
->GetName(), position
, spell_id
, act_state
);
354 //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
355 if(!((act_state
== ACT_ENABLED
|| act_state
== ACT_DISABLED
|| act_state
== ACT_PASSIVE
) && spell_id
&& !pet
->HasSpell(spell_id
)))
358 if(act_state
== ACT_ENABLED
&& spell_id
)
361 charmInfo
->ToggleCreatureAutocast(spell_id
, true);
363 ((Pet
*)pet
)->ToggleAutocast(spell_id
, true);
365 //sign for no/turn off autocast
366 else if(act_state
== ACT_DISABLED
&& spell_id
)
369 charmInfo
->ToggleCreatureAutocast(spell_id
, false);
371 ((Pet
*)pet
)->ToggleAutocast(spell_id
, false);
374 charmInfo
->GetActionBarEntry(position
)->Type
= act_state
;
375 charmInfo
->GetActionBarEntry(position
)->SpellOrAction
= spell_id
;
380 void WorldSession::HandlePetRename( WorldPacket
& recv_data
)
382 CHECK_PACKET_SIZE(recv_data
, 8+1);
384 sLog
.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" );
390 DeclinedName declinedname
;
392 recv_data
>> petguid
;
394 CHECK_PACKET_SIZE(recv_data
, recv_data
.rpos() + 1);
395 recv_data
>> isdeclined
;
397 Pet
* pet
= ObjectAccessor::GetPet(petguid
);
399 if( !pet
|| !pet
->isPet() || ((Pet
*)pet
)->getPetType()!= HUNTER_PET
||
400 pet
->GetByteValue(UNIT_FIELD_BYTES_2
, 2) != UNIT_RENAME_ALLOWED
||
401 pet
->GetOwnerGUID() != _player
->GetGUID() || !pet
->GetCharmInfo() )
404 if(!ObjectMgr::IsValidPetName(name
))
406 SendPetNameInvalid(PET_NAME_INVALID
, name
, NULL
);
410 if(objmgr
.IsReservedName(name
))
412 SendPetNameInvalid(PET_NAME_RESERVED
, name
, NULL
);
418 Unit
*owner
= pet
->GetOwner();
419 if(owner
&& (owner
->GetTypeId() == TYPEID_PLAYER
) && ((Player
*)owner
)->GetGroup())
420 ((Player
*)owner
)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME
);
422 pet
->SetByteValue(UNIT_FIELD_BYTES_2
, 2, UNIT_RENAME_NOT_ALLOWED
);
426 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
428 CHECK_PACKET_SIZE(recv_data
, recv_data
.rpos() + 1);
429 recv_data
>> declinedname
.name
[i
];
433 Utf8toWStr(name
, wname
);
434 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname
,0),declinedname
))
436 SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME
, name
, &declinedname
);
441 CharacterDatabase
.BeginTransaction();
444 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
445 CharacterDatabase
.escape_string(declinedname
.name
[i
]);
446 CharacterDatabase
.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player
->GetGUIDLow(), pet
->GetCharmInfo()->GetPetNumber());
447 CharacterDatabase
.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')",
448 pet
->GetCharmInfo()->GetPetNumber(), _player
->GetGUIDLow(), declinedname
.name
[0].c_str(), declinedname
.name
[1].c_str(), declinedname
.name
[2].c_str(), declinedname
.name
[3].c_str(), declinedname
.name
[4].c_str());
451 CharacterDatabase
.escape_string(name
);
452 CharacterDatabase
.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name
.c_str(), _player
->GetGUIDLow(), pet
->GetCharmInfo()->GetPetNumber());
453 CharacterDatabase
.CommitTransaction();
455 pet
->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
, time(NULL
));
458 void WorldSession::HandlePetAbandon( WorldPacket
& recv_data
)
460 CHECK_PACKET_SIZE(recv_data
, 8);
463 recv_data
>> guid
; //pet guid
464 sLog
.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid
) );
467 Creature
* pet
= ObjectAccessor::GetCreatureOrPetOrVehicle(*_player
, guid
);
472 if(pet
->GetGUID() == _player
->GetPetGUID())
474 uint32 feelty
= pet
->GetPower(POWER_HAPPINESS
);
475 pet
->SetPower(POWER_HAPPINESS
,(feelty
-50000) > 0 ?(feelty
-50000) : 0);
478 _player
->RemovePet((Pet
*)pet
,PET_SAVE_AS_DELETED
);
480 else if(pet
->GetGUID() == _player
->GetCharmGUID())
487 void WorldSession::HandlePetUnlearnOpcode(WorldPacket
& recvPacket
)
489 CHECK_PACKET_SIZE(recvPacket
,8);
491 sLog
.outDetail("CMSG_PET_UNLEARN");
493 recvPacket
>> guid
; // Pet guid
495 Pet
* pet
= _player
->GetPet();
497 if(!pet
|| pet
->getPetType() != HUNTER_PET
|| pet
->m_usedTalentCount
== 0)
500 if(guid
!= pet
->GetGUID())
502 sLog
.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid
)),GetPlayer()->GetName() );
506 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
509 sLog
.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD
" is considered pet-like but doesn't have a charminfo!", pet
->GetGUID());
515 void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket
& recvPacket
)
517 CHECK_PACKET_SIZE(recvPacket
,8+2+2+1);
519 sLog
.outDetail("CMSG_PET_SPELL_AUTOCAST");
522 uint16 spellid2
; //maybe second spell, automatically toggled off when first toggled on?
523 uint8 state
; //1 for on, 0 for off
524 recvPacket
>> guid
>> spellid
>> spellid2
>> state
;
526 if(!_player
->GetPet() && !_player
->GetCharm())
529 if(ObjectAccessor::FindPlayer(guid
))
532 Creature
* pet
=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player
,guid
);
534 if(!pet
|| (pet
!= _player
->GetPet() && pet
!= _player
->GetCharm()))
536 sLog
.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid
)),GetPlayer()->GetName() );
540 // do not add not learned spells/ passive spells
541 if(!pet
->HasSpell(spellid
) || IsPassiveSpell(spellid
))
544 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
547 sLog
.outError("WorldSession::HandlePetSpellAutocastOpcod: object "I64FMTD
" is considered pet-like but doesn't have a charminfo!", pet
->GetGUID());
552 //state can be used as boolean
553 pet
->GetCharmInfo()->ToggleCreatureAutocast(spellid
, state
);
555 ((Pet
*)pet
)->ToggleAutocast(spellid
, state
);
557 for(uint8 i
= 0; i
< 10; ++i
)
559 if((charmInfo
->GetActionBarEntry(i
)->Type
== ACT_ENABLED
|| charmInfo
->GetActionBarEntry(i
)->Type
== ACT_DISABLED
) && spellid
== charmInfo
->GetActionBarEntry(i
)->SpellOrAction
)
560 charmInfo
->GetActionBarEntry(i
)->Type
= state
? ACT_ENABLED
: ACT_DISABLED
;
564 void WorldSession::HandlePetCastSpellOpcode( WorldPacket
& recvPacket
)
566 sLog
.outDetail("WORLD: CMSG_PET_CAST_SPELL");
568 CHECK_PACKET_SIZE(recvPacket
,8+1+4+1);
572 uint8 unk_flags
; // flags (if 0x02 - some additional data are received)
574 recvPacket
>> guid
>> cast_count
>> spellid
>> unk_flags
;
576 sLog
.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count
, spellid
, unk_flags
);
578 if(!_player
->GetPet() && !_player
->GetCharm())
581 if (GUID_HIPART(guid
) == HIGHGUID_PLAYER
)
584 Creature
* pet
= ObjectAccessor::GetCreatureOrPetOrVehicle(*_player
,guid
);
586 if(!pet
|| (pet
!= _player
->GetPet() && pet
!= _player
->GetCharm()))
588 sLog
.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid
)),GetPlayer()->GetName() );
592 if (pet
->GetGlobalCooldown() > 0)
595 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellid
);
598 sLog
.outError("WORLD: unknown PET spell id %i\n", spellid
);
602 // do not cast not learned spells
603 if(!pet
->HasSpell(spellid
) || IsPassiveSpell(spellid
))
606 SpellCastTargets targets
;
607 if(!targets
.read(&recvPacket
,pet
))
610 pet
->clearUnitState(UNIT_STAT_FOLLOW
);
612 Spell
*spell
= new Spell(pet
, spellInfo
, false);
613 spell
->m_cast_count
= cast_count
; // probably pending spell cast
614 spell
->m_targets
= targets
;
616 int16 result
= spell
->PetCanCast(NULL
);
619 pet
->AddCreatureSpellCooldown(spellid
);
623 p
->CheckLearning(spellid
);
624 //10% chance to play special pet attack talk, else growl
625 //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
626 if(p
->getPetType() == SUMMON_PET
&& (urand(0, 100) < 10))
627 pet
->SendPetTalk((uint32
)PET_TALK_SPECIAL_SPELL
);
629 pet
->SendPetAIReaction(guid
);
632 spell
->prepare(&(spell
->m_targets
));
636 pet
->SendPetCastFail(spellid
, result
);
637 if(!pet
->HasSpellCooldown(spellid
))
638 pet
->SendPetClearCooldown(spellid
);
640 spell
->finish(false);
645 void WorldSession::SendPetNameInvalid(uint32 error
, const std::string
& name
, DeclinedName
*declinedName
)
647 WorldPacket
data(SMSG_PET_NAME_INVALID
, 4 + name
.size() + 1 + 1);
648 data
<< uint32(error
);
653 for(uint32 i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
654 data
<< declinedName
->name
[i
];
661 void WorldSession::HandlePetLearnTalent( WorldPacket
& recv_data
)
663 sLog
.outDebug("WORLD: CMSG_PET_LEARN_TALENT");
666 CHECK_PACKET_SIZE(recv_data
, 8+4+4);
669 uint32 talent_id
, requested_rank
;
670 recv_data
>> guid
>> talent_id
>> requested_rank
;
672 Pet
*pet
= _player
->GetPet();
677 if(guid
!= pet
->GetGUID())
680 uint32 CurTalentPoints
= pet
->GetFreeTalentPoints();
682 if(CurTalentPoints
== 0)
685 if (requested_rank
> 4)
688 TalentEntry
const *talentInfo
= sTalentStore
.LookupEntry(talent_id
);
693 TalentTabEntry
const *talentTabInfo
= sTalentTabStore
.LookupEntry(talentInfo
->TalentTab
);
698 CreatureInfo
const *ci
= pet
->GetCreatureInfo();
703 CreatureFamilyEntry
const *pet_family
= sCreatureFamilyStore
.LookupEntry(ci
->family
);
708 if(pet_family
->petTalentType
< 0) // not hunter pet
711 // prevent learn talent for different family (cheating)
712 if(!((1 << pet_family
->petTalentType
) & talentTabInfo
->petTalentMask
))
715 // prevent skip talent ranks (cheating)
716 if(requested_rank
> 0 && !pet
->HasSpell(talentInfo
->RankID
[requested_rank
-1]))
719 // Check if it requires another talent
720 if (talentInfo
->DependsOn
> 0)
722 if(TalentEntry
const *depTalentInfo
= sTalentStore
.LookupEntry(talentInfo
->DependsOn
))
724 bool hasEnoughRank
= false;
725 for (int i
= talentInfo
->DependsOnRank
; i
<= 4; i
++)
727 if (depTalentInfo
->RankID
[i
] != 0)
728 if (pet
->HasSpell(depTalentInfo
->RankID
[i
]))
729 hasEnoughRank
= true;
736 // Find out how many points we have in this field
737 uint32 spentPoints
= 0;
739 uint32 tTab
= talentInfo
->TalentTab
;
740 if (talentInfo
->Row
> 0)
742 unsigned int numRows
= sTalentStore
.GetNumRows();
743 for (unsigned int i
= 0; i
< numRows
; i
++) // Loop through all talents.
745 // Someday, someone needs to revamp
746 const TalentEntry
*tmpTalent
= sTalentStore
.LookupEntry(i
);
747 if (tmpTalent
) // the way talents are tracked
749 if (tmpTalent
->TalentTab
== tTab
)
751 for (int j
= 0; j
<= 4; j
++)
753 if (tmpTalent
->RankID
[j
] != 0)
755 if (pet
->HasSpell(tmpTalent
->RankID
[j
]))
757 spentPoints
+= j
+ 1;
766 // not have required min points spent in talent tree
767 if(spentPoints
< (talentInfo
->Row
* 3))
770 // spell not set in talent.dbc
771 uint32 spellid
= talentInfo
->RankID
[requested_rank
];
774 sLog
.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talent_id
, requested_rank
);
779 if(pet
->HasSpell(spellid
))
782 // learn! (other talent ranks will unlearned at learning)
783 pet
->learnSpell(spellid
);
784 sLog
.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id
, requested_rank
, spellid
);