Core/Pets: Implemented pet specializations (#17058)
* Use prepared statements in Pet::SavePetToDB * Add support for resetting all of a player's pet specializations * Send one big spell unlearn/learn packet instead of lots of small ones * Implemented Adaptation talent
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
ALTER TABLE `character_pet` ADD `specialization` smallint(5) unsigned NOT NULL DEFAULT '0' AFTER `abdata`;
|
||||
@@ -649,10 +649,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);
|
||||
PrepareStatement(CHAR_INS_PET_AURA_EFFECT, "INSERT INTO pet_aura_effect (guid, casterGuid, spell, effectMask, effectIndex, amount, baseAmount) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC);
|
||||
@@ -660,6 +660,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_ALL_PET_SPELLS_BY_OWNER, "DELETE FROM `pet_spell` WHERE `guid` in (SELECT `id` FROM `character_pet` WHERE owner=?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_PET_SPECS_BY_OWNER, "UPDATE character_pet SET specialization = 0 WHERE owner=?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PET, "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
|
||||
// PvPstats
|
||||
PrepareStatement(CHAR_SEL_PVPSTATS_MAXID, "SELECT MAX(id) FROM pvpstats_battlegrounds", CONNECTION_SYNCH);
|
||||
|
||||
@@ -559,6 +559,9 @@ enum CharacterDatabaseStatements
|
||||
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
|
||||
CHAR_DEL_CHAR_PET_BY_ID,
|
||||
CHAR_DEL_CHAR_PET_BY_SLOT,
|
||||
CHAR_DEL_ALL_PET_SPELLS_BY_OWNER,
|
||||
CHAR_UPD_PET_SPECS_BY_OWNER,
|
||||
CHAR_INS_PET,
|
||||
|
||||
CHAR_SEL_ITEMCONTAINER_ITEMS,
|
||||
CHAR_DEL_ITEMCONTAINER_ITEMS,
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
#define PET_XP_FACTOR 0.05f
|
||||
|
||||
Pet::Pet(Player* owner, PetType type) :
|
||||
Guardian(NULL, owner, true), m_usedTalentCount(0), m_removed(false),
|
||||
Guardian(NULL, owner, true), m_removed(false),
|
||||
m_petType(type), m_duration(0), m_loading(false), m_groupUpdateMask(0),
|
||||
m_declinedname(NULL)
|
||||
m_declinedname(NULL), m_petSpecialization(0)
|
||||
{
|
||||
ASSERT(GetOwner());
|
||||
|
||||
@@ -312,8 +312,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
|
||||
owner->SetMinion(this, true);
|
||||
map->AddToMap(this->ToCreature());
|
||||
|
||||
InitTalentForLevel(); // set original talents points before spell loading
|
||||
|
||||
uint32 timediff = uint32(time(NULL) - fields[13].GetUInt32());
|
||||
_LoadAuras(timediff);
|
||||
|
||||
@@ -323,24 +321,29 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
|
||||
m_charmInfo->LoadPetActionBar(fields[12].GetString());
|
||||
|
||||
_LoadSpells();
|
||||
InitTalentForLevel(); // re-init to check talent count
|
||||
_LoadSpellCooldowns();
|
||||
LearnPetPassives();
|
||||
InitLevelupSpellsForLevel();
|
||||
CastPetAuras(current);
|
||||
}
|
||||
|
||||
CleanupActionBar(); // remove unknown spells from action bar after load
|
||||
|
||||
TC_LOG_DEBUG("entities.pet", "New Pet has %s", GetGUID().ToString().c_str());
|
||||
|
||||
owner->PetSpellInitialize();
|
||||
uint16 specId = fields[16].GetUInt16();
|
||||
if (ChrSpecializationEntry const* petSpec = sChrSpecializationStore.LookupEntry(specId))
|
||||
specId = sChrSpecializationByIndexStore[owner->HasAuraType(SPELL_AURA_OVERRIDE_PET_SPECS) ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0][petSpec->OrderIndex]->ID;
|
||||
|
||||
SetSpecialization(specId);
|
||||
|
||||
// The SetSpecialization function will run these functions if the pet's spec is not 0
|
||||
if (!GetSpecialization())
|
||||
{
|
||||
CleanupActionBar(); // remove unknown spells from action bar after load
|
||||
owner->PetSpellInitialize();
|
||||
}
|
||||
|
||||
SetGroupUpdateFlag(GROUP_UPDATE_PET_FULL);
|
||||
|
||||
// TODO: 6.x remove/update pet talents
|
||||
//owner->SendTalentsInfoData(true);
|
||||
|
||||
if (getPetType() == HUNTER_PET)
|
||||
{
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME);
|
||||
@@ -415,8 +418,6 @@ void Pet::SavePetToDB(PetSaveMode mode)
|
||||
if (mode >= PET_SAVE_AS_CURRENT)
|
||||
{
|
||||
ObjectGuid::LowType ownerLowGUID = GetOwnerGUID().GetCounter();
|
||||
std::string name = m_name;
|
||||
CharacterDatabase.EscapeString(name);
|
||||
trans = CharacterDatabase.BeginTransaction();
|
||||
// remove current data
|
||||
|
||||
@@ -445,34 +446,28 @@ void Pet::SavePetToDB(PetSaveMode mode)
|
||||
}
|
||||
|
||||
// save pet
|
||||
std::ostringstream ss;
|
||||
ss << "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType) "
|
||||
<< "VALUES ("
|
||||
<< m_charmInfo->GetPetNumber() << ','
|
||||
<< GetEntry() << ','
|
||||
<< ownerLowGUID << ','
|
||||
<< GetNativeDisplayId() << ','
|
||||
<< uint32(getLevel()) << ','
|
||||
<< GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ','
|
||||
<< uint32(GetReactState()) << ','
|
||||
<< uint32(mode) << ", '"
|
||||
<< name.c_str() << "', "
|
||||
<< uint32(HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1) << ','
|
||||
<< curhealth << ','
|
||||
<< curmana << ", '";
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET);
|
||||
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
|
||||
stmt->setUInt32(1, GetEntry());
|
||||
stmt->setUInt64(2, ownerLowGUID);
|
||||
stmt->setUInt32(3, GetNativeDisplayId());
|
||||
stmt->setUInt8(4, getLevel());
|
||||
stmt->setUInt32(5, GetUInt32Value(UNIT_FIELD_PETEXPERIENCE));
|
||||
stmt->setUInt8(6, GetReactState());
|
||||
stmt->setInt16(7, mode);
|
||||
stmt->setString(8, m_name);
|
||||
stmt->setUInt8(9, HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1);
|
||||
stmt->setUInt32(10, curhealth);
|
||||
stmt->setUInt32(11, curmana);
|
||||
|
||||
for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
|
||||
{
|
||||
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' '
|
||||
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' ';
|
||||
};
|
||||
stmt->setString(12, GenerateActionBarData());
|
||||
|
||||
ss << "', "
|
||||
<< time(NULL) << ','
|
||||
<< GetUInt32Value(UNIT_CREATED_BY_SPELL) << ','
|
||||
<< uint32(getPetType()) << ')';
|
||||
stmt->setUInt32(13, time(NULL));
|
||||
stmt->setUInt32(14, GetUInt32Value(UNIT_CREATED_BY_SPELL));
|
||||
stmt->setUInt8(15, getPetType());
|
||||
stmt->setUInt16(16, m_petSpecialization);
|
||||
trans->Append(stmt);
|
||||
|
||||
trans->Append(ss.str().c_str());
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
// delete
|
||||
@@ -724,7 +719,6 @@ void Pet::GivePetLevel(uint8 level)
|
||||
|
||||
InitStatsForLevel(level);
|
||||
InitLevelupSpellsForLevel();
|
||||
InitTalentForLevel();
|
||||
}
|
||||
|
||||
bool Pet::CreateBaseAtCreature(Creature* creature)
|
||||
@@ -1436,6 +1430,22 @@ bool Pet::learnSpell(uint32 spell_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Pet::learnSpells(std::vector<uint32> const& spellIds)
|
||||
{
|
||||
WorldPackets::Pet::PetLearnedSpells packet;
|
||||
|
||||
for (uint32 spell : spellIds)
|
||||
{
|
||||
if (!addSpell(spell))
|
||||
continue;
|
||||
|
||||
packet.Spells.push_back(spell);
|
||||
}
|
||||
|
||||
if (!m_loading)
|
||||
GetOwner()->GetSession()->SendPacket(packet.Write());
|
||||
}
|
||||
|
||||
void Pet::InitLevelupSpellsForLevel()
|
||||
{
|
||||
uint8 level = getLevel();
|
||||
@@ -1488,6 +1498,22 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Pet::unlearnSpells(std::vector<uint32> const& spellIds, bool learn_prev, bool clear_ab)
|
||||
{
|
||||
WorldPackets::Pet::PetUnlearnedSpells packet;
|
||||
|
||||
for (uint32 spell : spellIds)
|
||||
{
|
||||
if (!removeSpell(spell, learn_prev, clear_ab))
|
||||
continue;
|
||||
|
||||
packet.Spells.push_back(spell);
|
||||
}
|
||||
|
||||
if (!m_loading)
|
||||
GetOwner()->GetSession()->SendPacket(packet.Write());
|
||||
}
|
||||
|
||||
bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
|
||||
{
|
||||
PetSpellMap::iterator itr = m_spells.find(spell_id);
|
||||
@@ -1513,7 +1539,7 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
|
||||
}
|
||||
|
||||
// if remove last rank or non-ranked then update action bar at server and client if need
|
||||
if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id))
|
||||
if (m_charmInfo->RemoveSpellFromActionBar(spell_id) && !learn_prev && clear_ab)
|
||||
{
|
||||
if (!m_loading)
|
||||
GetOwner()->PetSpellInitialize(); // need update action bar for last removed rank
|
||||
@@ -1549,182 +1575,6 @@ void Pet::InitPetCreateSpells()
|
||||
CastPetAuras(false);
|
||||
}
|
||||
|
||||
bool Pet::resetTalents()
|
||||
{
|
||||
/* TODO: 6.x remove pet talents
|
||||
Player* player = GetOwner();
|
||||
|
||||
// not need after this call
|
||||
if (player->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
|
||||
player->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS, true);
|
||||
|
||||
CreatureTemplate const* ci = GetCreatureTemplate();
|
||||
if (!ci)
|
||||
return false;
|
||||
// Check pet talent type
|
||||
CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
|
||||
if (!pet_family || pet_family->PetTalentType < 0)
|
||||
return false;
|
||||
|
||||
uint8 level = getLevel();
|
||||
uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level);
|
||||
|
||||
if (m_usedTalentCount == 0)
|
||||
{
|
||||
SetFreeTalentPoints(talentPointsForLevel);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
|
||||
{
|
||||
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
|
||||
|
||||
if (!talentInfo)
|
||||
continue;
|
||||
|
||||
TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
|
||||
|
||||
if (!talentTabInfo)
|
||||
continue;
|
||||
|
||||
// unlearn only talents for pets family talent type
|
||||
if (!((1 << pet_family->PetTalentType) & talentTabInfo->petTalentMask))
|
||||
continue;
|
||||
|
||||
for (uint8 j = 0; j < MAX_TALENT_RANK; ++j)
|
||||
{
|
||||
for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end();)
|
||||
{
|
||||
if (itr->second.state == PETSPELL_REMOVED)
|
||||
{
|
||||
++itr;
|
||||
continue;
|
||||
}
|
||||
// remove learned spells (all ranks)
|
||||
uint32 itrFirstId = sSpellMgr->GetFirstSpellInChain(itr->first);
|
||||
|
||||
// unlearn if first rank is talent or learned by talent
|
||||
if (itrFirstId == talentInfo->RankID[j] || sSpellMgr->IsSpellLearnToSpell(talentInfo->RankID[j], itrFirstId))
|
||||
{
|
||||
unlearnSpell(itr->first, false);
|
||||
itr = m_spells.begin();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetFreeTalentPoints(talentPointsForLevel);
|
||||
|
||||
if (!m_loading)
|
||||
player->PetSpellInitialize();*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void Pet::resetTalentsForAllPetsOf(Player* /*owner*/, Pet* /*onlinePet*/ /*= NULL*/)
|
||||
{
|
||||
/* TODO: 6.x remove pet talents
|
||||
// not need after this call
|
||||
if (owner->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
|
||||
owner->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS, true);
|
||||
|
||||
// reset for online
|
||||
if (onlinePet)
|
||||
onlinePet->resetTalents();
|
||||
|
||||
// now need only reset for offline pets (all pets except online case)
|
||||
uint32 exceptPetNumber = onlinePet ? onlinePet->GetCharmInfo()->GetPetNumber() : 0;
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET);
|
||||
stmt->setUInt64(0, owner->GetGUID().GetCounter());
|
||||
stmt->setUInt32(1, exceptPetNumber);
|
||||
PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
|
||||
|
||||
// no offline pets
|
||||
if (!resultPets)
|
||||
return;
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_LIST);
|
||||
stmt->setUInt64(0, owner->GetGUID().GetCounter());
|
||||
stmt->setUInt32(1, exceptPetNumber);
|
||||
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
bool need_comma = false;
|
||||
std::ostringstream ss;
|
||||
ss << "DELETE FROM pet_spell WHERE guid IN (";
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = resultPets->Fetch();
|
||||
|
||||
uint32 id = fields[0].GetUInt32();
|
||||
|
||||
if (need_comma)
|
||||
ss << ',';
|
||||
|
||||
ss << id;
|
||||
|
||||
need_comma = true;
|
||||
} while (resultPets->NextRow());
|
||||
|
||||
ss << ") AND spell IN (";
|
||||
|
||||
bool need_execute = false;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 spell = fields[0].GetUInt32();
|
||||
|
||||
if (!GetTalentSpellCost(spell))
|
||||
continue;
|
||||
|
||||
if (need_execute)
|
||||
ss << ',';
|
||||
|
||||
ss << spell;
|
||||
|
||||
need_execute = true;
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
if (!need_execute)
|
||||
return;
|
||||
|
||||
ss << ')';
|
||||
|
||||
CharacterDatabase.Execute(ss.str().c_str());*/
|
||||
}
|
||||
|
||||
void Pet::InitTalentForLevel()
|
||||
{
|
||||
/* TODO: 6.x remove/update pet talents
|
||||
uint8 level = getLevel();
|
||||
uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level);
|
||||
// Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent)
|
||||
if (talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel)
|
||||
resetTalents(); // Remove all talent points
|
||||
|
||||
SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
|
||||
|
||||
if (!m_loading)
|
||||
GetOwner()->SendTalentsInfoData(true);
|
||||
*/
|
||||
}
|
||||
|
||||
uint8 Pet::GetMaxTalentPointsForLevel(uint8 level) const
|
||||
{
|
||||
uint8 points = (level >= 20) ? ((level - 16) / 4) : 0;
|
||||
// Mod points from owner SPELL_AURA_MOD_PET_TALENT_POINTS
|
||||
points += GetOwner()->GetTotalAuraModifier(SPELL_AURA_MOD_PET_TALENT_POINTS);
|
||||
return points;
|
||||
}
|
||||
|
||||
void Pet::ToggleAutocast(SpellInfo const* spellInfo, bool apply)
|
||||
{
|
||||
ASSERT(spellInfo);
|
||||
@@ -1940,3 +1790,96 @@ void Pet::ResetGroupUpdateFlag()
|
||||
if (GetOwner()->GetGroup())
|
||||
GetOwner()->RemoveGroupUpdateFlag(GROUP_UPDATE_FLAG_PET);
|
||||
}
|
||||
|
||||
void Pet::LearnSpecializationSpells()
|
||||
{
|
||||
std::vector<uint32> learnedSpells;
|
||||
|
||||
if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(m_petSpecialization))
|
||||
{
|
||||
for (size_t j = 0; j < specSpells->size(); ++j)
|
||||
{
|
||||
SpecializationSpellsEntry const* specSpell = specSpells->at(j);
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID);
|
||||
if (!spellInfo || spellInfo->SpellLevel > getLevel())
|
||||
continue;
|
||||
|
||||
learnedSpells.push_back(specSpell->SpellID);
|
||||
}
|
||||
}
|
||||
|
||||
learnSpells(learnedSpells);
|
||||
}
|
||||
|
||||
void Pet::RemoveSpecializationSpells(bool clearActionBar)
|
||||
{
|
||||
std::vector<uint32> unlearnedSpells;
|
||||
|
||||
for (uint32 i = 0; i < MAX_SPECIALIZATIONS; ++i)
|
||||
{
|
||||
if (ChrSpecializationEntry const* specialization = sChrSpecializationByIndexStore[0][i])
|
||||
{
|
||||
if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(specialization->ID))
|
||||
{
|
||||
for (size_t j = 0; j < specSpells->size(); ++j)
|
||||
{
|
||||
SpecializationSpellsEntry const* specSpell = specSpells->at(j);
|
||||
unlearnedSpells.push_back(specSpell->SpellID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ChrSpecializationEntry const* specialization = sChrSpecializationByIndexStore[PET_SPEC_OVERRIDE_CLASS_INDEX][i])
|
||||
{
|
||||
if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(specialization->ID))
|
||||
{
|
||||
for (size_t j = 0; j < specSpells->size(); ++j)
|
||||
{
|
||||
SpecializationSpellsEntry const* specSpell = specSpells->at(j);
|
||||
unlearnedSpells.push_back(specSpell->SpellID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlearnSpells(unlearnedSpells, true, clearActionBar);
|
||||
}
|
||||
|
||||
void Pet::SetSpecialization(uint16 spec)
|
||||
{
|
||||
if (m_petSpecialization == spec)
|
||||
return;
|
||||
|
||||
// remove all the old spec's specalization spells, set the new spec, then add the new spec's spells
|
||||
// clearActionBars is false because we'll be updating the pet actionbar later so we don't have to do it now
|
||||
RemoveSpecializationSpells(false);
|
||||
if (!sChrSpecializationStore.LookupEntry(spec))
|
||||
{
|
||||
m_petSpecialization = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
m_petSpecialization = spec;
|
||||
LearnSpecializationSpells();
|
||||
|
||||
// resend SMSG_PET_SPELLS_MESSAGE to remove old specialization spells from the pet action bar
|
||||
CleanupActionBar();
|
||||
GetOwner()->PetSpellInitialize();
|
||||
|
||||
WorldPackets::Pet::SetPetSpecialization setPetSpecialization;
|
||||
setPetSpecialization.SpecID = m_petSpecialization;
|
||||
GetOwner()->GetSession()->SendPacket(setPetSpecialization.Write());
|
||||
}
|
||||
|
||||
std::string Pet::GenerateActionBarData() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
|
||||
{
|
||||
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' '
|
||||
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' ';
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -119,26 +119,24 @@ class TC_GAME_API Pet : public Guardian
|
||||
|
||||
bool addSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL);
|
||||
bool learnSpell(uint32 spell_id);
|
||||
void learnSpells(std::vector<uint32> const& spellIds);
|
||||
void learnSpellHighRank(uint32 spellid);
|
||||
void InitLevelupSpellsForLevel();
|
||||
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
void unlearnSpells(std::vector<uint32> const& spellIds, bool learn_prev, bool clear_ab = true);
|
||||
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
void CleanupActionBar();
|
||||
std::string GenerateActionBarData() const;
|
||||
|
||||
PetSpellMap m_spells;
|
||||
AutoSpellList m_autospells;
|
||||
|
||||
void InitPetCreateSpells();
|
||||
|
||||
bool resetTalents();
|
||||
static void resetTalentsForAllPetsOf(Player* owner, Pet* online_pet = nullptr);
|
||||
void InitTalentForLevel();
|
||||
|
||||
uint8 GetMaxTalentPointsForLevel(uint8 level) const;
|
||||
uint8 GetFreeTalentPoints() const { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
|
||||
void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); }
|
||||
|
||||
uint32 m_usedTalentCount;
|
||||
uint16 GetSpecialization() { return m_petSpecialization; }
|
||||
void SetSpecialization(uint16 spec);
|
||||
void LearnSpecializationSpells();
|
||||
void RemoveSpecializationSpells(bool clearActionBar);
|
||||
|
||||
uint32 GetGroupUpdateFlag() const { return m_groupUpdateMask; }
|
||||
void SetGroupUpdateFlag(uint32 flag);
|
||||
@@ -159,6 +157,8 @@ class TC_GAME_API Pet : public Guardian
|
||||
|
||||
DeclinedName *m_declinedname;
|
||||
|
||||
uint16 m_petSpecialization;
|
||||
|
||||
private:
|
||||
void SaveToDB(uint32, uint32, uint32) override // override of Creature::SaveToDB - must not be called
|
||||
{
|
||||
|
||||
@@ -20162,7 +20162,7 @@ void Player::PetSpellInitialize()
|
||||
WorldPackets::Pet::PetSpells petSpellsPacket;
|
||||
petSpellsPacket.PetGUID = pet->GetGUID();
|
||||
petSpellsPacket._CreatureFamily = pet->GetCreatureTemplate()->family; // creature family (required for pet talents)
|
||||
//petSpellsPacket.Specialization = pet->GetSpecialization(); NYI
|
||||
petSpellsPacket.Specialization = pet->GetSpecialization();
|
||||
petSpellsPacket.TimeLimit = pet->GetDuration();
|
||||
petSpellsPacket.ReactState = pet->GetReactState();
|
||||
petSpellsPacket.CommandState = charmInfo->GetCommandState();
|
||||
@@ -25954,7 +25954,6 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
|
||||
{
|
||||
case SUMMON_PET:
|
||||
pet->InitPetCreateSpells();
|
||||
pet->InitTalentForLevel();
|
||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||
PetSpellInitialize();
|
||||
break;
|
||||
|
||||
@@ -1077,7 +1077,17 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
|
||||
|
||||
// reset for all pets before pet loading
|
||||
if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
|
||||
Pet::resetTalentsForAllPetsOf(pCurrChar);
|
||||
{
|
||||
// Delete all of the player's pet spells
|
||||
PreparedStatement* stmtSpells = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PET_SPELLS_BY_OWNER);
|
||||
stmtSpells->setUInt64(0, pCurrChar->GetGUID().GetCounter());
|
||||
CharacterDatabase.Execute(stmtSpells);
|
||||
|
||||
// Then reset all of the player's pet specualizations
|
||||
PreparedStatement* stmtSpec = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PET_SPELLS_BY_OWNER);
|
||||
stmtSpec->setUInt64(0, pCurrChar->GetGUID().GetCounter());
|
||||
CharacterDatabase.Execute(stmtSpec);
|
||||
}
|
||||
|
||||
// Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
|
||||
pCurrChar->LoadPet();
|
||||
|
||||
@@ -722,3 +722,37 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec
|
||||
|
||||
SendPacket(petNameInvalid.Write());
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetSetSpecializationOpcode(WorldPackets::Pet::LearnPetSpecializationGroup& learnPetSpecializationGroup)
|
||||
{
|
||||
if (!_player->IsInWorld())
|
||||
return;
|
||||
|
||||
Pet* pet = ObjectAccessor::GetPet(*_player, learnPetSpecializationGroup.PetGUID);
|
||||
|
||||
if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
|
||||
pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
|
||||
return;
|
||||
|
||||
if (learnPetSpecializationGroup.SpecGroupIndex >= MAX_SPECIALIZATIONS)
|
||||
{
|
||||
TC_LOG_DEBUG("network", "WORLD: HandlePetSetSpecializationOpcode - specialization index %u out of range", learnPetSpecializationGroup.SpecGroupIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 specIndex = _player->HasAuraType(SPELL_AURA_OVERRIDE_PET_SPECS) ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0;
|
||||
ChrSpecializationEntry const* petSpec = sChrSpecializationByIndexStore[specIndex][learnPetSpecializationGroup.SpecGroupIndex];
|
||||
if (!petSpec)
|
||||
{
|
||||
TC_LOG_DEBUG("network", "WORLD: HandlePetSetSpecializationOpcode - specialization index %u not found", learnPetSpecializationGroup.SpecGroupIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->getLevel() < MIN_SPECIALIZATION_LEVEL)
|
||||
{
|
||||
TC_LOG_DEBUG("network", "WORLD: HandlePetSetSpecializationOpcode - player level too low for specializations");
|
||||
return;
|
||||
}
|
||||
|
||||
pet->SetSpecialization(petSpec->ID);
|
||||
}
|
||||
|
||||
@@ -185,3 +185,16 @@ void WorldPackets::Pet::PetCancelAura::Read()
|
||||
_worldPacket >> PetGUID;
|
||||
_worldPacket >> SpellID;
|
||||
}
|
||||
|
||||
void WorldPackets::Pet::LearnPetSpecializationGroup::Read()
|
||||
{
|
||||
_worldPacket >> PetGUID;
|
||||
_worldPacket >> SpecGroupIndex;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::Pet::SetPetSpecialization::Write()
|
||||
{
|
||||
_worldPacket << uint16(SpecID);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
@@ -226,6 +226,27 @@ namespace WorldPackets
|
||||
int32 SpellID = 0;
|
||||
};
|
||||
|
||||
class LearnPetSpecializationGroup final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
LearnPetSpecializationGroup(WorldPacket&& packet) : ClientPacket(CMSG_LEARN_PET_SPECIALIZATION_GROUP, std::move(packet)) { }
|
||||
|
||||
void Read() override;
|
||||
|
||||
ObjectGuid PetGUID;
|
||||
uint32 SpecGroupIndex = 0;
|
||||
};
|
||||
|
||||
class SetPetSpecialization final : public ServerPacket
|
||||
{
|
||||
public:
|
||||
SetPetSpecialization() : ServerPacket(SMSG_SET_PET_SPECIALIZATION, 2) { }
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
uint16 SpecID = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -430,7 +430,7 @@ void OpcodeTable::Initialize()
|
||||
DEFINE_HANDLER(CMSG_JOIN_RATED_BATTLEGROUND, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
|
||||
DEFINE_HANDLER(CMSG_KEEP_ALIVE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess);
|
||||
DEFINE_HANDLER(CMSG_KEYBOUND_OVERRIDE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
|
||||
DEFINE_HANDLER(CMSG_LEARN_PET_SPECIALIZATION_GROUP, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
|
||||
DEFINE_HANDLER(CMSG_LEARN_PET_SPECIALIZATION_GROUP, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Pet::LearnPetSpecializationGroup, &WorldSession::HandlePetSetSpecializationOpcode);
|
||||
DEFINE_HANDLER(CMSG_LEARN_TALENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Talent::LearnTalents, &WorldSession::HandleLearnTalentsOpcode);
|
||||
DEFINE_HANDLER(CMSG_LEAVE_GROUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::LeaveGroup, &WorldSession::HandleLeaveGroupOpcode);
|
||||
DEFINE_HANDLER(CMSG_LEAVE_PET_BATTLE_QUEUE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
|
||||
@@ -1586,7 +1586,7 @@ void OpcodeTable::Initialize()
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_MELEE_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_MOVEMENT_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PCT_SPELL_MODIFIER, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PET_SPECIALIZATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PET_SPECIALIZATION, STATUS_NEVER, CONNECTION_TYPE_REALM);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
|
||||
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROFICIENCY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
|
||||
|
||||
@@ -506,6 +506,7 @@ namespace WorldPackets
|
||||
class PetAction;
|
||||
class PetCancelAura;
|
||||
class PetSetAction;
|
||||
class LearnPetSpecializationGroup;
|
||||
}
|
||||
|
||||
namespace Petition
|
||||
@@ -1516,6 +1517,7 @@ class TC_GAME_API WorldSession
|
||||
void HandlePetCancelAuraOpcode(WorldPackets::Spells::PetCancelAura& packet);
|
||||
void HandlePetSpellAutocastOpcode(WorldPackets::Pet::PetSpellAutocast& packet);
|
||||
void HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& petCastSpell);
|
||||
void HandlePetSetSpecializationOpcode(WorldPackets::Pet::LearnPetSpecializationGroup& learnPetSpecializationGroup);
|
||||
|
||||
void HandleSetActionBarToggles(WorldPackets::Character::SetActionBarToggles& packet);
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
|
||||
&AuraEffect::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT
|
||||
&AuraEffect::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE
|
||||
&AuraEffect::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes
|
||||
&AuraEffect::HandleAuraModPetTalentsPoints, //145 SPELL_AURA_MOD_PET_TALENT_POINTS
|
||||
&AuraEffect::HandleNULL, //145 used by 5 spells in 6.2.4 dbc but the meaning of this aura changed (it's used by mind control spells but isn't the control itself)
|
||||
&AuraEffect::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE
|
||||
&AuraEffect::HandleModStateImmunityMask, //147 SPELL_AURA_MECHANIC_IMMUNITY_MASK
|
||||
&AuraEffect::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS
|
||||
@@ -510,7 +510,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
|
||||
&AuraEffect::HandleNULL, //448
|
||||
&AuraEffect::HandleNULL, //449
|
||||
&AuraEffect::HandleNULL, //450
|
||||
&AuraEffect::HandleNULL, //451 SPELL_AURA_OVERRIDE_PET_SPECS
|
||||
&AuraEffect::HandleOverridePetSpecs, //451 SPELL_AURA_OVERRIDE_PET_SPECS
|
||||
&AuraEffect::HandleNULL, //452
|
||||
&AuraEffect::HandleNoImmediateEffect, //453 SPELL_AURA_CHARGE_RECOVERY_MOD implemented in SpellHistory::GetChargeRecoveryTime
|
||||
&AuraEffect::HandleNoImmediateEffect, //454 SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER implemented in SpellHistory::GetChargeRecoveryTime
|
||||
@@ -2601,21 +2601,6 @@ void AuraEffect::HandleAuraUntrackable(AuraApplication const* aurApp, uint8 mode
|
||||
/*** SKILLS & TALENTS ***/
|
||||
/****************************/
|
||||
|
||||
void AuraEffect::HandleAuraModPetTalentsPoints(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
|
||||
{
|
||||
if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
|
||||
return;
|
||||
|
||||
Unit* target = aurApp->GetTarget();
|
||||
|
||||
if (target->GetTypeId() != TYPEID_PLAYER)
|
||||
return;
|
||||
|
||||
// Recalculate pet talent points
|
||||
if (Pet* pet = target->ToPlayer()->GetPet())
|
||||
pet->InitTalentForLevel();
|
||||
}
|
||||
|
||||
void AuraEffect::HandleAuraModSkill(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||
{
|
||||
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_SKILL)))
|
||||
@@ -6666,3 +6651,26 @@ void AuraEffect::HandleModSpellCategoryCooldown(AuraApplication const* aurApp, u
|
||||
if (Player* player = aurApp->GetTarget()->ToPlayer())
|
||||
player->SendSpellCategoryCooldowns();
|
||||
}
|
||||
|
||||
void AuraEffect::HandleOverridePetSpecs(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||
{
|
||||
if (!(mode & AURA_EFFECT_HANDLE_REAL))
|
||||
return;
|
||||
|
||||
Player* player = aurApp->GetTarget()->ToPlayer();
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
if (player->getClass() != CLASS_HUNTER)
|
||||
return;
|
||||
|
||||
Pet* pet = player->GetPet();
|
||||
if (!pet)
|
||||
return;
|
||||
|
||||
ChrSpecializationEntry const* currSpec = sChrSpecializationStore.LookupEntry(pet->GetSpecialization());
|
||||
if (!currSpec)
|
||||
return;
|
||||
|
||||
pet->SetSpecialization(sChrSpecializationByIndexStore[apply ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0][currSpec->OrderIndex]->ID);
|
||||
}
|
||||
|
||||
@@ -174,7 +174,6 @@ class TC_GAME_API AuraEffect
|
||||
void HandleAuraModStalked(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
void HandleAuraUntrackable(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
// skills & talents
|
||||
void HandleAuraModPetTalentsPoints(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
void HandleAuraModSkill(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
// movement
|
||||
void HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
@@ -305,6 +304,7 @@ class TC_GAME_API AuraEffect
|
||||
void HandleAuraForceWeather(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
void HandleEnableAltPower(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
void HandleModSpellCategoryCooldown(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
void HandleOverridePetSpecs(AuraApplication const* aurApp, uint8 mode, bool apply) const;
|
||||
|
||||
// aura effect periodic tick handlers
|
||||
void HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const;
|
||||
|
||||
@@ -2724,8 +2724,6 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
|
||||
// caster have pet now
|
||||
m_caster->SetMinion(pet, true);
|
||||
|
||||
pet->InitTalentForLevel();
|
||||
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||
@@ -5265,8 +5263,6 @@ void Spell::EffectCreateTamedPet(SpellEffIndex /*effIndex*/)
|
||||
// unitTarget has pet now
|
||||
unitTarget->SetMinion(pet, true);
|
||||
|
||||
pet->InitTalentForLevel();
|
||||
|
||||
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||
|
||||
Reference in New Issue
Block a user