aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities/Pet
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2020-08-16 21:39:25 +0200
committerShauren <shauren.trinity@gmail.com>2022-01-31 00:17:21 +0100
commitca92686b44cc8c53a8991bf02d6e8534797fc115 (patch)
tree2456cf72513722d93bb444590a4606d7717261d6 /src/server/game/Entities/Pet
parentb79a91039bba12d62e43806d8a20ad76107ac5d7 (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.cpp411
-rw-r--r--src/server/game/Entities/Pet/Pet.h19
-rw-r--r--src/server/game/Entities/Pet/PetDefines.h45
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