diff options
author | Shauren <shauren.trinity@gmail.com> | 2020-08-16 21:39:25 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-01-31 00:17:21 +0100 |
commit | ca92686b44cc8c53a8991bf02d6e8534797fc115 (patch) | |
tree | 2456cf72513722d93bb444590a4606d7717261d6 /src/server/game/Entities/Pet | |
parent | b79a91039bba12d62e43806d8a20ad76107ac5d7 (diff) |
Core/Pets: Pet management refactoring (#25191)
* Core/Pets: Pet management refactoring
* Preload basic pet data on character login with async query
* Load additional pet data (declined names/auras/spells/cooldowns) using async query after we are sure pet loading will succeed
* Remove all select queries related to pet stable/unstable
* Remove all silent pet deletions except explicit UI-triggered abandons
* Fixed displaying stable master content when current pet is not summoned
* Allow to stable/swap unsummoned current pet
Closes #3610
Closes #21266
(cherry picked from commit 5c1fc5e3876549f5ed2b9051fffb6f3d94a67d7a)
Diffstat (limited to 'src/server/game/Entities/Pet')
-rw-r--r-- | src/server/game/Entities/Pet/Pet.cpp | 411 | ||||
-rw-r--r-- | src/server/game/Entities/Pet/Pet.h | 19 | ||||
-rw-r--r-- | src/server/game/Entities/Pet/PetDefines.h | 45 |
3 files changed, 278 insertions, 197 deletions
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 25029227159..489d7c96aa6 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -27,6 +27,7 @@ #include "PetPackets.h" #include "PhasingHandler.h" #include "Player.h" +#include "QueryHolder.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" @@ -44,7 +45,7 @@ Pet::Pet(Player* owner, PetType type) : Guardian(nullptr, owner, true), m_removed(false), m_petType(type), m_duration(0), m_loading(false), m_groupUpdateMask(0), - m_declinedname(nullptr), m_petSpecialization(0) + m_petSpecialization(0) { ASSERT(GetOwner()); @@ -62,10 +63,7 @@ Pet::Pet(Player* owner, PetType type) : m_focusRegenTimer = PET_FOCUS_REGEN_INTERVAL; } -Pet::~Pet() -{ - delete m_declinedname; -} +Pet::~Pet() = default; void Pet::AddToWorld() { @@ -103,94 +101,145 @@ void Pet::RemoveFromWorld() } } -bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current) +std::pair<PetStable::PetInfo const*, PetSaveMode> Pet::GetLoadPetInfo(PetStable const& stable, uint32 petEntry, uint32 petnumber, bool current) { - m_loading = true; - - ObjectGuid::LowType ownerid = owner->GetGUID().GetCounter(); - - CharacterDatabasePreparedStatement* stmt; - PreparedQueryResult result; - if (petnumber) { // Known petnumber entry - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY); - stmt->setUInt64(0, ownerid); - stmt->setUInt32(1, petnumber); + if (stable.CurrentPet && stable.CurrentPet->PetNumber == petnumber) + return { &stable.CurrentPet.value(), PET_SAVE_AS_CURRENT }; + + for (std::size_t stableSlot = 0; stableSlot < stable.StabledPets.size(); ++stableSlot) + if (stable.StabledPets[stableSlot] && stable.StabledPets[stableSlot]->PetNumber == petnumber) + return { &stable.StabledPets[stableSlot].value(), PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + stableSlot) }; + + for (PetStable::PetInfo const& pet : stable.UnslottedPets) + if (pet.CreatureId == petEntry) + return { &pet, PET_SAVE_NOT_IN_SLOT }; } else if (current) { // Current pet (slot 0) - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT); - stmt->setUInt64(0, ownerid); - stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT)); + if (stable.CurrentPet) + return { &stable.CurrentPet.value(), PET_SAVE_AS_CURRENT }; } else if (petEntry) { // known petEntry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2); - stmt->setUInt64(0, ownerid); - stmt->setUInt32(1, petEntry); - stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT)); - stmt->setUInt8(3, uint8(PET_SAVE_LAST_STABLE_SLOT)); + if (stable.CurrentPet && stable.CurrentPet->CreatureId == petEntry) + return { &stable.CurrentPet.value(), PET_SAVE_AS_CURRENT }; + + for (PetStable::PetInfo const& pet : stable.UnslottedPets) + if (pet.CreatureId == petEntry) + return { &pet, PET_SAVE_NOT_IN_SLOT }; } else { // Any current or other non-stabled pet (for hunter "call pet") - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_SLOT); - stmt->setUInt64(0, ownerid); - stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT)); - stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT)); + if (stable.CurrentPet) + return { &stable.CurrentPet.value(), PET_SAVE_AS_CURRENT }; + + if (!stable.UnslottedPets.empty()) + return { &stable.UnslottedPets.front(), PET_SAVE_NOT_IN_SLOT }; } - result = CharacterDatabase.Query(stmt); + return { nullptr, PET_SAVE_AS_DELETED }; +} + +class PetLoadQueryHolder : public CharacterDatabaseQueryHolder +{ +public: + enum + { + DECLINED_NAMES, + AURAS, + AURA_EFFECTS, + SPELLS, + COOLDOWNS, + CHARGES, + + MAX + }; - if (!result) + PetLoadQueryHolder(ObjectGuid::LowType ownerGuid, uint32 petNumber) { - m_loading = false; - return false; + SetSize(MAX); + + CharacterDatabasePreparedStatement* stmt; + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME); + stmt->setUInt64(0, ownerGuid); + stmt->setUInt32(1, petNumber); + SetPreparedQuery(DECLINED_NAMES, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA); + stmt->setUInt32(0, petNumber); + SetPreparedQuery(AURAS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA_EFFECT); + stmt->setUInt32(0, petNumber); + SetPreparedQuery(AURA_EFFECTS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL); + stmt->setUInt32(0, petNumber); + SetPreparedQuery(SPELLS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN); + stmt->setUInt32(0, petNumber); + SetPreparedQuery(COOLDOWNS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_CHARGES); + stmt->setUInt32(0, petNumber); + SetPreparedQuery(CHARGES, stmt); } +}; - Field* fields = result->Fetch(); +bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current) +{ + m_loading = true; + + PetStable* petStable = ASSERT_NOTNULL(owner->GetPetStable()); - // update for case of current pet "slot = 0" - petEntry = fields[1].GetUInt32(); - if (!petEntry) + ObjectGuid::LowType ownerid = owner->GetGUID().GetCounter(); + std::pair<PetStable::PetInfo const*, PetSaveMode> info = GetLoadPetInfo(*petStable, petEntry, petnumber, current); + PetStable::PetInfo const* petInfo = info.first; + PetSaveMode slot = info.second; + if (!petInfo) + { + m_loading = false; return false; + } - uint32 summonSpellId = fields[14].GetUInt32(); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summonSpellId, owner->GetMap()->GetDifficultyID()); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petInfo->CreatedBySpellId, owner->GetMap()->GetDifficultyID()); bool isTemporarySummon = spellInfo && spellInfo->GetDuration() > 0; if (current && isTemporarySummon) return false; - PetType petType = PetType(fields[15].GetUInt8()); - if (petType == HUNTER_PET) + if (petInfo->Type == HUNTER_PET) { - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry); + CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petInfo->CreatureId); if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets())) return false; } - uint32 petId = fields[0].GetUInt32(); - if (current && owner->IsPetNeedBeTemporaryUnsummoned()) { - owner->SetTemporaryUnsummonedPetNumber(petId); + owner->SetTemporaryUnsummonedPetNumber(petInfo->PetNumber); return false; } Map* map = owner->GetMap(); - if (!Create(map->GenerateLowGuid<HighGuid::Pet>(), map, petEntry)) + ObjectGuid::LowType guid = map->GenerateLowGuid<HighGuid::Pet>(); + + if (!Create(guid, map, petInfo->CreatureId, petInfo->PetNumber)) return false; PhasingHandler::InheritPhaseShift(this, owner); - setPetType(petType); + setPetType(petInfo->Type); SetFaction(owner->GetFaction()); - SetCreatedBySpell(summonSpellId); + SetCreatedBySpell(petInfo->CreatedBySpellId); if (IsCritter()) { @@ -209,14 +258,14 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c return true; } - m_charmInfo->SetPetNumber(petId, IsPermanentPetFor(owner)); + m_charmInfo->SetPetNumber(petInfo->PetNumber, IsPermanentPetFor(owner)); - SetDisplayId(fields[3].GetUInt32()); - SetNativeDisplayId(fields[3].GetUInt32()); - uint32 petlevel = fields[4].GetUInt16(); + SetDisplayId(petInfo->DisplayId); + SetNativeDisplayId(petInfo->DisplayId); + uint8 petlevel = petInfo->Level; SetNpcFlags(UNIT_NPC_FLAG_NONE); SetNpcFlags2(UNIT_NPC_FLAG_2_NONE); - SetName(fields[8].GetString()); + SetName(petInfo->Name); switch (getPetType()) { @@ -229,7 +278,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c SetClass(CLASS_WARRIOR); SetGender(GENDER_NONE); SetSheath(SHEATH_STATE_MELEE); - SetPetFlags(fields[9].GetBool() ? UNIT_PET_FLAG_CAN_BE_ABANDONED : UnitPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED | UNIT_PET_FLAG_CAN_BE_ABANDONED)); + SetPetFlags(petInfo->WasRenamed ? UNIT_PET_FLAG_CAN_BE_ABANDONED : UnitPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED | UNIT_PET_FLAG_CAN_BE_ABANDONED)); SetUnitFlags(UNIT_FLAG_PLAYER_CONTROLLED); // this enables popup window (pet abandon, cancel) break; default: @@ -242,7 +291,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c SetCreatorGUID(owner->GetGUID()); InitStatsForLevel(petlevel); - SetPetExperience(fields[5].GetUInt32()); + SetPetExperience(petInfo->Experience); SynchronizeLevelWithOwner(); @@ -257,15 +306,15 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c return false; } - SetReactState(ReactStates(fields[6].GetUInt8())); + SetReactState(petInfo->ReactState); SetCanModifyStats(true); if (getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current SetFullPower(POWER_MANA); else { - uint32 savedhealth = fields[10].GetUInt32(); - uint32 savedmana = fields[11].GetUInt32(); + uint32 savedhealth = petInfo->Health; + uint32 savedmana = petInfo->Mana; if (!savedhealth && getPetType() == HUNTER_PET) setDeathState(JUST_DIED); else @@ -279,38 +328,31 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c // 0=current // 1..MAX_PET_STABLES in stable slot // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) - if (fields[7].GetUInt8()) + if (slot == PET_SAVE_NOT_IN_SLOT) { - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID); - stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT)); - stmt->setUInt64(1, ownerid); - stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT)); - stmt->setUInt32(3, m_charmInfo->GetPetNumber()); - trans->Append(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID); - stmt->setUInt8(0, uint8(PET_SAVE_AS_CURRENT)); - stmt->setUInt64(1, ownerid); - stmt->setUInt32(2, m_charmInfo->GetPetNumber()); - trans->Append(stmt); + auto unslottedPetItr = std::find_if(petStable->UnslottedPets.begin(), petStable->UnslottedPets.end(), [&](PetStable::PetInfo const& unslottedPet) + { + return unslottedPet.PetNumber == petInfo->PetNumber; + }); + ASSERT(!petStable->CurrentPet); + ASSERT(unslottedPetItr != petStable->UnslottedPets.end()); - CharacterDatabase.CommitTransaction(trans); + petStable->CurrentPet = std::move(*unslottedPetItr); + petStable->UnslottedPets.erase(unslottedPetItr); } // Send fake summon spell cast - this is needed for correct cooldown application for spells // Example: 46584 - without this cooldown (which should be set always when pet is loaded) isn't set clientside /// @todo pets should be summoned from real cast instead of just faking it? - if (summonSpellId) + if (petInfo->CreatedBySpellId) { WorldPackets::Spells::SpellGo spellGo; WorldPackets::Spells::SpellCastData& castData = spellGo.Cast; castData.CasterGUID = owner->GetGUID(); castData.CasterUnit = owner->GetGUID(); - castData.CastID = ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, summonSpellId, map->GenerateLowGuid<HighGuid::Cast>()); - castData.SpellID = summonSpellId; + castData.CastID = ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, petInfo->CreatedBySpellId, map->GenerateLowGuid<HighGuid::Cast>()); + castData.SpellID = petInfo->CreatedBySpellId; castData.CastFlags = CAST_FLAG_UNKNOWN_9; castData.CastTime = getMSTime(); @@ -318,69 +360,75 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c } owner->SetMinion(this, true); + + if (!isTemporarySummon) + m_charmInfo->LoadPetActionBar(petInfo->ActionBar); + map->AddToMap(ToCreature()); - uint32 timediff = uint32(GameTime::GetGameTime() - fields[13].GetUInt32()); - _LoadAuras(timediff); + //set last used pet number (for use in BG's) + if (owner->GetTypeId() == TYPEID_PLAYER && isControlled() && !isTemporarySummoned() && (getPetType() == SUMMON_PET || getPetType() == HUNTER_PET)) + owner->ToPlayer()->SetLastPetNumber(petInfo->PetNumber); - // load action bar, if data broken will fill later by default spells. - if (!isTemporarySummon) + owner->GetSession()->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(new PetLoadQueryHolder(ownerid, petInfo->PetNumber))) + .AfterComplete([this, owner, session = owner->GetSession(), isTemporarySummon, current, lastSaveTime = petInfo->LastSaveTime, specializationId = petInfo->SpecializationId](SQLQueryHolderBase* holder) { - m_charmInfo->LoadPetActionBar(fields[12].GetString()); + if (session->GetPlayer() != owner || owner->GetPet() != this) + return; - _LoadSpells(); - _LoadSpellCooldowns(); - LearnPetPassives(); - InitLevelupSpellsForLevel(); - if (map->IsBattleArena()) - RemoveArenaAuras(); + // passing previous checks ensure that 'this' is still valid + if (m_removed) + return; - CastPetAuras(current); - } + uint32 timediff = uint32(GameTime::GetGameTime() - lastSaveTime); + _LoadAuras(holder->GetPreparedResult(PetLoadQueryHolder::AURAS), holder->GetPreparedResult(PetLoadQueryHolder::AURA_EFFECTS), timediff); - TC_LOG_DEBUG("entities.pet", "New Pet has %s", GetGUID().ToString().c_str()); + // load action bar, if data broken will fill later by default spells. + if (!isTemporarySummon) + { + _LoadSpells(holder->GetPreparedResult(PetLoadQueryHolder::SPELLS)); + GetSpellHistory()->LoadFromDB<Pet>(holder->GetPreparedResult(PetLoadQueryHolder::COOLDOWNS), holder->GetPreparedResult(PetLoadQueryHolder::CHARGES)); + LearnPetPassives(); + InitLevelupSpellsForLevel(); + if (GetMap()->IsBattleArena()) + RemoveArenaAuras(); + + CastPetAuras(current); + } - uint16 specId = fields[16].GetUInt16(); - if (ChrSpecializationEntry const* petSpec = sChrSpecializationStore.LookupEntry(specId)) - specId = sDB2Manager.GetChrSpecializationByIndex(owner->HasAuraType(SPELL_AURA_OVERRIDE_PET_SPECS) ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0, petSpec->OrderIndex)->ID; + TC_LOG_DEBUG("entities.pet", "New Pet has %s", GetGUID().ToString().c_str()); - SetSpecialization(specId); + uint16 specId = specializationId; + if (ChrSpecializationEntry const* petSpec = sChrSpecializationStore.LookupEntry(specId)) + specId = sDB2Manager.GetChrSpecializationByIndex(owner->HasAuraType(SPELL_AURA_OVERRIDE_PET_SPECS) ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0, petSpec->OrderIndex)->ID; - // 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(); - } + SetSpecialization(specId); - SetGroupUpdateFlag(GROUP_UPDATE_PET_FULL); + // 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 - if (getPetType() == HUNTER_PET) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME); - stmt->setUInt64(0, owner->GetGUID().GetCounter()); - stmt->setUInt32(1, GetCharmInfo()->GetPetNumber()); - result = CharacterDatabase.Query(stmt); + owner->PetSpellInitialize(); + } + + SetGroupUpdateFlag(GROUP_UPDATE_PET_FULL); - if (result) + if (getPetType() == HUNTER_PET) { - delete m_declinedname; - m_declinedname = new DeclinedName; - Field* fields2 = result->Fetch(); - for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + if (PreparedQueryResult result = holder->GetPreparedResult(PetLoadQueryHolder::DECLINED_NAMES)) { - m_declinedname->name[i] = fields2[i].GetString(); + m_declinedname = std::make_unique<DeclinedName>(); + Field* fields = result->Fetch(); + for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + m_declinedname->name[i] = fields[i].GetString(); } } - } - - //set last used pet number (for use in BG's) - if (owner->GetTypeId() == TYPEID_PLAYER && isControlled() && !isTemporarySummoned() && (getPetType() == SUMMON_PET || getPetType() == HUNTER_PET)) - owner->ToPlayer()->SetLastPetNumber(petId); - // must be after SetMinion (owner guid check) - LoadTemplateImmunities(); - m_loading = false; + // must be after SetMinion (owner guid check) + LoadTemplateImmunities(); + m_loading = false; + }); return true; } @@ -438,16 +486,6 @@ void Pet::SavePetToDB(PetSaveMode mode) stmt->setUInt32(0, m_charmInfo->GetPetNumber()); trans->Append(stmt); - // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) - if (mode <= PET_SAVE_LAST_STABLE_SLOT) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT); - stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT)); - stmt->setUInt64(1, ownerLowGUID); - stmt->setUInt8(2, uint8(mode)); - trans->Append(stmt); - } - // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT if (getPetType() == HUNTER_PET && (mode == PET_SAVE_AS_CURRENT || mode > PET_SAVE_LAST_STABLE_SLOT)) { @@ -459,6 +497,11 @@ void Pet::SavePetToDB(PetSaveMode mode) } // save pet + std::string actionBar = GenerateActionBarData(); + + ASSERT(owner->GetPetStable()->CurrentPet && owner->GetPetStable()->CurrentPet->PetNumber == m_charmInfo->GetPetNumber()); + FillPetInfo(&owner->GetPetStable()->CurrentPet.value()); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET); stmt->setUInt32(0, m_charmInfo->GetPetNumber()); stmt->setUInt32(1, GetEntry()); @@ -472,13 +515,11 @@ void Pet::SavePetToDB(PetSaveMode mode) stmt->setUInt8(9, HasPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED) ? 0 : 1); stmt->setUInt32(10, curhealth); stmt->setUInt32(11, curmana); - - stmt->setString(12, GenerateActionBarData()); - + stmt->setString(12, actionBar); stmt->setUInt32(13, GameTime::GetGameTime()); stmt->setUInt32(14, m_unitData->CreatedBySpell); stmt->setUInt8(15, getPetType()); - stmt->setUInt16(16, m_petSpecialization); + stmt->setUInt16(16, GetSpecialization()); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); @@ -491,36 +532,55 @@ void Pet::SavePetToDB(PetSaveMode mode) } } -void Pet::DeleteFromDB(uint32 guidlow) +void Pet::FillPetInfo(PetStable::PetInfo* petInfo) const +{ + petInfo->PetNumber = m_charmInfo->GetPetNumber(); + petInfo->CreatureId = GetEntry(); + petInfo->DisplayId = GetNativeDisplayId(); + petInfo->Level = GetLevel(); + petInfo->Experience = m_unitData->PetExperience; + petInfo->ReactState = GetReactState(); + petInfo->Name = GetName(); + petInfo->WasRenamed = !HasPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED); + petInfo->Health = GetHealth(); + petInfo->Mana = GetPower(POWER_MANA); + petInfo->ActionBar = GenerateActionBarData(); + petInfo->LastSaveTime = GameTime::GetGameTime(); + petInfo->CreatedBySpellId = m_unitData->CreatedBySpell; + petInfo->Type = getPetType(); + petInfo->SpecializationId = GetSpecialization(); +} + +void Pet::DeleteFromDB(uint32 petNumber) { CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURA_EFFECTS); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELLS); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_CHARGES); - stmt->setUInt32(0, guidlow); + stmt->setUInt32(0, petNumber); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); @@ -582,7 +642,8 @@ void Pet::Update(uint32 diff) if (owner->GetPetGUID() != GetGUID()) { TC_LOG_ERROR("entities.pet", "Pet %u is not pet of owner %s, removed", GetEntry(), GetOwner()->GetName().c_str()); - Remove(getPetType() == HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT); + ASSERT(getPetType() != HUNTER_PET, "Unexpected unlinked pet found for owner %s", owner->GetSession()->GetPlayerInfo().c_str()); + Remove(PET_SAVE_NOT_IN_SLOT); return; } } @@ -593,7 +654,7 @@ void Pet::Update(uint32 diff) m_duration -= diff; else { - Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT); + Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT); return; } } @@ -744,7 +805,7 @@ bool Pet::CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner) bool Pet::CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map) { TC_LOG_DEBUG("entities.pet", "Pet::CreateBaseForTamed"); - if (!Create(map->GenerateLowGuid<HighGuid::Pet>(), map, cinfo->Entry)) + if (!Create(map->GenerateLowGuid<HighGuid::Pet>(), map, cinfo->Entry, sObjectMgr->GeneratePetNumber())) return false; SetPetNameTimestamp(0); @@ -1042,25 +1103,8 @@ bool Pet::HaveInDiet(ItemTemplate const* item) const return (diet & FoodMask) != 0; } -void Pet::_LoadSpellCooldowns() +void Pet::_LoadSpells(PreparedQueryResult result) { - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - PreparedQueryResult cooldownsResult = CharacterDatabase.Query(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_CHARGES); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - PreparedQueryResult chargesResult = CharacterDatabase.Query(stmt); - - GetSpellHistory()->LoadFromDB<Pet>(cooldownsResult, chargesResult); -} - -void Pet::_LoadSpells() -{ - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - PreparedQueryResult result = CharacterDatabase.Query(stmt); - if (result) { do @@ -1122,21 +1166,18 @@ void Pet::_SaveSpells(CharacterDatabaseTransaction& trans) } } -void Pet::_LoadAuras(uint32 timediff) +void Pet::_LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effectResult, uint32 timediff) { TC_LOG_DEBUG("entities.pet", "Loading auras for pet %s", GetGUID().ToString().c_str()); + ObjectGuid casterGuid, itemGuid; + std::map<AuraKey, AuraLoadEffectInfo> effectInfo; + /* 0 1 2 3 4 5 SELECT casterGuid, spell, effectMask, effectIndex, amount, baseAmount FROM pet_aura_effect WHERE guid = ? */ - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA_EFFECT); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - - ObjectGuid casterGuid, itemGuid; - std::map<AuraKey, AuraLoadEffectInfo> effectInfo; - if (PreparedQueryResult effectResult = CharacterDatabase.Query(stmt)) + if (effectResult) { do { @@ -1156,14 +1197,11 @@ void Pet::_LoadAuras(uint32 timediff) } while (effectResult->NextRow()); } - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - /* 0 1 2 3 4 5 6 7 8 SELECT casterGuid, spell, effectMask, recalculateMask, difficulty, stackCount, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ? */ - if (PreparedQueryResult auraResult = CharacterDatabase.Query(stmt)) + if (auraResult) { do { @@ -1453,9 +1491,9 @@ void Pet::InitLevelupSpellsForLevel() // default spells (can be not learned if pet level (as owner level decrease result for example) less first possible in normal game) if (PetDefaultSpellsEntry const* defSpells = sSpellMgr->GetPetDefaultSpellsEntry(int32(GetEntry()))) { - for (uint8 i = 0; i < MAX_CREATURE_SPELL_DATA_SLOT; ++i) + for (uint32 spellId : defSpells->spellid) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(defSpells->spellid[i], DIFFICULTY_NONE); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE); if (!spellInfo) continue; @@ -1627,11 +1665,12 @@ bool Pet::IsPermanentPetFor(Player* owner) const } } -bool Pet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry) +bool Pet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry, uint32 /*petId*/) { ASSERT(map); SetMap(map); + // TODO: counter should be constructed as (summon_count << 32) | petNumber Object::_Create(ObjectGuid::Create<HighGuid::Pet>(map->GetId(), Entry, guidlow)); m_spawnId = guidlow; @@ -1672,8 +1711,8 @@ void Pet::LearnPetPassives() // For general hunter pets skill 270 // Passive 01~10, Passive 00 (20782, not used), Ferocious Inspiration (34457) // Scale 01~03 (34902~34904, bonus from owner, not used) - for (PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet) - addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, PETSPELL_FAMILY); + for (uint32 spellId : petStore->second) + addSpell(spellId, ACT_DECIDE, PETSPELL_NEW, PETSPELL_FAMILY); } } @@ -1716,11 +1755,10 @@ bool Pet::IsPetAura(Aura const* aura) Player* owner = GetOwner(); // if the owner has that pet aura, return true - for (auto itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); ++itr) - { - if ((*itr)->GetAura(GetEntry()) == aura->GetId()) + for (PetAura const* petAura : owner->m_petAuras) + if (petAura->GetAura(GetEntry()) == aura->GetId()) return true; - } + return false; } @@ -1896,6 +1934,7 @@ std::string Pet::GetDebugInfo() const std::stringstream sstr; sstr << Guardian::GetDebugInfo() << "\n" << std::boolalpha - << "PetType: " << std::to_string(getPetType()); + << "PetType: " << std::to_string(getPetType()) + << "PetNumber: " << m_charmInfo->GetPetNumber(); return sstr.str(); } diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 88880884ed6..9456dab4448 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -61,15 +61,17 @@ class TC_GAME_API Pet : public Guardian bool IsPermanentPetFor(Player* owner) const; // pet have tab in character windows and set UNIT_FIELD_PETNUMBER - bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry); + bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry, uint32 pet_number); bool CreateBaseAtCreature(Creature* creature); bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner); bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map); - bool LoadPetFromDB(Player* owner, uint32 petentry = 0, uint32 petnumber = 0, bool current = false); + static std::pair<PetStable::PetInfo const*, PetSaveMode> GetLoadPetInfo(PetStable const& stable, uint32 petEntry, uint32 petnumber, bool current); + bool LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current); bool IsLoading() const override { return m_loading;} void SavePetToDB(PetSaveMode mode); + void FillPetInfo(PetStable::PetInfo* petInfo) const; void Remove(PetSaveMode mode, bool returnreagent = false); - static void DeleteFromDB(uint32 guidlow); + static void DeleteFromDB(uint32 petNumber); void setDeathState(DeathState s) override; // overwrite virtual Creature::setDeathState and Unit::setDeathState void Update(uint32 diff) override; // overwrite virtual Creature::Update and Unit::Update @@ -112,10 +114,9 @@ class TC_GAME_API Pet : public Guardian void CastPetAura(PetAura const* aura); bool IsPetAura(Aura const* aura); - void _LoadSpellCooldowns(); - void _LoadAuras(uint32 timediff); + void _LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effectResult, uint32 timediff); void _SaveAuras(CharacterDatabaseTransaction& trans); - void _LoadSpells(); + void _LoadSpells(PreparedQueryResult result); void _SaveSpells(CharacterDatabaseTransaction& trans); bool addSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL); @@ -134,7 +135,7 @@ class TC_GAME_API Pet : public Guardian void InitPetCreateSpells(); - uint16 GetSpecialization() { return m_petSpecialization; } + uint16 GetSpecialization() const { return m_petSpecialization; } void SetSpecialization(uint16 spec); void LearnSpecializationSpells(); void RemoveSpecializationSpells(bool clearActionBar); @@ -143,7 +144,7 @@ class TC_GAME_API Pet : public Guardian void SetGroupUpdateFlag(uint32 flag); void ResetGroupUpdateFlag(); - DeclinedName const* GetDeclinedNames() const { return m_declinedname; } + DeclinedName const* GetDeclinedNames() const { return m_declinedname.get(); } bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved) @@ -158,7 +159,7 @@ class TC_GAME_API Pet : public Guardian uint32 m_focusRegenTimer; uint32 m_groupUpdateMask; - DeclinedName *m_declinedname; + std::unique_ptr<DeclinedName> m_declinedname; uint16 m_petSpecialization; diff --git a/src/server/game/Entities/Pet/PetDefines.h b/src/server/game/Entities/Pet/PetDefines.h index 83607eb0bc1..f69b6ff8af9 100644 --- a/src/server/game/Entities/Pet/PetDefines.h +++ b/src/server/game/Entities/Pet/PetDefines.h @@ -19,8 +19,14 @@ #define TRINITYCORE_PET_DEFINES_H #include "Define.h" +#include "Optional.h" +#include <array> +#include <string> +#include <vector> -enum PetType +enum ReactStates : uint8; + +enum PetType : uint8 { SUMMON_PET = 0, HUNTER_PET = 1, @@ -30,7 +36,7 @@ enum PetType #define MAX_PET_STABLES 4 // stored in character_pet.slot -enum PetSaveMode +enum PetSaveMode : int8 { PET_SAVE_AS_DELETED = -1, // not saved in fact PET_SAVE_AS_CURRENT = 0, // in current slot (with player) @@ -98,4 +104,39 @@ enum class PetTameResult : uint8 EliteTooHighLevel = 14 }; +class PetStable +{ +public: + struct PetInfo + { + PetInfo() { } + + std::string Name; + std::string ActionBar; + uint32 PetNumber = 0; + uint32 CreatureId = 0; + uint32 DisplayId = 0; + uint32 Experience = 0; + uint32 Health = 0; + uint32 Mana = 0; + uint32 LastSaveTime = 0; + uint32 CreatedBySpellId = 0; + uint16 SpecializationId = 0; + uint8 Level = 0; + ReactStates ReactState = ReactStates(0); + PetType Type = MAX_PET_TYPE; + bool WasRenamed = false; + }; + + Optional<PetInfo> CurrentPet; // PET_SAVE_AS_CURRENT + std::array<Optional<PetInfo>, MAX_PET_STABLES> StabledPets; // PET_SAVE_FIRST_STABLE_SLOT - PET_SAVE_LAST_STABLE_SLOT + uint32 MaxStabledPets = 0; + std::vector<PetInfo> UnslottedPets; // PET_SAVE_NOT_IN_SLOT + + PetInfo const* GetUnslottedHunterPet() const + { + return UnslottedPets.size() == 1 && UnslottedPets[0].Type == HUNTER_PET ? &UnslottedPets[0] : nullptr; + } +}; + #endif |