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:
myran2
2016-05-26 17:20:01 +02:00
committed by Shauren
parent 6c71c8694f
commit bc1a81747a
16 changed files with 292 additions and 257 deletions
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,
+159 -216
View File
@@ -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();
}
+9 -9
View File
@@ -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
{
+1 -2
View File
@@ -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;
+11 -1
View File
@@ -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();
+34
View File
@@ -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;
};
}
}
+2 -2
View File
@@ -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);
+2
View File
@@ -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;
-4
View File
@@ -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);