aboutsummaryrefslogtreecommitdiff
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
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)
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp27
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h13
-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
-rw-r--r--src/server/game/Entities/Player/Player.cpp104
-rw-r--r--src/server/game/Entities/Player/Player.h12
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp13
-rw-r--r--src/server/game/Entities/Unit/UnitDefines.h2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp4
-rw-r--r--src/server/game/Handlers/NPCHandler.cpp340
-rw-r--r--src/server/game/Handlers/PetHandler.cpp7
-rw-r--r--src/server/game/Server/WorldSession.h4
-rw-r--r--src/server/game/Spells/Spell.cpp30
-rw-r--r--src/server/game/Spells/SpellEffects.cpp4
-rw-r--r--src/server/scripts/Commands/cs_pet.cpp31
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp20
17 files changed, 648 insertions, 438 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 6003983276b..37b59e6cc3a 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -704,27 +704,21 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_CALENDAR_INVITE, "DELETE FROM calendar_invites WHERE InviteID = ?", CONNECTION_ASYNC);
// Pet
- PrepareStatement(CHAR_SEL_PET_SLOTS, "SELECT owner, slot FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT owner, id, entry, level, name, modelid FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_PET_ENTRY, "SELECT entry FROM character_pet WHERE owner = ? AND id = ? AND slot >= ? AND slot <= ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, entry FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_PET_SPELL_LIST, "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet WHERE character_pet.owner = ? AND character_pet.id = pet_spell.guid AND character_pet.id <> ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT id FROM character_pet WHERE owner = ? AND id <> ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_IDS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, difficulty, stackCount, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_PET_AURA_EFFECT, "SELECT casterGuid, spell, effectMask, effectIndex, amount, baseAmount FROM pet_aura_effect WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time, categoryId, categoryEnd FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, difficulty, stackCount, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_AURA_EFFECT, "SELECT casterGuid, spell, effectMask, effectIndex, amount, baseAmount FROM pet_aura_effect WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time, categoryId, categoryEnd FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_DEL_PET_AURA_EFFECTS, "DELETE FROM pet_aura_effect WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time, categoryId, categoryEnd) VALUES (?, ?, ?, ?, ?)", CONNECTION_BOTH);
- PrepareStatement(CHAR_SEL_PET_SPELL_CHARGES, "SELECT categoryId, rechargeStart, rechargeEnd FROM pet_spell_charges WHERE guid = ? AND rechargeEnd > UNIX_TIMESTAMP() ORDER BY rechargeEnd", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PET_SPELL_CHARGES, "SELECT categoryId, rechargeStart, rechargeEnd FROM pet_spell_charges WHERE guid = ? AND rechargeEnd > UNIX_TIMESTAMP() ORDER BY rechargeEnd", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_SPELL_CHARGES, "DELETE FROM pet_spell_charges WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PET_SPELL_CHARGES, "INSERT INTO pet_spell_charges (guid, categoryId, rechargeStart, rechargeEnd) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? and spell = ?", CONNECTION_ASYNC);
@@ -733,14 +727,9 @@ 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, 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_SEL_CHAR_PETS, "SELECT id, entry, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
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);
- PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ?", CONNECTION_ASYNC);
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);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index b83ed38531d..5fe79e83112 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -592,22 +592,11 @@ enum CharacterDatabaseStatements : uint32
CHAR_DEL_PET_SPELLS,
CHAR_DEL_CHAR_PET_BY_OWNER,
CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER,
- CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT,
- CHAR_SEL_PET_SLOTS,
- CHAR_SEL_PET_SLOTS_DETAIL,
- CHAR_SEL_PET_ENTRY,
- CHAR_SEL_PET_SLOT_BY_ID,
- CHAR_SEL_PET_SPELL_LIST,
- CHAR_SEL_CHAR_PET,
+ CHAR_SEL_CHAR_PET_IDS,
CHAR_SEL_CHAR_PETS,
- CHAR_SEL_CHAR_PET_BY_ENTRY,
- CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2,
- CHAR_SEL_CHAR_PET_BY_SLOT,
CHAR_DEL_CHAR_PET_DECLINEDNAME,
CHAR_INS_CHAR_PET_DECLINEDNAME,
CHAR_UPD_CHAR_PET_NAME,
- CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID,
- CHAR_UPD_CHAR_PET_SLOT_BY_SLOT,
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
CHAR_DEL_CHAR_PET_BY_ID,
CHAR_DEL_CHAR_PET_BY_SLOT,
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
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index d50efffe822..d579d970426 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -258,8 +258,6 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
m_movementForceModMagnitudeChanges = 0;
- m_stableSlots = 0;
-
/////////////////// Instance System /////////////////////
m_HomebindTimer = 0;
@@ -3909,7 +3907,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
// Unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
// NOW we can finally clear other DB data related to character
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PETS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_IDS);
stmt->setUInt64(0, guid);
PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
@@ -18415,13 +18413,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder)
uint32 extraflags = fields.extra_flags;
- m_stableSlots = fields.stable_slots;
- if (m_stableSlots > MAX_PET_STABLES)
- {
- TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) can't have more stable slots than %u, but has %u in DB",
- GetGUID().ToString().c_str(), MAX_PET_STABLES, uint32(m_stableSlots));
- m_stableSlots = MAX_PET_STABLES;
- }
+ _LoadPetStable(fields.stable_slots, holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS));
if (HasAtLoginFlag(AT_LOGIN_RENAME))
{
@@ -19467,7 +19459,7 @@ void Player::LoadPet()
{
//fixme: the pet should still be loaded if the player is not in world
// just not added to the map
- if (IsInWorld())
+ if (m_petStable && IsInWorld())
{
Pet* pet = new Pet(this);
if (!pet->LoadPetFromDB(this, 0, 0, true))
@@ -20485,7 +20477,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
stmt->setInt64(index++, GetTalentResetTime());
stmt->setUInt32(index++, GetPrimarySpecialization());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
- stmt->setUInt8(index++, m_stableSlots);
+ stmt->setUInt8(index++, m_petStable ? m_petStable->MaxStabledPets : 0);
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
stmt->setInt64(index++, m_deathExpireTime);
@@ -20629,7 +20621,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
stmt->setUInt8(index++, GetNumRespecs());
stmt->setUInt32(index++, GetPrimarySpecialization());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
- stmt->setUInt8(index++, m_stableSlots);
+ stmt->setUInt8(index++, m_petStable ? m_petStable->MaxStabledPets : 0);
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
stmt->setUInt16(index++, GetZoneId());
stmt->setInt64(index++, m_deathExpireTime);
@@ -21991,6 +21983,17 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
// only if current pet in slot
pet->SavePetToDB(mode);
+ ASSERT(m_petStable->CurrentPet && m_petStable->CurrentPet->PetNumber == pet->GetCharmInfo()->GetPetNumber());
+ if (mode == PET_SAVE_NOT_IN_SLOT)
+ {
+ m_petStable->UnslottedPets.push_back(std::move(*m_petStable->CurrentPet));
+ m_petStable->CurrentPet.reset();
+ }
+ else if (mode == PET_SAVE_AS_DELETED)
+ m_petStable->CurrentPet.reset();
+ // else if (stable slots) handled in opcode handlers due to required swaps
+ // else (current pet) doesnt need to do anything
+
SetMinion(pet, false);
pet->AddObjectToRemoveList();
@@ -28070,6 +28073,14 @@ bool Player::AddItem(uint32 itemId, uint32 count)
return true;
}
+PetStable& Player::GetOrInitPetStable()
+{
+ if (!m_petStable)
+ m_petStable = std::make_unique<PetStable>();
+
+ return *m_petStable;
+}
+
void Player::SendItemRefundResult(Item* item, ItemExtendedCostEntry const* iece, uint8 error) const
{
WorldPackets::Item::ItemPurchaseRefundResult itemPurchaseRefundResult;
@@ -28270,6 +28281,55 @@ void Player::_LoadInstanceTimeRestrictions(PreparedQueryResult result)
} while (result->NextRow());
}
+void Player::_LoadPetStable(uint8 petStableSlots, PreparedQueryResult result)
+{
+ if (!petStableSlots && !result)
+ return;
+
+ m_petStable = std::make_unique<PetStable>();
+ m_petStable->MaxStabledPets = petStableSlots;
+ if (m_petStable->MaxStabledPets > MAX_PET_STABLES)
+ {
+ TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) can't have more stable slots than %u, but has %u in DB",
+ GetGUID().ToString().c_str(), MAX_PET_STABLES, m_petStable->MaxStabledPets);
+ m_petStable->MaxStabledPets = MAX_PET_STABLES;
+ }
+
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ // SELECT id, entry, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ?
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ PetStable::PetInfo petInfo;
+ petInfo.PetNumber = fields[0].GetUInt32();
+ petInfo.CreatureId = fields[1].GetUInt32();
+ petInfo.DisplayId = fields[2].GetUInt32();
+ petInfo.Level = fields[3].GetUInt16();
+ petInfo.Experience = fields[4].GetUInt32();
+ petInfo.ReactState = ReactStates(fields[5].GetUInt8());
+ PetSaveMode slot = PetSaveMode(fields[6].GetUInt8());
+ petInfo.Name = fields[7].GetString();
+ petInfo.WasRenamed = fields[8].GetBool();
+ petInfo.Health = fields[9].GetUInt32();
+ petInfo.Mana = fields[10].GetUInt32();
+ petInfo.ActionBar = fields[11].GetString();
+ petInfo.LastSaveTime = fields[12].GetUInt32();
+ petInfo.CreatedBySpellId = fields[13].GetUInt32();
+ petInfo.Type = PetType(fields[14].GetUInt8());
+ petInfo.SpecializationId = fields[15].GetUInt16();
+ if (slot == PET_SAVE_AS_CURRENT)
+ m_petStable->CurrentPet = std::move(petInfo);
+ else if (slot >= PET_SAVE_FIRST_STABLE_SLOT && slot <= PET_SAVE_LAST_STABLE_SLOT)
+ m_petStable->StabledPets[slot - 1] = std::move(petInfo);
+ else if (slot == PET_SAVE_NOT_IN_SLOT)
+ m_petStable->UnslottedPets.push_back(std::move(petInfo));
+
+ } while (result->NextRow());
+ }
+}
+
void Player::_SaveInstanceTimeRestrictions(CharacterDatabaseTransaction& trans)
{
if (_instanceResetTimes.empty())
@@ -28605,19 +28665,14 @@ Guild const* Player::GetGuild() const
return guildId ? sGuildMgr->GetGuildById(guildId) : nullptr;
}
-Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration, bool aliveOnly)
+Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration)
{
+ PetStable& petStable = GetOrInitPetStable();
+
Pet* pet = new Pet(this, petType);
- if (petType == SUMMON_PET && pet->LoadPetFromDB(this, entry))
+ if (petType == SUMMON_PET && pet->LoadPetFromDB(this, entry, 0, false))
{
- if (aliveOnly && !pet->IsAlive())
- {
- pet->DespawnOrUnsummon();
- SendTameFailure(PetTameResult::Dead);
- return nullptr;
- }
-
if (duration > 0)
pet->SetDuration(duration);
@@ -28641,7 +28696,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
Map* map = GetMap();
uint32 pet_number = sObjectMgr->GeneratePetNumber();
- if (!pet->Create(map->GenerateLowGuid<HighGuid::Pet>(), map, entry))
+ if (!pet->Create(map->GenerateLowGuid<HighGuid::Pet>(), map, entry, pet_number))
{
TC_LOG_ERROR("misc", "Player::SummonPet: No such creature entry %u", entry);
delete pet;
@@ -28677,6 +28732,9 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
map->AddToMap(pet->ToCreature());
+ ASSERT(!petStable.CurrentPet && (petType != HUNTER_PET || !petStable.GetUnslottedHunterPet()));
+ pet->FillPetInfo(&petStable.CurrentPet.emplace());
+
switch (petType)
{
case SUMMON_PET:
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index d7ef33b7f73..05a106e2ef1 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -889,6 +889,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_CURRENCY,
PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES,
PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION,
+ PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS,
PLAYER_LOGIN_QUERY_LOAD_GARRISON,
PLAYER_LOGIN_QUERY_LOAD_GARRISON_BLUEPRINTS,
PLAYER_LOGIN_QUERY_LOAD_GARRISON_BUILDINGS,
@@ -1205,8 +1206,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void setDeathState(DeathState s) override; // overwrite Unit::setDeathState
+ PetStable* GetPetStable() { return m_petStable.get(); }
+ PetStable& GetOrInitPetStable();
+ PetStable const* GetPetStable() const { return m_petStable.get(); }
+
Pet* GetPet() const;
- Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime, bool aliveOnly = false);
+ Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime);
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
void SendTameFailure(PetTameResult result);
@@ -1490,8 +1495,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool AddItem(uint32 itemId, uint32 count);
- uint32 m_stableSlots;
-
/*********************************************************/
/*** GOSSIP SYSTEM ***/
/*********************************************************/
@@ -2833,6 +2836,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadTalents(PreparedQueryResult result);
void _LoadPvpTalents(PreparedQueryResult result);
void _LoadInstanceTimeRestrictions(PreparedQueryResult result);
+ void _LoadPetStable(uint8 petStableSlots, PreparedQueryResult result);
void _LoadCurrency(PreparedQueryResult result);
void _LoadCUFProfiles(PreparedQueryResult result);
@@ -3073,6 +3077,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool m_bCanDelayTeleport;
bool m_bHasDelayedTeleport;
+ std::unique_ptr<PetStable> m_petStable;
+
// Temporary removed pet cache
uint32 m_temporaryUnsummonedPetNumber;
uint32 m_oldpetspell;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 65d4f4f5165..d037047e9de 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -10283,7 +10283,11 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id)
uint8 level = creatureTarget->GetLevelForTarget(this) + 5 < GetLevel() ? (GetLevel() - 5) : creatureTarget->GetLevelForTarget(this);
- InitTamedPet(pet, level, spell_id);
+ if (!InitTamedPet(pet, level, spell_id))
+ {
+ delete pet;
+ return nullptr;
+ }
return pet;
}
@@ -10310,6 +10314,11 @@ Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id)
bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
{
+ Player* player = ToPlayer();
+ PetStable& petStable = player->GetOrInitPetStable();
+ if (petStable.CurrentPet || petStable.GetUnslottedHunterPet())
+ return false;
+
pet->SetCreatorGUID(GetGUID());
pet->SetFaction(GetFaction());
pet->SetCreatedBySpell(spell_id);
@@ -10330,6 +10339,8 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
pet->InitPetCreateSpells();
//pet->InitLevelupSpellsForLevel();
pet->SetFullHealth();
+
+ pet->FillPetInfo(&petStable.CurrentPet.emplace());
return true;
}
diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h
index b852ebdc02e..60babf771db 100644
--- a/src/server/game/Entities/Unit/UnitDefines.h
+++ b/src/server/game/Entities/Unit/UnitDefines.h
@@ -380,7 +380,7 @@ enum ActiveStates
ACT_DECIDE = 0x00 // custom
};
-enum ReactStates
+enum ReactStates : uint8
{
REACT_PASSIVE = 0,
REACT_DEFENSIVE = 1,
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index befda8ece15..ae75f958c8e 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -310,6 +310,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PETS);
+ stmt->setUInt64(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS, stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GARRISON, stmt);
diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp
index 09dceb99ec2..9854aaa6710 100644
--- a/src/server/game/Handlers/NPCHandler.cpp
+++ b/src/server/game/Handlers/NPCHandler.cpp
@@ -362,62 +362,62 @@ void WorldSession::HandleRequestStabledPets(WorldPackets::NPC::RequestStabledPet
void WorldSession::SendStablePet(ObjectGuid guid)
{
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
-
- stmt->setUInt64(0, _player->GetGUID().GetCounter());
- stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
- stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
-
- _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1)));
-}
-
-void WorldSession::SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result)
-{
- if (!GetPlayer())
- return;
-
WorldPackets::Pet::PetStableList packet;
packet.StableMaster = guid;
- Pet* pet = _player->GetPet();
+ PetStable* petStable = GetPlayer()->GetPetStable();
+ if (!petStable)
+ {
+ SendPacket(packet.Write());
+ return;
+ }
int32 petSlot = 0;
- // not let move dead pet in slot
- if (pet && pet->IsAlive() && pet->getPetType() == HUNTER_PET)
+ if (petStable->CurrentPet)
{
- WorldPackets::Pet::PetStableInfo stableEntry;
+ PetStable::PetInfo const& pet = *petStable->CurrentPet;
+ WorldPackets::Pet::PetStableInfo& stableEntry = packet.Pets.emplace_back();
stableEntry.PetSlot = petSlot;
- stableEntry.PetNumber = pet->GetCharmInfo()->GetPetNumber();
- stableEntry.CreatureID = pet->GetEntry();
- stableEntry.DisplayID = pet->GetDisplayId();
- stableEntry.ExperienceLevel = pet->GetLevel();
+ stableEntry.PetNumber = pet.PetNumber;
+ stableEntry.CreatureID = pet.CreatureId;
+ stableEntry.DisplayID = pet.DisplayId;
+ stableEntry.ExperienceLevel = pet.Level;
stableEntry.PetFlags = PET_STABLE_ACTIVE;
- stableEntry.PetName = pet->GetName();
+ stableEntry.PetName = pet.Name;
++petSlot;
-
- packet.Pets.push_back(stableEntry);
}
-
- if (result)
+ else
{
- do
+ if (PetStable::PetInfo const* pet = petStable->GetUnslottedHunterPet())
{
- Field* fields = result->Fetch();
- WorldPackets::Pet::PetStableInfo stableEntry;
+ WorldPackets::Pet::PetStableInfo& stableEntry = packet.Pets.emplace_back();
+ stableEntry.PetSlot = petSlot;
+ stableEntry.PetNumber = pet->PetNumber;
+ stableEntry.CreatureID = pet->CreatureId;
+ stableEntry.DisplayID = pet->DisplayId;
+ stableEntry.ExperienceLevel = pet->Level;
+ stableEntry.PetFlags = PET_STABLE_ACTIVE;
+ stableEntry.PetName = pet->Name;
+ ++petSlot;
+ }
+ }
+ for (Optional<PetStable::PetInfo> const& stabledSlot : petStable->StabledPets)
+ {
+ if (stabledSlot)
+ {
+ PetStable::PetInfo const& pet = *stabledSlot;
+ WorldPackets::Pet::PetStableInfo& stableEntry = packet.Pets.emplace_back();
stableEntry.PetSlot = petSlot;
- stableEntry.PetNumber = fields[1].GetUInt32(); // petnumber
- stableEntry.CreatureID = fields[2].GetUInt32(); // creature entry
- stableEntry.DisplayID = fields[5].GetUInt32(); // creature displayid
- stableEntry.ExperienceLevel = fields[3].GetUInt16(); // level
+ stableEntry.PetNumber = pet.PetNumber;
+ stableEntry.CreatureID = pet.CreatureId;
+ stableEntry.DisplayID = pet.DisplayId;
+ stableEntry.ExperienceLevel = pet.Level;
stableEntry.PetFlags = PET_STABLE_INACTIVE;
- stableEntry.PetName = fields[4].GetString(); // Name
-
+ stableEntry.PetName = pet.Name;
++petSlot;
- packet.Pets.push_back(stableEntry);
}
- while (result->NextRow());
}
SendPacket(packet.Write());
@@ -452,55 +452,49 @@ void WorldSession::HandleStablePet(WorldPacket& recvData)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+ PetStable* petStable = GetPlayer()->GetPetStable();
+ if (!petStable)
+ return;
+
Pet* pet = _player->GetPet();
// can't place in stable dead pet
- if (!pet || !pet->IsAlive() || pet->getPetType() != HUNTER_PET)
+ if ((pet && (!pet->IsAlive() || pet->getPetType() != HUNTER_PET))
+ || (!pet && (petStable->UnslottedPets.size() != 1 || !petStable->UnslottedPets[0].Health || petStable->UnslottedPets[0].Type != HUNTER_PET)))
{
SendPetStableResult(StableResult::InternalError);
return;
}
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
-
- stmt->setUInt64(0, _player->GetGUID().GetCounter());
- stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
- stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
-
- _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1)));
-}
-
-void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
-{
- if (!GetPlayer())
- return;
-
- uint8 freeSlot = 1;
- if (result)
+ for (uint32 freeSlot = 0; freeSlot < petStable->MaxStabledPets; ++freeSlot)
{
- do
+ if (!petStable->StabledPets[freeSlot])
{
- Field* fields = result->Fetch();
-
- uint8 slot = fields[1].GetUInt8();
-
- // slots ordered in query, and if not equal then free
- if (slot != freeSlot)
- break;
-
- // this slot not free, skip
- ++freeSlot;
+ if (pet)
+ {
+ // stable summoned pet
+ _player->RemovePet(pet, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + freeSlot));
+ std::swap(petStable->StabledPets[freeSlot], petStable->CurrentPet);
+ SendPetStableResult(StableResult::StableSuccess);
+ return;
+ }
+
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, freeSlot);
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petStable->UnslottedPets[0].PetNumber);
+ CharacterDatabase.Execute(stmt);
+
+ // stable unsummoned pet
+ petStable->StabledPets[freeSlot] = std::move(petStable->UnslottedPets.back());
+ petStable->UnslottedPets.pop_back();
+ SendPetStableResult(StableResult::StableSuccess);
+ return;
}
- while (result->NextRow());
}
- if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
- {
- _player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
- SendPetStableResult(StableResult::StableSuccess);
- }
- else
- SendPetStableResult(StableResult::InvalidSlot);
+ // not free stable slot
+ SendPetStableResult(StableResult::InvalidSlot);
}
void WorldSession::HandleUnstablePet(WorldPacket& recvData)
@@ -520,35 +514,25 @@ void WorldSession::HandleUnstablePet(WorldPacket& recvData)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
-
- stmt->setUInt64(0, _player->GetGUID().GetCounter());
- stmt->setUInt32(1, petnumber);
- stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
- stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
-
- _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1)));
-}
-
-void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result)
-{
- if (!GetPlayer())
+ PetStable* petStable = GetPlayer()->GetPetStable();
+ if (!petStable)
+ {
+ SendPetStableResult(StableResult::InternalError);
return;
+ }
- uint32 petEntry = 0;
- if (result)
+ auto stabledPet = std::find_if(petStable->StabledPets.begin(), petStable->StabledPets.end(), [petnumber](Optional<PetStable::PetInfo> const& pet)
{
- Field* fields = result->Fetch();
- petEntry = fields[0].GetUInt32();
- }
+ return pet && pet->PetNumber == petnumber;
+ });
- if (!petEntry)
+ if (stabledPet == petStable->StabledPets.end())
{
SendPetStableResult(StableResult::InternalError);
return;
}
- CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
+ CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate((*stabledPet)->CreatureId);
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
{
// if problem in exotic pet
@@ -559,27 +543,72 @@ void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult r
return;
}
- Pet* pet = _player->GetPet();
- if (pet && pet->IsAlive())
+ Pet* oldPet = _player->GetPet();
+ if (oldPet)
+ {
+ // try performing a swap, client sends this packet instead of swap when starting from stabled slot
+ if (!oldPet->IsAlive() || !oldPet->IsHunterPet())
+ {
+ SendPetStableResult(StableResult::InternalError);
+ return;
+ }
+
+ _player->RemovePet(oldPet, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + std::distance(petStable->StabledPets.begin(), stabledPet)));
+ }
+ else if (petStable->UnslottedPets.size() == 1)
+ {
+ if (petStable->CurrentPet || !petStable->UnslottedPets[0].Health || petStable->UnslottedPets[0].Type != HUNTER_PET)
+ {
+ SendPetStableResult(StableResult::InternalError);
+ return;
+ }
+
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + std::distance(petStable->StabledPets.begin(), stabledPet)));
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petStable->UnslottedPets[0].PetNumber);
+ CharacterDatabase.Execute(stmt);
+
+ // move unsummoned pet into CurrentPet slot so that it gets moved into stable slot later
+ petStable->CurrentPet = std::move(petStable->UnslottedPets.back());
+ petStable->UnslottedPets.pop_back();
+ }
+ else if (petStable->CurrentPet || !petStable->UnslottedPets.empty())
{
SendPetStableResult(StableResult::InternalError);
return;
}
- // delete dead pet
- if (pet)
- _player->RemovePet(pet, PET_SAVE_AS_DELETED);
-
Pet* newPet = new Pet(_player, HUNTER_PET);
- if (!newPet->LoadPetFromDB(_player, petEntry, petId))
+ if (!newPet->LoadPetFromDB(_player, 0, petnumber, false))
{
delete newPet;
- newPet = nullptr;
+
+ petStable->UnslottedPets.push_back(std::move(*petStable->CurrentPet));
+ petStable->CurrentPet.reset();
+
+ // update current pet slot in db immediately to maintain slot consistency, dismissed pet was already saved
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, PET_SAVE_NOT_IN_SLOT);
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petnumber);
+ CharacterDatabase.Execute(stmt);
+
SendPetStableResult(StableResult::InternalError);
- return;
}
+ else
+ {
+ std::swap(*stabledPet, petStable->CurrentPet);
- SendPetStableResult(StableResult::UnstableSuccess);
+ // update current pet slot in db immediately to maintain slot consistency, dismissed pet was already saved
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, PET_SAVE_AS_CURRENT);
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petnumber);
+ CharacterDatabase.Execute(stmt);
+
+ SendPetStableResult(StableResult::UnstableSuccess);
+ }
}
void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
@@ -598,12 +627,13 @@ void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
- if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
+ PetStable& petStable = GetPlayer()->GetOrInitPetStable();
+ if (petStable.MaxStabledPets < MAX_PET_STABLES)
{
- /*StableSlotPricesEntry const* SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
+ /*StableSlotPricesEntry const* SlotPrice = sStableSlotPricesStore.LookupEntry(petStable.MaxStabledPets + 1);
if (_player->HasEnoughMoney(SlotPrice->Cost))
{
- ++GetPlayer()->m_stableSlots;
+ ++petStable.MaxStabledPets;
_player->ModifyMoney(-int32(SlotPrice->Cost));
SendPetStableResult(StableResult::BuySlotSuccess);
}
@@ -636,79 +666,97 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recvData)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
- Pet* pet = _player->GetPet();
-
- if (!pet || pet->getPetType() != HUNTER_PET)
+ PetStable* petStable = GetPlayer()->GetPetStable();
+ if (!petStable)
{
SendPetStableResult(StableResult::InternalError);
return;
}
// Find swapped pet slot in stable
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
-
- stmt->setUInt64(0, _player->GetGUID().GetCounter());
- stmt->setUInt32(1, petId);
-
- _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1)));
-}
-
-void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result)
-{
- if (!GetPlayer())
- return;
-
- if (!result)
+ auto stabledPet = std::find_if(petStable->StabledPets.begin(), petStable->StabledPets.end(), [petId](Optional<PetStable::PetInfo> const& pet)
{
- SendPetStableResult(StableResult::InternalError);
- return;
- }
-
- Field* fields = result->Fetch();
+ return pet && pet->PetNumber == petId;
+ });
- uint32 slot = fields[0].GetUInt8();
- uint32 petEntry = fields[1].GetUInt32();
-
- if (!petEntry)
+ if (stabledPet == petStable->StabledPets.end())
{
SendPetStableResult(StableResult::InternalError);
return;
}
- CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
+ CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate((*stabledPet)->CreatureId);
if (!creatureInfo || !creatureInfo->IsTameable(true))
{
SendPetStableResult(StableResult::InternalError);
return;
}
- if (!creatureInfo->IsTameable(_player->CanTameExoticPets()))
+ Pet* oldPet = _player->GetPet();
+ if (oldPet)
{
- SendPetStableResult(StableResult::CantControlExotic);
- return;
+ if (!oldPet->IsAlive() || !oldPet->IsHunterPet())
+ {
+ SendPetStableResult(StableResult::InternalError);
+ return;
+ }
+
+ _player->RemovePet(oldPet, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + std::distance(petStable->StabledPets.begin(), stabledPet)));
}
+ else if (petStable->UnslottedPets.size() == 1)
+ {
+ if (petStable->CurrentPet || !petStable->UnslottedPets[0].Health || petStable->UnslottedPets[0].Type != HUNTER_PET)
+ {
+ SendPetStableResult(StableResult::InternalError);
+ return;
+ }
- Pet* pet = _player->GetPet();
- // The player's pet could have been removed during the delay of the DB callback
- if (!pet)
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + std::distance(petStable->StabledPets.begin(), stabledPet)));
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petStable->UnslottedPets[0].PetNumber);
+ CharacterDatabase.Execute(stmt);
+
+ // move unsummoned pet into CurrentPet slot so that it gets moved into stable slot later
+ petStable->CurrentPet = std::move(petStable->UnslottedPets.back());
+ petStable->UnslottedPets.pop_back();
+ }
+ else if (petStable->CurrentPet || !petStable->UnslottedPets.empty())
{
SendPetStableResult(StableResult::InternalError);
return;
}
- // move alive pet to slot or delete dead pet
- _player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
-
// summon unstabled pet
- Pet* newPet = new Pet(_player);
- if (!newPet->LoadPetFromDB(_player, petEntry, petId))
+ Pet* newPet = new Pet(_player, HUNTER_PET);
+ if (!newPet->LoadPetFromDB(_player, 0, petId, false))
{
delete newPet;
SendPetStableResult(StableResult::InternalError);
+
+ petStable->UnslottedPets.push_back(std::move(*petStable->CurrentPet));
+ petStable->CurrentPet.reset();
+
+ // update current pet slot in db immediately to maintain slot consistency, dismissed pet was already saved
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, PET_SAVE_NOT_IN_SLOT);
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petId);
+ CharacterDatabase.Execute(stmt);
}
else
+ {
+ std::swap(*stabledPet, petStable->CurrentPet);
+
+ // update current pet slot in db immediately to maintain slot consistency, dismissed pet was already saved
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
+ stmt->setUInt8(0, PET_SAVE_AS_CURRENT);
+ stmt->setUInt64(1, _player->GetGUID().GetCounter());
+ stmt->setUInt32(2, petId);
+ CharacterDatabase.Execute(stmt);
+
SendPetStableResult(StableResult::UnstableSuccess);
+ }
}
void WorldSession::HandleRepairItemOpcode(WorldPackets::Item::RepairItem& packet)
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index c57920c7691..ecb60b8770d 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -532,11 +532,13 @@ void WorldSession::HandlePetRename(WorldPackets::Pet::PetRename& packet)
std::string name = packet.RenameData.NewName;
Optional<DeclinedName> const& declinedname = packet.RenameData.DeclinedNames;
+ PetStable* petStable = _player->GetPetStable();
Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
// check it!
if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
!pet->HasPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED) ||
- pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
+ pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ||
+ !petStable || !petStable->CurrentPet || petStable->CurrentPet->PetNumber != pet->GetCharmInfo()->GetPetNumber())
return;
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
@@ -558,6 +560,9 @@ void WorldSession::HandlePetRename(WorldPackets::Pet::PetRename& packet)
pet->RemovePetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED);
+ petStable->CurrentPet->Name = name;
+ petStable->CurrentPet->WasRenamed = true;
+
if (declinedname)
{
std::wstring wname;
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 193dea6100c..a32395fe64f 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -1065,7 +1065,6 @@ class TC_GAME_API WorldSession
// Pet
void SendQueryPetNameResponse(ObjectGuid guid);
void SendStablePet(ObjectGuid guid);
- void SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result);
void SendPetStableResult(StableResult result);
bool CheckStableMaster(ObjectGuid guid);
@@ -1405,13 +1404,10 @@ class TC_GAME_API WorldSession
void HandleBinderActivateOpcode(WorldPackets::NPC::Hello& packet);
void HandleRequestStabledPets(WorldPackets::NPC::RequestStabledPets& packet);
void HandleStablePet(WorldPacket& recvPacket);
- void HandleStablePetCallback(PreparedQueryResult result);
void HandleUnstablePet(WorldPacket& recvPacket);
- void HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result);
void HandleBuyStableSlot(WorldPacket& recvPacket);
void HandleStableRevivePet(WorldPacket& recvPacket);
void HandleStableSwapPet(WorldPacket& recvPacket);
- void HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result);
void HandleCanDuel(WorldPackets::Duel::CanDuel& packet);
void HandleDuelResponseOpcode(WorldPackets::Duel::DuelResponse& duelResponse);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index d1415153575..4fd9582b1e8 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -5994,6 +5994,36 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32
if (!unitCaster->GetCharmedGUID().IsEmpty())
return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ Player* playerCaster = unitCaster->ToPlayer();
+ if (playerCaster && playerCaster->GetPetStable())
+ {
+ std::pair<PetStable::PetInfo const*, PetSaveMode> info = Pet::GetLoadPetInfo(*playerCaster->GetPetStable(), spellEffectInfo.MiscValue, 0, false);
+ if (!info.first)
+ {
+ playerCaster->SendTameFailure(PetTameResult::NoPetAvailable);
+ return SPELL_FAILED_DONT_REPORT;
+ }
+
+ if (info.first->Type == HUNTER_PET && !info.first->Health)
+ {
+ playerCaster->SendTameFailure(PetTameResult::Dead);
+ return SPELL_FAILED_DONT_REPORT;
+ }
+
+ CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(info.first->CreatureId);
+ if (!creatureInfo || !creatureInfo->IsTameable(playerCaster->CanTameExoticPets()))
+ {
+ // if problem in exotic pet
+ if (creatureInfo && creatureInfo->IsTameable(true))
+ playerCaster->SendTameFailure(PetTameResult::CantControlExotic);
+ else
+ playerCaster->SendTameFailure(PetTameResult::NoPetAvailable);
+
+ return SPELL_FAILED_DONT_REPORT;
+ }
+ }
+
break;
}
case SPELL_EFFECT_SUMMON_PLAYER:
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 95d509b2c58..beef7e3c9c8 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2621,14 +2621,14 @@ void Spell::EffectSummonPet()
}
if (owner->GetTypeId() == TYPEID_PLAYER)
- owner->ToPlayer()->RemovePet(OldSummon, (OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT), false);
+ owner->ToPlayer()->RemovePet(OldSummon, PET_SAVE_NOT_IN_SLOT, false);
else
return;
}
float x, y, z;
owner->GetClosePoint(x, y, z, owner->GetCombatReach());
- Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET, 0, true);
+ Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET, 0);
if (!pet)
return;
diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp
index 7ca9de2c249..b23a71780af 100644
--- a/src/server/scripts/Commands/cs_pet.cpp
+++ b/src/server/scripts/Commands/cs_pet.cpp
@@ -91,42 +91,25 @@ public:
}
// Everything looks OK, create new pet
- Pet* pet = new Pet(player, HUNTER_PET);
- if (!pet->CreateBaseAtCreature(creatureTarget))
- {
- delete pet;
- handler->PSendSysMessage("Error 1");
- return false;
- }
+ Pet* pet = player->CreateTamedPetFrom(creatureTarget);
+ // "kill" original creature
creatureTarget->DespawnOrUnsummon();
- creatureTarget->SetHealth(0); // just for nice GM-mode view
- pet->SetCreatorGUID(player->GetGUID());
- pet->SetFaction(player->GetFaction());
- if (!pet->InitStatsForLevel(creatureTarget->GetLevel()))
- {
- TC_LOG_ERROR("misc", "InitStatsForLevel() in EffectTameCreature failed! Pet deleted.");
- handler->PSendSysMessage("Error 2");
- delete pet;
- return false;
- }
// prepare visual effect for levelup
- pet->SetLevel(creatureTarget->GetLevel() - 1);
-
- pet->GetCharmInfo()->SetPetNumber(sObjectMgr->GeneratePetNumber(), true);
- // this enables pet details window (Shift+P)
- pet->InitPetCreateSpells();
- pet->SetFullHealth();
+ pet->SetLevel(player->GetLevel() - 1);
+ // add to world
pet->GetMap()->AddToMap(pet->ToCreature());
// visual effect for levelup
- pet->SetLevel(creatureTarget->GetLevel());
+ pet->SetLevel(player->GetLevel());
+ // caster have pet now
player->SetMinion(pet, true);
+
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
player->PetSpellInitialize();
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index e3be7cf78ed..ef3197d7119 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -617,8 +617,8 @@ class spell_hun_tame_beast : public SpellScriptLoader
SpellCastResult CheckCast()
{
- Unit* caster = GetCaster();
- if (caster->GetTypeId() != TYPEID_PLAYER)
+ Player* caster = GetCaster()->ToPlayer();
+ if (!caster)
return SPELL_FAILED_DONT_REPORT;
if (!GetExplTargetUnit())
@@ -630,11 +630,21 @@ class spell_hun_tame_beast : public SpellScriptLoader
return SPELL_FAILED_HIGHLEVEL;
// use SMSG_PET_TAME_FAILURE?
- if (!target->GetCreatureTemplate()->IsTameable(caster->ToPlayer()->CanTameExoticPets()))
+ if (!target->GetCreatureTemplate()->IsTameable(caster->CanTameExoticPets()))
return SPELL_FAILED_BAD_TARGETS;
- if (!caster->GetPetGUID().IsEmpty())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+ PetStable const* petStable = caster->GetPetStable();
+ if (petStable)
+ {
+ if (petStable->CurrentPet)
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if (petStable->GetUnslottedHunterPet())
+ {
+ caster->SendTameFailure(PetTameResult::TooMany);
+ return SPELL_FAILED_DONT_REPORT;
+ }
+ }
if (!caster->GetCharmedGUID().IsEmpty())
return SPELL_FAILED_ALREADY_HAVE_CHARM;