feat(Core/Pets): Management refactoring (#9712)
* feat(Core/Pets): rework managment * 1 * 2 * 3 * 4 * 5 * cs pet * check before ressurect * pet DECLINED_NAMES * display - https://github.com/azerothcore/azerothcore-wotlk/issues/9297 * ArenaSpectator * 1
This commit is contained in:
@@ -540,16 +540,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
PrepareStatement(CHAR_DEL_CALENDAR_INVITE, "DELETE FROM calendar_invites WHERE id = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_DEL_CALENDAR_INVITE, "DELETE FROM calendar_invites WHERE id = ?", CONNECTION_ASYNC);
|
||||||
|
|
||||||
// Pet
|
// 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_CHAR_PET_IDS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
|
||||||
PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT owner, id, entry, level, name FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
|
|
||||||
PrepareStatement(CHAR_SEL_PET_ENTRY, "SELECT entry, slot 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_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC);
|
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_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_ADD_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_ADD_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", 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_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell 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, category, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, category, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||||
@@ -561,15 +556,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
|
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
|
||||||
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id, entry, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
|
|
||||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_ASYNC);
|
|
||||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_ASYNC);
|
|
||||||
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
|
|
||||||
PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_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_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC);
|
|
||||||
PrepareStatement(CHAR_UDP_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_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_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
|
||||||
|
|||||||
@@ -468,23 +468,12 @@ enum CharacterDatabaseStatements : uint32
|
|||||||
CHAR_DEL_PET_SPELLS,
|
CHAR_DEL_PET_SPELLS,
|
||||||
CHAR_DEL_CHAR_PET_BY_OWNER,
|
CHAR_DEL_CHAR_PET_BY_OWNER,
|
||||||
CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER,
|
CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER,
|
||||||
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT,
|
|
||||||
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS,
|
|
||||||
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_PETS,
|
CHAR_SEL_CHAR_PETS,
|
||||||
CHAR_SEL_CHAR_PET_BY_ENTRY,
|
CHAR_SEL_CHAR_PET_IDS,
|
||||||
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2,
|
|
||||||
CHAR_SEL_CHAR_PET_BY_SLOT,
|
|
||||||
CHAR_DEL_CHAR_PET_DECLINEDNAME,
|
CHAR_DEL_CHAR_PET_DECLINEDNAME,
|
||||||
CHAR_ADD_CHAR_PET_DECLINEDNAME,
|
CHAR_ADD_CHAR_PET_DECLINEDNAME,
|
||||||
|
CHAR_SEL_PET_DECLINED_NAME,
|
||||||
CHAR_UPD_CHAR_PET_NAME,
|
CHAR_UPD_CHAR_PET_NAME,
|
||||||
CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID,
|
|
||||||
CHAR_UDP_CHAR_PET_SLOT_BY_SLOT,
|
|
||||||
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
|
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
|
||||||
CHAR_DEL_CHAR_PET_BY_ID,
|
CHAR_DEL_CHAR_PET_BY_ID,
|
||||||
CHAR_DEL_CHAR_PET_BY_SLOT,
|
CHAR_DEL_CHAR_PET_BY_SLOT,
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt);
|
|||||||
DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
|
DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
|
||||||
DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
|
DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
|
||||||
TalentSpellPosMap sTalentSpellPosMap;
|
TalentSpellPosMap sTalentSpellPosMap;
|
||||||
|
std::unordered_set<uint32> sPetTalentSpells;
|
||||||
DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt);
|
DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt);
|
||||||
|
|
||||||
// store absolute bit position for first rank for talent inspect
|
// store absolute bit position for first rank for talent inspect
|
||||||
@@ -474,10 +475,23 @@ void LoadDBCStores(const std::string& dataPath)
|
|||||||
|
|
||||||
// create talent spells set
|
// create talent spells set
|
||||||
for (TalentEntry const* talentInfo : sTalentStore)
|
for (TalentEntry const* talentInfo : sTalentStore)
|
||||||
|
{
|
||||||
|
TalentTabEntry const* talentTab = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
|
||||||
|
|
||||||
for (uint8 j = 0; j < MAX_TALENT_RANK; ++j)
|
for (uint8 j = 0; j < MAX_TALENT_RANK; ++j)
|
||||||
|
{
|
||||||
if (talentInfo->RankID[j])
|
if (talentInfo->RankID[j])
|
||||||
|
{
|
||||||
sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(talentInfo->TalentID, j);
|
sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(talentInfo->TalentID, j);
|
||||||
|
|
||||||
|
if (talentTab && talentTab->petTalentMask)
|
||||||
|
{
|
||||||
|
sPetTalentSpells.insert(talentInfo->RankID[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// prepare fast data access to bit pos of talent ranks for use at inspecting
|
// prepare fast data access to bit pos of talent ranks for use at inspecting
|
||||||
{
|
{
|
||||||
// now have all max ranks (and then bit amount used for store talent ranks in inspect)
|
// now have all max ranks (and then bit amount used for store talent ranks in inspect)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "DBCStructure.h"
|
#include "DBCStructure.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
typedef std::list<uint32> SimpleFactionsList;
|
typedef std::list<uint32> SimpleFactionsList;
|
||||||
typedef std::vector<FlyByCamera> FlyByCameraCollection;
|
typedef std::vector<FlyByCamera> FlyByCameraCollection;
|
||||||
@@ -155,6 +156,7 @@ extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore;
|
|||||||
extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore;
|
extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore;
|
||||||
extern SpellCategoryStore sSpellsByCategoryStore;
|
extern SpellCategoryStore sSpellsByCategoryStore;
|
||||||
extern PetFamilySpellsStore sPetFamilySpellsStore;
|
extern PetFamilySpellsStore sPetFamilySpellsStore;
|
||||||
|
extern std::unordered_set<uint32> sPetTalentSpells;
|
||||||
extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore;
|
extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore;
|
||||||
extern DBCStorage <SpellRangeEntry> sSpellRangeStore;
|
extern DBCStorage <SpellRangeEntry> sSpellRangeStore;
|
||||||
extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore;
|
extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
#include "WaypointMovementGenerator.h"
|
#include "WaypointMovementGenerator.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
|
#include "Pet.h"
|
||||||
|
|
||||||
// TODO: this import is not necessary for compilation and marked as unused by the IDE
|
// TODO: this import is not necessary for compilation and marked as unused by the IDE
|
||||||
// however, for some reasons removing it would cause a damn linking issue
|
// however, for some reasons removing it would cause a damn linking issue
|
||||||
@@ -854,7 +855,7 @@ void Creature::Regenerate(Powers power)
|
|||||||
if (Powers((*i)->GetMiscValue()) == power)
|
if (Powers((*i)->GetMiscValue()) == power)
|
||||||
AddPct(addvalue, (*i)->GetAmount());
|
AddPct(addvalue, (*i)->GetAmount());
|
||||||
|
|
||||||
addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (power == POWER_FOCUS ? PET_FOCUS_REGEN_INTERVAL : CREATURE_REGEN_INTERVAL) / (5 * IN_MILLISECONDS);
|
addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (power == POWER_FOCUS ? PET_FOCUS_REGEN_INTERVAL.count() : CREATURE_REGEN_INTERVAL) / (5 * IN_MILLISECONDS);
|
||||||
|
|
||||||
ModifyPower(power, int32(addvalue));
|
ModifyPower(power, int32(addvalue));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,15 @@
|
|||||||
|
|
||||||
#define MAX_KILL_CREDIT 2
|
#define MAX_KILL_CREDIT 2
|
||||||
#define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS
|
#define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS
|
||||||
#define PET_FOCUS_REGEN_INTERVAL 4 * IN_MILLISECONDS
|
|
||||||
|
|
||||||
#define MAX_CREATURE_QUEST_ITEMS 6
|
#define MAX_CREATURE_QUEST_ITEMS 6
|
||||||
|
|
||||||
#define MAX_EQUIPMENT_ITEMS 3
|
#define MAX_EQUIPMENT_ITEMS 3
|
||||||
|
|
||||||
|
constexpr Milliseconds PET_FOCUS_REGEN_INTERVAL = 4s;
|
||||||
|
|
||||||
enum class VisibilityDistanceType : uint8;
|
enum class VisibilityDistanceType : uint8;
|
||||||
|
|
||||||
// TODO: Implement missing flags from TC in places that custom flags from xinef&pussywizzard use flag values.
|
// TODO: Implement missing flags from TC in places that custom flags from xinef&pussywizzard use flag values.
|
||||||
// EnumUtils: DESCRIBE THIS
|
// EnumUtils: DESCRIBE THIS
|
||||||
enum CreatureFlagsExtra : uint32
|
enum CreatureFlagsExtra : uint32
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,8 @@
|
|||||||
#include "PetDefines.h"
|
#include "PetDefines.h"
|
||||||
#include "TemporarySummon.h"
|
#include "TemporarySummon.h"
|
||||||
|
|
||||||
#define PET_FOCUS_REGEN_INTERVAL 4 * IN_MILLISECONDS
|
constexpr auto PET_LOSE_HAPPINES_INTERVAL = 7500;
|
||||||
#define PET_LOSE_HAPPINES_INTERVAL 7500
|
constexpr auto HAPPINESS_LEVEL_SIZE = 333000;
|
||||||
#define HAPPINESS_LEVEL_SIZE 333000
|
|
||||||
|
|
||||||
struct PetSpell
|
struct PetSpell
|
||||||
{
|
{
|
||||||
@@ -32,20 +31,6 @@ struct PetSpell
|
|||||||
PetSpellType type;
|
PetSpellType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsynchPetSummon
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AsynchPetSummon(uint32 entry, Position position, PetType petType, uint32 duration, uint32 createdBySpell, ObjectGuid casterGUID, int32 healthPct = 0) :
|
|
||||||
m_entry(entry), pos(position), m_petType(petType), m_duration(duration), m_createdBySpell(createdBySpell), m_casterGUID(casterGUID), m_healthPct(healthPct) { }
|
|
||||||
|
|
||||||
uint32 m_entry;
|
|
||||||
Position pos;
|
|
||||||
PetType m_petType;
|
|
||||||
uint32 m_duration, m_createdBySpell;
|
|
||||||
ObjectGuid m_casterGUID;
|
|
||||||
int32 m_healthPct;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::unordered_map<uint32, PetSpell> PetSpellMap;
|
typedef std::unordered_map<uint32, PetSpell> PetSpellMap;
|
||||||
typedef std::vector<uint32> AutoSpellList;
|
typedef std::vector<uint32> AutoSpellList;
|
||||||
|
|
||||||
@@ -55,7 +40,7 @@ class Pet : public Guardian
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Pet(Player* owner, PetType type = MAX_PET_TYPE);
|
explicit Pet(Player* owner, PetType type = MAX_PET_TYPE);
|
||||||
~Pet() override;
|
~Pet() override = default;
|
||||||
|
|
||||||
void AddToWorld() override;
|
void AddToWorld() override;
|
||||||
void RemoveFromWorld() override;
|
void RemoveFromWorld() override;
|
||||||
@@ -65,7 +50,7 @@ public:
|
|||||||
PetType getPetType() const { return m_petType; }
|
PetType getPetType() const { return m_petType; }
|
||||||
void setPetType(PetType type) { m_petType = type; }
|
void setPetType(PetType type) { m_petType = type; }
|
||||||
bool isControlled() const { return getPetType() == SUMMON_PET || getPetType() == HUNTER_PET; }
|
bool isControlled() const { return getPetType() == SUMMON_PET || getPetType() == HUNTER_PET; }
|
||||||
bool isTemporarySummoned() const { return m_duration > 0; }
|
bool isTemporarySummoned() const { return m_duration > 0s; }
|
||||||
|
|
||||||
bool IsPermanentPetFor(Player* owner) const; // pet have tab in character windows and set UNIT_FIELD_PETNUMBER
|
bool IsPermanentPetFor(Player* owner) const; // pet have tab in character windows and set UNIT_FIELD_PETNUMBER
|
||||||
|
|
||||||
@@ -73,10 +58,11 @@ public:
|
|||||||
bool CreateBaseAtCreature(Creature* creature);
|
bool CreateBaseAtCreature(Creature* creature);
|
||||||
bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner);
|
bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner);
|
||||||
bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phaseMask);
|
bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phaseMask);
|
||||||
static SpellCastResult TryLoadFromDB(Player* owner, bool current = false, PetType mandatoryPetType = MAX_PET_TYPE, bool checkDead = false);
|
static std::pair<PetStable::PetInfo const*, PetSaveMode> GetLoadPetInfo(PetStable const& stable, uint32 petEntry, uint32 petnumber, bool current);
|
||||||
static bool LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry = 0, uint32 petnumber = 0, bool current = false, AsynchPetSummon* info = nullptr);
|
bool LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current);
|
||||||
bool isBeingLoaded() const override { return m_loading; }
|
bool isBeingLoaded() const override { return m_loading; }
|
||||||
void SavePetToDB(PetSaveMode mode, bool logout);
|
void SavePetToDB(PetSaveMode mode);
|
||||||
|
void FillPetInfo(PetStable::PetInfo* petInfo) const;
|
||||||
void Remove(PetSaveMode mode, bool returnreagent = false);
|
void Remove(PetSaveMode mode, bool returnreagent = false);
|
||||||
static void DeleteFromDB(ObjectGuid::LowType guidlow);
|
static void DeleteFromDB(ObjectGuid::LowType guidlow);
|
||||||
|
|
||||||
@@ -99,19 +85,8 @@ public:
|
|||||||
void SynchronizeLevelWithOwner();
|
void SynchronizeLevelWithOwner();
|
||||||
bool HaveInDiet(ItemTemplate const* item) const;
|
bool HaveInDiet(ItemTemplate const* item) const;
|
||||||
uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel) const;
|
uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel) const;
|
||||||
void SetDuration(int32 dur) { m_duration = dur; }
|
void SetDuration(Milliseconds dur) { m_duration = dur; }
|
||||||
int32 GetDuration() const { return m_duration; }
|
Milliseconds GetDuration() const { return m_duration; }
|
||||||
|
|
||||||
/*
|
|
||||||
bool UpdateStats(Stats stat);
|
|
||||||
bool UpdateAllStats();
|
|
||||||
void UpdateResistances(uint32 school);
|
|
||||||
void UpdateArmor();
|
|
||||||
void UpdateMaxHealth();
|
|
||||||
void UpdateMaxPower(Powers power);
|
|
||||||
void UpdateAttackPowerAndDamage(bool ranged = false);
|
|
||||||
void UpdateDamagePhysical(WeaponAttackType attType);
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ToggleAutocast(SpellInfo const* spellInfo, bool apply);
|
void ToggleAutocast(SpellInfo const* spellInfo, bool apply);
|
||||||
|
|
||||||
@@ -124,8 +99,8 @@ public:
|
|||||||
void ClearCastWhenWillAvailable();
|
void ClearCastWhenWillAvailable();
|
||||||
void RemoveSpellCooldown(uint32 spell_id, bool update /* = false */);
|
void RemoveSpellCooldown(uint32 spell_id, bool update /* = false */);
|
||||||
|
|
||||||
void _SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout);
|
void _SaveSpellCooldowns(CharacterDatabaseTransaction trans);
|
||||||
void _SaveAuras(CharacterDatabaseTransaction trans, bool logout);
|
void _SaveAuras(CharacterDatabaseTransaction trans);
|
||||||
void _SaveSpells(CharacterDatabaseTransaction trans);
|
void _SaveSpells(CharacterDatabaseTransaction trans);
|
||||||
|
|
||||||
void _LoadSpellCooldowns(PreparedQueryResult result);
|
void _LoadSpellCooldowns(PreparedQueryResult result);
|
||||||
@@ -139,6 +114,7 @@ public:
|
|||||||
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||||
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||||
void CleanupActionBar();
|
void CleanupActionBar();
|
||||||
|
std::string GenerateActionBarData() const;
|
||||||
|
|
||||||
PetSpellMap m_spells;
|
PetSpellMap m_spells;
|
||||||
AutoSpellList m_autospells;
|
AutoSpellList m_autospells;
|
||||||
@@ -159,37 +135,30 @@ public:
|
|||||||
void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); }
|
void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); }
|
||||||
void ResetAuraUpdateMaskForRaid() { m_auraRaidUpdateMask = 0; }
|
void ResetAuraUpdateMaskForRaid() { m_auraRaidUpdateMask = 0; }
|
||||||
|
|
||||||
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)
|
bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved)
|
||||||
|
|
||||||
Player* GetOwner() const { return m_owner; }
|
Player* GetOwner() const;
|
||||||
void SetLoading(bool load) { m_loading = load; }
|
void SetLoading(bool load) { m_loading = load; }
|
||||||
void HandleAsynchLoadSucceed();
|
|
||||||
static void HandleAsynchLoadFailed(AsynchPetSummon* info, Player* player, uint8 asynchLoadType, uint8 loadResult);
|
|
||||||
uint8 GetAsynchLoadType() const { return asynchLoadType; }
|
|
||||||
void SetAsynchLoadType(uint8 type) { asynchLoadType = type; }
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasTempSpell() const { return m_tempspell != 0; }
|
[[nodiscard]] bool HasTempSpell() const { return m_tempspell != 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Player* m_owner;
|
Player* m_owner;
|
||||||
int32 m_happinessTimer;
|
int32 m_happinessTimer;
|
||||||
PetType m_petType;
|
PetType m_petType;
|
||||||
int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
|
Milliseconds m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
|
||||||
uint64 m_auraRaidUpdateMask;
|
uint64 m_auraRaidUpdateMask;
|
||||||
bool m_loading;
|
bool m_loading;
|
||||||
int32 m_petRegenTimer; // xinef: used for focus regeneration
|
Milliseconds m_petRegenTimer; // xinef: used for focus regeneration
|
||||||
|
|
||||||
DeclinedName* m_declinedname;
|
std::unique_ptr<DeclinedName> m_declinedname;
|
||||||
|
|
||||||
Unit* m_tempspellTarget;
|
Unit* m_tempspellTarget;
|
||||||
Unit* m_tempoldTarget;
|
Unit* m_tempoldTarget;
|
||||||
bool m_tempspellIsPositive;
|
bool m_tempspellIsPositive;
|
||||||
uint32 m_tempspell;
|
uint32 m_tempspell;
|
||||||
|
|
||||||
uint8 asynchLoadType;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SaveToDB(uint32, uint8, uint32) override // override of Creature::SaveToDB - must not be called
|
void SaveToDB(uint32, uint8, uint32) override // override of Creature::SaveToDB - must not be called
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,17 +18,25 @@
|
|||||||
#ifndef AZEROTHCORE_PET_DEFINES_H
|
#ifndef AZEROTHCORE_PET_DEFINES_H
|
||||||
#define AZEROTHCORE_PET_DEFINES_H
|
#define AZEROTHCORE_PET_DEFINES_H
|
||||||
|
|
||||||
enum PetType
|
#include "Define.h"
|
||||||
|
#include "Optional.h"
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
enum ReactStates : uint8;
|
||||||
|
|
||||||
|
enum PetType : uint8
|
||||||
{
|
{
|
||||||
SUMMON_PET = 0,
|
SUMMON_PET = 0,
|
||||||
HUNTER_PET = 1,
|
HUNTER_PET = 1,
|
||||||
MAX_PET_TYPE = 4
|
MAX_PET_TYPE = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_PET_STABLES 4
|
constexpr auto MAX_PET_STABLES = 4;
|
||||||
|
|
||||||
// stored in character_pet.slot
|
// stored in character_pet.slot
|
||||||
enum PetSaveMode
|
enum PetSaveMode : int8
|
||||||
{
|
{
|
||||||
PET_SAVE_AS_DELETED = -1, // not saved in fact
|
PET_SAVE_AS_DELETED = -1, // not saved in fact
|
||||||
PET_SAVE_AS_CURRENT = 0, // in current slot (with player)
|
PET_SAVE_AS_CURRENT = 0, // in current slot (with player)
|
||||||
@@ -73,24 +81,6 @@ enum PetTalk
|
|||||||
PET_TALK_ATTACK = 1
|
PET_TALK_ATTACK = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
// used at pet loading query list preparing, and later result selection
|
|
||||||
enum PetLoadQueryIndex
|
|
||||||
{
|
|
||||||
PET_LOAD_QUERY_LOADAURAS = 0,
|
|
||||||
PET_LOAD_QUERY_LOADSPELLS = 1,
|
|
||||||
PET_LOAD_QUERY_LOADSPELLCOOLDOWN = 2,
|
|
||||||
MAX_PET_LOAD_QUERY,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PetLoadStage
|
|
||||||
{
|
|
||||||
PET_LOAD_DEFAULT = 0,
|
|
||||||
PET_LOAD_HANDLE_UNSTABLE_CALLBACK = 1, // used also in HandleStableSwapPetCallback, uses same error / ok messages
|
|
||||||
PET_LOAD_BG_RESURRECT = 2,
|
|
||||||
PET_LOAD_SUMMON_PET = 3,
|
|
||||||
PET_LOAD_SUMMON_DEAD_PET = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PetLoadState
|
enum PetLoadState
|
||||||
{
|
{
|
||||||
PET_LOAD_OK = 0,
|
PET_LOAD_OK = 0,
|
||||||
@@ -202,4 +192,39 @@ enum PetScalingSpells
|
|||||||
#define PET_FOLLOW_DIST 1.0f
|
#define PET_FOLLOW_DIST 1.0f
|
||||||
#define PET_FOLLOW_ANGLE (M_PI/2)
|
#define PET_FOLLOW_ANGLE (M_PI/2)
|
||||||
|
|
||||||
|
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 Happiness = 0;
|
||||||
|
uint32 LastSaveTime = 0;
|
||||||
|
uint32 CreatedBySpellId = 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
|
#endif
|
||||||
|
|||||||
@@ -282,8 +282,6 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
|
|||||||
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
|
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
|
||||||
m_forced_speed_changes[i] = 0;
|
m_forced_speed_changes[i] = 0;
|
||||||
|
|
||||||
m_stableSlots = 0;
|
|
||||||
|
|
||||||
/////////////////// Instance System /////////////////////
|
/////////////////// Instance System /////////////////////
|
||||||
|
|
||||||
m_HomebindTimer = 0;
|
m_HomebindTimer = 0;
|
||||||
@@ -3969,7 +3967,7 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up
|
|||||||
|
|
||||||
// Unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
|
// 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
|
// 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->setUInt32(0, lowGuid);
|
stmt->setUInt32(0, lowGuid);
|
||||||
PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
|
PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
|
||||||
|
|
||||||
@@ -8703,7 +8701,18 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
|
|||||||
pet->CombatStop();
|
pet->CombatStop();
|
||||||
|
|
||||||
// only if current pet in slot
|
// only if current pet in slot
|
||||||
pet->SavePetToDB(mode, true);
|
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);
|
SetMinion(pet, false);
|
||||||
|
|
||||||
@@ -8898,7 +8907,7 @@ void Player::PetSpellInitialize()
|
|||||||
WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1);
|
WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1);
|
||||||
data << pet->GetGUID();
|
data << pet->GetGUID();
|
||||||
data << uint16(pet->GetCreatureTemplate()->family); // creature family (required for pet talents)
|
data << uint16(pet->GetCreatureTemplate()->family); // creature family (required for pet talents)
|
||||||
data << uint32(pet->GetDuration());
|
data << uint32(pet->GetDuration().count());
|
||||||
data << uint8(pet->GetReactState());
|
data << uint8(pet->GetReactState());
|
||||||
data << uint8(charmInfo->GetCommandState());
|
data << uint8(charmInfo->GetCommandState());
|
||||||
data << uint16(0); // Flags, mostly unknown
|
data << uint16(0); // Flags, mostly unknown
|
||||||
@@ -13396,8 +13405,11 @@ void Player::ResummonPetTemporaryUnSummonedIfAny()
|
|||||||
if (!CanResummonPet(GetLastPetSpell()))
|
if (!CanResummonPet(GetLastPetSpell()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Pet::LoadPetFromDB(this, PET_LOAD_SUMMON_PET, 0, m_temporaryUnsummonedPetNumber, true);
|
Pet* newPet = new Pet(this);
|
||||||
//m_temporaryUnsummonedPetNumber = 0;
|
if (!newPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
|
||||||
|
delete newPet;
|
||||||
|
|
||||||
|
m_temporaryUnsummonedPetNumber = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::CanResummonPet(uint32 spellid)
|
bool Player::CanResummonPet(uint32 spellid)
|
||||||
@@ -13878,7 +13890,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
|||||||
stmt->setUInt32(index++, m_resetTalentsCost);
|
stmt->setUInt32(index++, m_resetTalentsCost);
|
||||||
stmt->setUInt32(index++, uint32(m_resetTalentsTime));
|
stmt->setUInt32(index++, uint32(m_resetTalentsTime));
|
||||||
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
|
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++, (uint16)m_atLoginFlags);
|
||||||
stmt->setUInt16(index++, GetZoneId());
|
stmt->setUInt16(index++, GetZoneId());
|
||||||
stmt->setUInt32(index++, uint32(m_deathExpireTime));
|
stmt->setUInt32(index++, uint32(m_deathExpireTime));
|
||||||
@@ -14017,7 +14029,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
|||||||
stmt->setUInt32(index++, m_resetTalentsCost);
|
stmt->setUInt32(index++, m_resetTalentsCost);
|
||||||
stmt->setUInt32(index++, uint32(m_resetTalentsTime));
|
stmt->setUInt32(index++, uint32(m_resetTalentsTime));
|
||||||
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
|
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++, (uint16)m_atLoginFlags);
|
||||||
stmt->setUInt16(index++, GetZoneId());
|
stmt->setUInt16(index++, GetZoneId());
|
||||||
stmt->setUInt32(index++, uint32(m_deathExpireTime));
|
stmt->setUInt32(index++, uint32(m_deathExpireTime));
|
||||||
@@ -14468,6 +14480,7 @@ void Player::SetReputation(uint32 factionentry, uint32 value)
|
|||||||
{
|
{
|
||||||
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value);
|
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Player::GetReputation(uint32 factionentry) const
|
uint32 Player::GetReputation(uint32 factionentry) const
|
||||||
{
|
{
|
||||||
return GetReputationMgr().GetReputation(sFactionStore.LookupEntry(factionentry));
|
return GetReputationMgr().GetReputation(sFactionStore.LookupEntry(factionentry));
|
||||||
@@ -14701,6 +14714,14 @@ bool Player::AddItem(uint32 itemId, uint32 count)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PetStable& Player::GetOrInitPetStable()
|
||||||
|
{
|
||||||
|
if (!m_petStable)
|
||||||
|
m_petStable = std::make_unique<PetStable>();
|
||||||
|
|
||||||
|
return *m_petStable;
|
||||||
|
}
|
||||||
|
|
||||||
void Player::RefundItem(Item* item)
|
void Player::RefundItem(Item* item)
|
||||||
{
|
{
|
||||||
if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE))
|
if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE))
|
||||||
@@ -14933,6 +14954,58 @@ void Player::_LoadBrewOfTheMonth(PreparedQueryResult result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
FMT_LOG_ERROR("entities.player", "Player::LoadFromDB: Player ({}) can't have more stable slots than {}, but has {} in DB",
|
||||||
|
GetGUID().ToString(), 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, curhappiness, abdata, savetime, CreatedBySpell, PetType 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.Happiness = fields[11].GetUInt32();
|
||||||
|
petInfo.ActionBar = fields[12].GetString();
|
||||||
|
petInfo.LastSaveTime = fields[13].GetUInt32();
|
||||||
|
petInfo.CreatedBySpellId = fields[14].GetUInt32();
|
||||||
|
petInfo.Type = PetType(fields[15].GetUInt8());
|
||||||
|
|
||||||
|
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)
|
void Player::_SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans)
|
||||||
{
|
{
|
||||||
if (_instanceResetTimes.empty())
|
if (_instanceResetTimes.empty())
|
||||||
@@ -15119,30 +15192,134 @@ Guild* Player::GetGuild() const
|
|||||||
return guildId ? sGuildMgr->GetGuildById(guildId) : nullptr;
|
return guildId ? sGuildMgr->GetGuildById(guildId) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration, uint32 createdBySpell, ObjectGuid casterGUID, uint8 asynchLoadType, int32 healthPct /*= 0*/)
|
Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, Milliseconds duration /*= 0s*/)
|
||||||
{
|
{
|
||||||
Position pos = {x, y, z, ang};
|
PetStable& petStable = GetOrInitPetStable();
|
||||||
if (!pos.IsPositionValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
AsynchPetSummon* asynchPetInfo = new AsynchPetSummon(entry, pos, petType, duration, createdBySpell, casterGUID, healthPct);
|
Pet* pet = new Pet(this, petType);
|
||||||
Pet::LoadPetFromDB(this, asynchLoadType, entry, 0, false, asynchPetInfo);
|
|
||||||
|
if (petType == SUMMON_PET && pet->LoadPetFromDB(this, entry, 0, false))
|
||||||
|
{
|
||||||
|
// Remove Demonic Sacrifice auras (known pet)
|
||||||
|
Unit::AuraEffectList const& auraClassScripts = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
|
||||||
|
for (Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin(); itr != auraClassScripts.end();)
|
||||||
|
{
|
||||||
|
if ((*itr)->GetMiscValue() == 2228)
|
||||||
|
{
|
||||||
|
RemoveAurasDueToSpell((*itr)->GetId());
|
||||||
|
itr = auraClassScripts.begin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++itr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::IsPetDismissed()
|
if (duration > 0s)
|
||||||
|
pet->SetDuration(duration);
|
||||||
|
|
||||||
|
// Generate a new name for the newly summoned ghoul
|
||||||
|
if (pet->IsPetGhoul())
|
||||||
{
|
{
|
||||||
/*
|
std::string new_name = sObjectMgr->GeneratePetName(entry);
|
||||||
* Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever
|
if (!new_name.empty())
|
||||||
* Changes the slot flag, they will break this validation.
|
pet->SetName(new_name);
|
||||||
*/
|
}
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS);
|
|
||||||
stmt->setUInt32(0, GetGUID().GetCounter());
|
|
||||||
stmt->setUInt8(1, uint8(PET_SAVE_NOT_IN_SLOT));
|
|
||||||
|
|
||||||
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
return nullptr;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
return false;
|
// petentry == 0 for hunter "call pet" (current pet summoned if any)
|
||||||
|
if (!entry)
|
||||||
|
{
|
||||||
|
delete pet;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pet->Relocate(x, y, z, ang);
|
||||||
|
if (!pet->IsPositionValid())
|
||||||
|
{
|
||||||
|
LOG_ERROR("misc", "Player::SummonPet: Pet (%s, Entry: %d) not summoned. Suggested coordinates aren't valid (X: %f Y: %f)", pet->GetGUID().ToString().c_str(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY());
|
||||||
|
delete pet;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map* map = GetMap();
|
||||||
|
uint32 pet_number = sObjectMgr->GeneratePetNumber();
|
||||||
|
if (!pet->Create(map->GenerateLowGuid<HighGuid::Pet>(), map, GetPhaseMask(), entry, pet_number))
|
||||||
|
{
|
||||||
|
LOG_ERROR("misc", "Player::SummonPet: No such creature entry %u", entry);
|
||||||
|
delete pet;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (petType == SUMMON_PET && petStable.CurrentPet)
|
||||||
|
RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
|
||||||
|
|
||||||
|
pet->SetCreatorGUID(GetGUID());
|
||||||
|
pet->SetFaction(GetFaction());
|
||||||
|
pet->setPowerType(POWER_MANA);
|
||||||
|
pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
|
||||||
|
pet->InitStatsForLevel(getLevel());
|
||||||
|
|
||||||
|
SetMinion(pet, true);
|
||||||
|
|
||||||
|
switch (petType)
|
||||||
|
{
|
||||||
|
case SUMMON_PET:
|
||||||
|
{
|
||||||
|
if (pet->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON || pet->GetCreatureTemplate()->type == CREATURE_TYPE_UNDEAD)
|
||||||
|
pet->GetCharmInfo()->SetPetNumber(pet_number, true); // Show pet details tab (Shift+P) only for demons & undead
|
||||||
|
else
|
||||||
|
pet->GetCharmInfo()->SetPetNumber(pet_number, false);
|
||||||
|
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048);
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
|
||||||
|
pet->SetFullHealth();
|
||||||
|
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped in this case
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
map->AddToMap(pet->ToCreature(), true);
|
||||||
|
|
||||||
|
ASSERT(!petStable.CurrentPet && (petType != HUNTER_PET || !petStable.GetUnslottedHunterPet()));
|
||||||
|
pet->FillPetInfo(&petStable.CurrentPet.emplace());
|
||||||
|
|
||||||
|
if (petType == SUMMON_PET)
|
||||||
|
{
|
||||||
|
pet->InitPetCreateSpells();
|
||||||
|
pet->InitTalentForLevel();
|
||||||
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
|
PetSpellInitialize();
|
||||||
|
|
||||||
|
// Remove Demonic Sacrifice auras (known pet)
|
||||||
|
Unit::AuraEffectList const& auraClassScripts = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
|
||||||
|
for (Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin(); itr != auraClassScripts.end();)
|
||||||
|
{
|
||||||
|
if ((*itr)->GetMiscValue() == 2228)
|
||||||
|
{
|
||||||
|
RemoveAurasDueToSpell((*itr)->GetId());
|
||||||
|
itr = auraClassScripts.begin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duration > 0s)
|
||||||
|
pet->SetDuration(duration);
|
||||||
|
|
||||||
|
if (NeedSendSpectatorData() && pet->GetCreatureTemplate()->family)
|
||||||
|
{
|
||||||
|
ArenaSpectator::SendCommand_UInt32Value(FindMap(), GetGUID(), "PHP", (uint32)pet->GetHealthPct());
|
||||||
|
ArenaSpectator::SendCommand_UInt32Value(FindMap(), GetGUID(), "PET", pet->GetCreatureTemplate()->family);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pet;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Player::GetSpec(int8 spec)
|
uint32 Player::GetSpec(int8 spec)
|
||||||
|
|||||||
@@ -877,6 +877,7 @@ enum PlayerLoginQueryIndex
|
|||||||
PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH = 34,
|
PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH = 34,
|
||||||
PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 35,
|
PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 35,
|
||||||
PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS = 36,
|
PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS = 36,
|
||||||
|
PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS = 37,
|
||||||
MAX_PLAYER_LOGIN_QUERY
|
MAX_PLAYER_LOGIN_QUERY
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1162,9 +1163,12 @@ public:
|
|||||||
void RemoveRestFlag(RestFlag restFlag);
|
void RemoveRestFlag(RestFlag restFlag);
|
||||||
[[nodiscard]] uint32 GetInnTriggerId() const { return _innTriggerId; }
|
[[nodiscard]] uint32 GetInnTriggerId() const { return _innTriggerId; }
|
||||||
|
|
||||||
|
PetStable* GetPetStable() { return m_petStable.get(); }
|
||||||
|
PetStable& GetOrInitPetStable();
|
||||||
|
PetStable const* GetPetStable() const { return m_petStable.get(); }
|
||||||
|
|
||||||
[[nodiscard]] Pet* GetPet() const;
|
[[nodiscard]] Pet* GetPet() const;
|
||||||
bool IsPetDismissed();
|
Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, Milliseconds duration = 0s);
|
||||||
void SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime, uint32 createdBySpell, ObjectGuid casterGUID, uint8 asynchLoadType, int32 healthPct = 0);
|
|
||||||
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
|
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
|
||||||
[[nodiscard]] uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn
|
[[nodiscard]] uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn
|
||||||
|
|
||||||
@@ -1345,8 +1349,6 @@ public:
|
|||||||
|
|
||||||
bool AddItem(uint32 itemId, uint32 count);
|
bool AddItem(uint32 itemId, uint32 count);
|
||||||
|
|
||||||
uint32 m_stableSlots;
|
|
||||||
|
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
/*** GOSSIP SYSTEM ***/
|
/*** GOSSIP SYSTEM ***/
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
@@ -2684,6 +2686,7 @@ public:
|
|||||||
void _LoadInstanceTimeRestrictions(PreparedQueryResult result);
|
void _LoadInstanceTimeRestrictions(PreparedQueryResult result);
|
||||||
void _LoadBrewOfTheMonth(PreparedQueryResult result);
|
void _LoadBrewOfTheMonth(PreparedQueryResult result);
|
||||||
void _LoadCharacterSettings(PreparedQueryResult result);
|
void _LoadCharacterSettings(PreparedQueryResult result);
|
||||||
|
void _LoadPetStable(uint8 petStableSlots, PreparedQueryResult result);
|
||||||
|
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
/*** SAVE SYSTEM ***/
|
/*** SAVE SYSTEM ***/
|
||||||
@@ -2923,6 +2926,8 @@ private:
|
|||||||
bool m_bMustDelayTeleport;
|
bool m_bMustDelayTeleport;
|
||||||
bool m_bHasDelayedTeleport;
|
bool m_bHasDelayedTeleport;
|
||||||
|
|
||||||
|
std::unique_ptr<PetStable> m_petStable;
|
||||||
|
|
||||||
// Temporary removed pet cache
|
// Temporary removed pet cache
|
||||||
uint32 m_temporaryUnsummonedPetNumber;
|
uint32 m_temporaryUnsummonedPetNumber;
|
||||||
uint32 m_oldpetspell;
|
uint32 m_oldpetspell;
|
||||||
|
|||||||
@@ -5375,12 +5375,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
|
|
||||||
uint32 extraflags = fields[36].GetUInt16();
|
uint32 extraflags = fields[36].GetUInt16();
|
||||||
|
|
||||||
m_stableSlots = fields[37].GetUInt8();
|
_LoadPetStable(fields[37].GetUInt8(), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS));
|
||||||
if (m_stableSlots > MAX_PET_STABLES)
|
|
||||||
{
|
|
||||||
LOG_ERROR("entities.player", "Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots));
|
|
||||||
m_stableSlots = MAX_PET_STABLES;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_atLoginFlags = fields[38].GetUInt16();
|
m_atLoginFlags = fields[38].GetUInt16();
|
||||||
|
|
||||||
@@ -6293,9 +6288,11 @@ void Player::LoadPet()
|
|||||||
{
|
{
|
||||||
//fixme: the pet should still be loaded if the player is not in world
|
//fixme: the pet should still be loaded if the player is not in world
|
||||||
// just not added to the map
|
// just not added to the map
|
||||||
if (IsInWorld())
|
if (m_petStable && IsInWorld())
|
||||||
{
|
{
|
||||||
Pet::LoadPetFromDB(this, PET_LOAD_SUMMON_PET, 0, 0, true);
|
Pet* pet = new Pet(this);
|
||||||
|
if (!pet->LoadPetFromDB(this, 0, 0, true))
|
||||||
|
delete pet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7177,7 +7174,7 @@ void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create, bool logo
|
|||||||
|
|
||||||
// save pet (hunter pet level and experience and all type pets health/mana).
|
// save pet (hunter pet level and experience and all type pets health/mana).
|
||||||
if (Pet* pet = GetPet())
|
if (Pet* pet = GetPet())
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, logout);
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fast save function for item/money cheating preventing - save only inventory and money state
|
// fast save function for item/money cheating preventing - save only inventory and money state
|
||||||
|
|||||||
@@ -16609,11 +16609,10 @@ bool Unit::IsPetAura(Aura const* aura)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// if the owner has that pet aura, return true
|
// if the owner has that pet aura, return true
|
||||||
for (PetAuraSet::const_iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); ++itr)
|
for (PetAura const* petAura : owner->m_petAuras)
|
||||||
{
|
if (petAura->GetAura(GetEntry()) == aura->GetId())
|
||||||
if ((*itr)->GetAura(GetEntry()) == aura->GetId())
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16632,7 +16631,11 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id)
|
|||||||
|
|
||||||
uint8 level = creatureTarget->getLevel() + 5 < getLevel() ? (getLevel() - 5) : creatureTarget->getLevel();
|
uint8 level = creatureTarget->getLevel() + 5 < getLevel() ? (getLevel() - 5) : creatureTarget->getLevel();
|
||||||
|
|
||||||
InitTamedPet(pet, level, spell_id);
|
if (!InitTamedPet(pet, level, spell_id))
|
||||||
|
{
|
||||||
|
delete pet;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return pet;
|
return pet;
|
||||||
}
|
}
|
||||||
@@ -16659,6 +16662,11 @@ Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id)
|
|||||||
|
|
||||||
bool Unit::InitTamedPet(Pet* pet, uint8 level, 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->SetCreatorGUID(GetGUID());
|
||||||
pet->SetFaction(GetFaction());
|
pet->SetFaction(GetFaction());
|
||||||
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, spell_id);
|
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, spell_id);
|
||||||
@@ -16676,6 +16684,7 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
|
|||||||
// this enables pet details window (Shift+P)
|
// this enables pet details window (Shift+P)
|
||||||
pet->InitPetCreateSpells();
|
pet->InitPetCreateSpells();
|
||||||
pet->SetFullHealth();
|
pet->SetFullHealth();
|
||||||
|
pet->FillPetInfo(&petStable.CurrentPet.emplace());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1146,7 +1146,7 @@ enum ActiveStates
|
|||||||
ACT_DECIDE = 0x00 // custom
|
ACT_DECIDE = 0x00 // custom
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ReactStates
|
enum ReactStates : uint8
|
||||||
{
|
{
|
||||||
REACT_PASSIVE = 0,
|
REACT_PASSIVE = 0,
|
||||||
REACT_DEFENSIVE = 1,
|
REACT_DEFENSIVE = 1,
|
||||||
|
|||||||
@@ -202,13 +202,17 @@ bool LoginQueryHolder::Initialize()
|
|||||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES, stmt);
|
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES, stmt);
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_LOCATION);
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_LOCATION);
|
||||||
stmt->setUInt64(0, lowGuid);
|
stmt->setUInt32(0, lowGuid);
|
||||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt);
|
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt);
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS);
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS);
|
||||||
stmt->setUInt64(0, lowGuid);
|
stmt->setUInt32(0, lowGuid);
|
||||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS, stmt);
|
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS, stmt);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PETS);
|
||||||
|
stmt->setUInt32(0, lowGuid);
|
||||||
|
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS, stmt);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -488,74 +488,61 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData)
|
|||||||
|
|
||||||
void WorldSession::SendStablePet(ObjectGuid guid)
|
void WorldSession::SendStablePet(ObjectGuid guid)
|
||||||
{
|
{
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
|
|
||||||
|
|
||||||
stmt->setUInt32(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;
|
|
||||||
|
|
||||||
LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
|
LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
|
||||||
|
|
||||||
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
|
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
|
||||||
data << guid;
|
data << guid;
|
||||||
size_t wpos = data.wpos();
|
size_t wpos = data.wpos();
|
||||||
data << uint8(0); // place holder for slot show number
|
data << uint8(0); // place holder for slot show number
|
||||||
data << uint8(GetPlayer()->m_stableSlots);
|
|
||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
PetStable* petStable = GetPlayer()->GetPetStable();
|
||||||
|
if (!petStable)
|
||||||
|
{
|
||||||
|
data << uint8(0); // stable slots
|
||||||
|
SendPacket(&data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data << uint8(petStable->MaxStabledPets);
|
||||||
|
|
||||||
uint8 num = 0; // counter for place holder
|
uint8 num = 0; // counter for place holder
|
||||||
|
|
||||||
// not let move dead pet in slot
|
// not let move dead pet in slot
|
||||||
if (pet && pet->IsAlive() && pet->getPetType() == HUNTER_PET)
|
if (petStable->CurrentPet)
|
||||||
{
|
{
|
||||||
data << uint32(pet->GetCharmInfo()->GetPetNumber());
|
PetStable::PetInfo const& pet = *petStable->CurrentPet;
|
||||||
data << uint32(pet->GetEntry());
|
data << uint32(pet.PetNumber);
|
||||||
data << uint32(pet->getLevel());
|
data << uint32(pet.CreatureId);
|
||||||
data << pet->GetName(); // petname
|
data << uint32(pet.Level);
|
||||||
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
|
data << pet.Name; // petname
|
||||||
|
data << uint8(1); // flags: 1 active, 2 inactive
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
else
|
||||||
{
|
{
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS);
|
if (PetStable::PetInfo const* pet = petStable->GetUnslottedHunterPet())
|
||||||
stmt->setUInt32(0, _player->GetGUID().GetCounter());
|
|
||||||
stmt->setUInt8(1, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
|
||||||
|
|
||||||
if (PreparedQueryResult _result = CharacterDatabase.Query(stmt))
|
|
||||||
{
|
{
|
||||||
Field* fields = _result->Fetch();
|
data << uint32(pet->PetNumber);
|
||||||
|
data << uint32(pet->CreatureId);
|
||||||
data << uint32(fields[0].GetUInt32()); // id
|
data << uint32(pet->Level);
|
||||||
data << uint32(fields[1].GetUInt32()); // entry
|
data << pet->Name; // petname
|
||||||
data << uint32(fields[4].GetUInt16()); // level
|
data << uint8(1); // flags: 1 active, 2 inactive
|
||||||
data << fields[8].GetString(); // petname
|
|
||||||
data << uint8(1);
|
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result)
|
for (Optional<PetStable::PetInfo> const& stabledSlot : petStable->StabledPets)
|
||||||
{
|
{
|
||||||
do
|
if (stabledSlot)
|
||||||
{
|
{
|
||||||
Field* fields = result->Fetch();
|
PetStable::PetInfo const& pet = *stabledSlot;
|
||||||
|
data << uint32(pet.PetNumber);
|
||||||
data << uint32(fields[1].GetUInt32()); // petnumber
|
data << uint32(pet.CreatureId);
|
||||||
data << uint32(fields[2].GetUInt32()); // creature entry
|
data << uint32(pet.Level);
|
||||||
data << uint32(fields[3].GetUInt16()); // level
|
data << pet.Name; // petname
|
||||||
data << fields[4].GetString(); // name
|
data << uint8(2); // flags: 1 active, 2 inactive
|
||||||
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
|
|
||||||
|
|
||||||
++num;
|
++num;
|
||||||
} while (result->NextRow());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.put<uint8>(wpos, num); // set real data to placeholder
|
data.put<uint8>(wpos, num); // set real data to placeholder
|
||||||
@@ -592,80 +579,48 @@ void WorldSession::HandleStablePet(WorldPacket& recvData)
|
|||||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||||
|
|
||||||
|
PetStable* petStable = GetPlayer()->GetPetStable();
|
||||||
|
if (!petStable)
|
||||||
|
return;
|
||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
Pet* pet = _player->GetPet();
|
||||||
|
|
||||||
// can't place in stable dead pet
|
// can't place in stable dead pet
|
||||||
|
if ((pet && (!pet->IsAlive() || pet->getPetType() != HUNTER_PET))
|
||||||
|
|| (!pet && (petStable->UnslottedPets.size() != 1 || !petStable->UnslottedPets[0].Health || petStable->UnslottedPets[0].Type != HUNTER_PET)))
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32 freeSlot = 0; freeSlot < petStable->MaxStabledPets; ++freeSlot)
|
||||||
|
{
|
||||||
|
if (!petStable->StabledPets[freeSlot])
|
||||||
|
{
|
||||||
if (pet)
|
if (pet)
|
||||||
{
|
{
|
||||||
if (!pet->IsAlive() || pet->getPetType() != HUNTER_PET)
|
// stable summoned pet
|
||||||
{
|
_player->RemovePet(pet, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + freeSlot));
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
std::swap(petStable->StabledPets[freeSlot], petStable->CurrentPet);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
|
|
||||||
if (loadResult != SPELL_CAST_OK)
|
|
||||||
{
|
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
|
|
||||||
|
|
||||||
stmt->setUInt32(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)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
} while (result->NextRow());
|
|
||||||
}
|
|
||||||
|
|
||||||
WorldPacket data(SMSG_STABLE_RESULT, 1);
|
|
||||||
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
|
|
||||||
{
|
|
||||||
if (_player->GetPetGUID())
|
|
||||||
{
|
|
||||||
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
|
|
||||||
SendStableResult(STABLE_SUCCESS_STABLE);
|
SendStableResult(STABLE_SUCCESS_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
|
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
|
||||||
stmt->setUInt8(0, freeSlot);
|
stmt->setUInt8(0, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + freeSlot));
|
||||||
stmt->setUInt32(1, _player->GetGUID().GetCounter());
|
stmt->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
stmt->setUInt32(2, petStable->UnslottedPets[0].PetNumber);
|
||||||
CharacterDatabase.Execute(stmt);
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
_player->SetTemporaryUnsummonedPetNumber(0);
|
// stable unsummoned pet
|
||||||
|
petStable->StabledPets[freeSlot] = std::move(petStable->UnslottedPets.back());
|
||||||
|
petStable->UnslottedPets.pop_back();
|
||||||
SendStableResult(STABLE_SUCCESS_STABLE);
|
SendStableResult(STABLE_SUCCESS_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
// not free stable slot
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,37 +642,25 @@ void WorldSession::HandleUnstablePet(WorldPacket& recvData)
|
|||||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||||
|
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
|
PetStable* petStable = GetPlayer()->GetPetStable();
|
||||||
|
if (!petStable)
|
||||||
stmt->setUInt32(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())
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32 petEntry = 0;
|
|
||||||
uint32 slot = 0;
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
Field* fields = result->Fetch();
|
|
||||||
petEntry = fields[0].GetUInt32();
|
|
||||||
slot = fields[1].GetUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!petEntry)
|
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
|
auto stabledPet = std::find_if(petStable->StabledPets.begin(), petStable->StabledPets.end(), [petnumber](Optional<PetStable::PetInfo> const& pet)
|
||||||
|
{
|
||||||
|
return pet && pet->PetNumber == petnumber;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stabledPet == petStable->StabledPets.end())
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate((*stabledPet)->CreatureId);
|
||||||
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
|
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
|
||||||
{
|
{
|
||||||
// if problem in exotic pet
|
// if problem in exotic pet
|
||||||
@@ -725,45 +668,74 @@ void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult r
|
|||||||
SendStableResult(STABLE_ERR_EXOTIC);
|
SendStableResult(STABLE_ERR_EXOTIC);
|
||||||
else
|
else
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
Pet* oldPet = _player->GetPet();
|
||||||
if (pet && pet->IsAlive())
|
if (oldPet)
|
||||||
|
{
|
||||||
|
// try performing a swap, client sends this packet instead of swap when starting from stabled slot
|
||||||
|
if (!oldPet->IsAlive() || !oldPet->IsHunterPet())
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete dead pet
|
_player->RemovePet(oldPet, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + std::distance(petStable->StabledPets.begin(), stabledPet)));
|
||||||
if (pet)
|
|
||||||
{
|
|
||||||
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
|
|
||||||
}
|
}
|
||||||
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
else if (petStable->UnslottedPets.size() == 1)
|
||||||
{
|
{
|
||||||
// try to find if pet is actually temporary unsummoned and alive
|
if (petStable->CurrentPet || !petStable->UnslottedPets[0].Health || petStable->UnslottedPets[0].Type != HUNTER_PET)
|
||||||
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
|
|
||||||
if (loadResult != SPELL_CAST_OK)
|
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
|
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
|
||||||
stmt->setUInt8(0, slot);
|
stmt->setUInt8(0, PetSaveMode(PET_SAVE_FIRST_STABLE_SLOT + std::distance(petStable->StabledPets.begin(), stabledPet)));
|
||||||
stmt->setUInt32(1, _player->GetGUID().GetCounter());
|
stmt->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
stmt->setUInt32(2, petStable->UnslottedPets[0].PetNumber);
|
||||||
CharacterDatabase.Execute(stmt);
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
_player->SetTemporaryUnsummonedPetNumber(0);
|
// 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())
|
||||||
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pet* newPet = new Pet(_player, HUNTER_PET);
|
||||||
|
if (!newPet->LoadPetFromDB(_player, 0, petnumber, false))
|
||||||
|
{
|
||||||
|
delete newPet;
|
||||||
|
|
||||||
|
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->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
|
stmt->setUInt32(2, petnumber);
|
||||||
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
|
stmt->setUInt32(2, petnumber);
|
||||||
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
|
SendStableResult(STABLE_SUCCESS_UNSTABLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
|
void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
|
||||||
@@ -783,12 +755,13 @@ void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
|
|||||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
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->Price))
|
if (_player->HasEnoughMoney(SlotPrice->Price))
|
||||||
{
|
{
|
||||||
++GetPlayer()->m_stableSlots;
|
++petStable.MaxStabledPets;
|
||||||
_player->ModifyMoney(-int32(SlotPrice->Price));
|
_player->ModifyMoney(-int32(SlotPrice->Price));
|
||||||
SendStableResult(STABLE_SUCCESS_BUY_SLOT);
|
SendStableResult(STABLE_SUCCESS_BUY_SLOT);
|
||||||
}
|
}
|
||||||
@@ -822,58 +795,26 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recvData)
|
|||||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
PetStable* petStable = GetPlayer()->GetPetStable();
|
||||||
|
if (!petStable)
|
||||||
if (pet)
|
|
||||||
{
|
|
||||||
if (pet->getPetType() != HUNTER_PET)
|
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
|
||||||
{
|
|
||||||
// try to find if pet is actually temporary unsummoned and alive
|
|
||||||
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
|
|
||||||
if (loadResult != SPELL_CAST_OK)
|
|
||||||
{
|
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find swapped pet slot in stable
|
// Find swapped pet slot in stable
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
|
auto stabledPet = std::find_if(petStable->StabledPets.begin(), petStable->StabledPets.end(), [petId](Optional<PetStable::PetInfo> const& pet)
|
||||||
|
|
||||||
stmt->setUInt32(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 pet && pet->PetNumber == petId;
|
||||||
return;
|
});
|
||||||
|
|
||||||
if (!result)
|
if (stabledPet == petStable->StabledPets.end())
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Field* fields = result->Fetch();
|
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate((*stabledPet)->CreatureId);
|
||||||
uint32 slot = fields[0].GetUInt8();
|
|
||||||
uint32 petEntry = fields[1].GetUInt32();
|
|
||||||
|
|
||||||
if (!petEntry)
|
|
||||||
{
|
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
|
|
||||||
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
|
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
|
||||||
{
|
{
|
||||||
// if problem in exotic pet
|
// if problem in exotic pet
|
||||||
@@ -884,30 +825,70 @@ void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
Pet* oldPet = _player->GetPet();
|
||||||
|
if (oldPet)
|
||||||
// move alive pet to slot or delete dead pet
|
|
||||||
if (pet)
|
|
||||||
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
|
|
||||||
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
|
||||||
{
|
{
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
|
if (!oldPet->IsAlive() || !oldPet->IsHunterPet())
|
||||||
stmt->setUInt8(0, slot);
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
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->setUInt32(1, _player->GetGUID().GetCounter());
|
stmt->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
stmt->setUInt32(2, petStable->UnslottedPets[0].PetNumber);
|
||||||
CharacterDatabase.Execute(stmt);
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
_player->SetTemporaryUnsummonedPetNumber(0);
|
// 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())
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// summon unstabled pet
|
// summon unstabled pet
|
||||||
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
Pet* newPet = new Pet(_player, HUNTER_PET);
|
||||||
|
if (!newPet->LoadPetFromDB(_player, 0, petId, false))
|
||||||
{
|
{
|
||||||
|
delete newPet;
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
|
||||||
|
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->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
|
stmt->setUInt32(2, petId);
|
||||||
|
CharacterDatabase.Execute(stmt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// 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->setUInt32(1, _player->GetGUID().GetCounter());
|
||||||
|
stmt->setUInt32(2, petId);
|
||||||
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
SendStableResult(STABLE_SUCCESS_UNSTABLE);
|
SendStableResult(STABLE_SUCCESS_UNSTABLE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WorldSession::HandleRepairItemOpcode(WorldPacket& recvData)
|
void WorldSession::HandleRepairItemOpcode(WorldPacket& recvData)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,355 +35,6 @@
|
|||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
#include "WorldSession.h"
|
#include "WorldSession.h"
|
||||||
|
|
||||||
class LoadPetFromDBQueryHolder : public CharacterDatabaseQueryHolder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LoadPetFromDBQueryHolder(uint32 petNumber, bool current, uint32 diffTime, std::string&& actionBar, uint32 health, uint32 mana)
|
|
||||||
: _petNumber(petNumber),
|
|
||||||
_current(current),
|
|
||||||
_diffTime(diffTime),
|
|
||||||
_actionBar(std::move(actionBar)),
|
|
||||||
_savedHealth(health),
|
|
||||||
_savedMana(mana) { }
|
|
||||||
|
|
||||||
uint32 GetPetNumber() const { return _petNumber; }
|
|
||||||
uint32 GetDiffTime() const { return _diffTime; }
|
|
||||||
bool GetCurrent() const { return _current; }
|
|
||||||
uint32 GetSavedHealth() const { return _savedHealth; }
|
|
||||||
uint32 GetSavedMana() const { return _savedMana; }
|
|
||||||
std::string GetActionBar() const { return _actionBar; }
|
|
||||||
|
|
||||||
bool Initialize();
|
|
||||||
private:
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
AURAS,
|
|
||||||
SPELLS,
|
|
||||||
COOLDOWNS,
|
|
||||||
|
|
||||||
MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint32 _petNumber;
|
|
||||||
const bool _current;
|
|
||||||
const uint32 _diffTime;
|
|
||||||
const std::string _actionBar;
|
|
||||||
const uint32 _savedHealth;
|
|
||||||
const uint32 _savedMana;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool LoadPetFromDBQueryHolder::Initialize()
|
|
||||||
{
|
|
||||||
SetSize(MAX);
|
|
||||||
|
|
||||||
bool res = true;
|
|
||||||
CharacterDatabasePreparedStatement* stmt = nullptr;
|
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA);
|
|
||||||
stmt->setUInt32(0, _petNumber);
|
|
||||||
res &= SetPreparedQuery(AURAS, stmt);
|
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL);
|
|
||||||
stmt->setUInt32(0, _petNumber);
|
|
||||||
res &= SetPreparedQuery(SPELLS, stmt);
|
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN);
|
|
||||||
stmt->setUInt32(0, _petNumber);
|
|
||||||
res &= SetPreparedQuery(COOLDOWNS, stmt);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, uint8 asynchLoadType, AsynchPetSummon* info)
|
|
||||||
{
|
|
||||||
if (!result)
|
|
||||||
return PET_LOAD_NO_RESULT;
|
|
||||||
|
|
||||||
Player* owner = GetPlayer();
|
|
||||||
if (!owner || owner->GetPet() || owner->GetVehicle() || owner->IsSpectator() || owner->IsBeingTeleportedFar())
|
|
||||||
{
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
Field* fields = result->Fetch();
|
|
||||||
|
|
||||||
// Xinef: this can happen if fetch is called twice, impossibru.
|
|
||||||
if (!fields)
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
|
|
||||||
// update for case of current pet "slot = 0"
|
|
||||||
uint32 petentry = fields[1].GetUInt32();
|
|
||||||
if (!petentry)
|
|
||||||
return PET_LOAD_NO_RESULT;
|
|
||||||
|
|
||||||
uint8 petSlot = fields[7].GetUInt8();
|
|
||||||
bool current = petSlot == PET_SAVE_AS_CURRENT;
|
|
||||||
uint32 summon_spell_id = fields[15].GetUInt32();
|
|
||||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon_spell_id); // CANT BE nullptr
|
|
||||||
bool is_temporary_summoned = spellInfo && spellInfo->GetDuration() > 0;
|
|
||||||
uint32 pet_number = fields[0].GetUInt32();
|
|
||||||
uint32 savedhealth = fields[10].GetUInt32();
|
|
||||||
uint32 savedmana = fields[11].GetUInt32();
|
|
||||||
PetType pet_type = PetType(fields[16].GetUInt8());
|
|
||||||
|
|
||||||
// xinef: BG resurrect, overwrite saved value
|
|
||||||
if (asynchLoadType == PET_LOAD_BG_RESURRECT)
|
|
||||||
savedhealth = 1;
|
|
||||||
|
|
||||||
if (pet_type == HUNTER_PET && savedhealth == 0 && asynchLoadType != PET_LOAD_SUMMON_DEAD_PET)
|
|
||||||
{
|
|
||||||
WorldPacket data(SMSG_CAST_FAILED, 1 + 4 + 1 + 4);
|
|
||||||
data << uint8(0);
|
|
||||||
data << uint32(883);
|
|
||||||
data << uint8(SPELL_FAILED_TARGETS_DEAD);
|
|
||||||
SendPacket(&data);
|
|
||||||
owner->RemoveSpellCooldown(883, false);
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check temporary summoned pets like mage water elemental
|
|
||||||
if (current && is_temporary_summoned)
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
|
|
||||||
if (pet_type == HUNTER_PET)
|
|
||||||
{
|
|
||||||
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petentry);
|
|
||||||
if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets()))
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map* map = owner->GetMap();
|
|
||||||
ObjectGuid::LowType guid = map->GenerateLowGuid<HighGuid::Pet>();
|
|
||||||
Pet* pet = new Pet(owner, pet_type);
|
|
||||||
if (!pet->Create(guid, map, owner->GetPhaseMask(), petentry, pet_number))
|
|
||||||
{
|
|
||||||
delete pet;
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<LoadPetFromDBQueryHolder> holder = std::make_shared<LoadPetFromDBQueryHolder>(pet_number, current, uint32(time(nullptr) - fields[14].GetUInt32()), fields[13].GetString(), savedhealth, savedmana);
|
|
||||||
if (!holder->Initialize())
|
|
||||||
{
|
|
||||||
delete pet;
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
float px, py, pz;
|
|
||||||
owner->GetClosePoint(px, py, pz, pet->GetObjectSize(), PET_FOLLOW_DIST, pet->GetFollowAngle());
|
|
||||||
if (!pet->IsPositionValid())
|
|
||||||
{
|
|
||||||
LOG_ERROR("network.opcode", "Pet (%s, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",
|
|
||||||
pet->GetGUID().ToString().c_str(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY());
|
|
||||||
delete pet;
|
|
||||||
return PET_LOAD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
pet->SetLoading(true);
|
|
||||||
pet->Relocate(px, py, pz, owner->GetOrientation());
|
|
||||||
pet->setPetType(pet_type);
|
|
||||||
pet->SetFaction(owner->GetFaction());
|
|
||||||
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
|
|
||||||
|
|
||||||
if (pet->IsCritter())
|
|
||||||
{
|
|
||||||
pet->UpdatePositionData();
|
|
||||||
map->AddToMap(pet->ToCreature(), true);
|
|
||||||
pet->SetLoading(false); // xinef, mine
|
|
||||||
return PET_LOAD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pet->getPetType() == HUNTER_PET || pet->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON || pet->GetCreatureTemplate()->type == CREATURE_TYPE_UNDEAD)
|
|
||||||
pet->GetCharmInfo()->SetPetNumber(pet_number, pet->IsPermanentPetFor(owner)); // Show pet details tab (Shift+P) only for hunter pets, demons or undead
|
|
||||||
else
|
|
||||||
pet->GetCharmInfo()->SetPetNumber(pet_number, false);
|
|
||||||
|
|
||||||
pet->SetDisplayId(fields[3].GetUInt32());
|
|
||||||
pet->UpdatePositionData();
|
|
||||||
pet->SetNativeDisplayId(fields[3].GetUInt32());
|
|
||||||
pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
|
|
||||||
pet->SetName(fields[8].GetString());
|
|
||||||
uint32 petlevel = fields[4].GetUInt16();
|
|
||||||
|
|
||||||
switch (pet->getPetType())
|
|
||||||
{
|
|
||||||
case SUMMON_PET:
|
|
||||||
petlevel = owner->getLevel();
|
|
||||||
|
|
||||||
if (pet->IsPetGhoul())
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 0x400); // class = rogue
|
|
||||||
else
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 0x800); // class = mage
|
|
||||||
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
|
|
||||||
// this enables popup window (pet dismiss, cancel)
|
|
||||||
break;
|
|
||||||
case HUNTER_PET:
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); // class = warrior, gender = none, power = focus
|
|
||||||
pet->SetSheath(SHEATH_STATE_MELEE);
|
|
||||||
pet->SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
|
|
||||||
// this enables popup window (pet abandon, cancel)
|
|
||||||
pet->SetMaxPower(POWER_HAPPINESS, pet->GetCreatePowers(POWER_HAPPINESS));
|
|
||||||
pet->SetPower(POWER_HAPPINESS, fields[12].GetUInt32());
|
|
||||||
pet->setPowerType(POWER_FOCUS);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!pet->IsPetGhoul())
|
|
||||||
LOG_ERROR("network.opcode", "Pet have incorrect type (%u) for pet loading.", pet->getPetType());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped here
|
|
||||||
pet->SetCreatorGUID(owner->GetGUID());
|
|
||||||
owner->SetMinion(pet, true);
|
|
||||||
|
|
||||||
pet->InitStatsForLevel(petlevel);
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
|
|
||||||
pet->SynchronizeLevelWithOwner();
|
|
||||||
pet->SetReactState(ReactStates(fields[6].GetUInt8()));
|
|
||||||
pet->SetCanModifyStats(true);
|
|
||||||
|
|
||||||
// set current pet as current
|
|
||||||
// 0=current
|
|
||||||
// 1..MAX_PET_STABLES in stable slot
|
|
||||||
// PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning))
|
|
||||||
if (petSlot)
|
|
||||||
{
|
|
||||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
|
||||||
|
|
||||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID);
|
|
||||||
stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT));
|
|
||||||
stmt->setUInt32(1, owner->GetGUID().GetCounter());
|
|
||||||
stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT));
|
|
||||||
stmt->setUInt32(3, pet_number);
|
|
||||||
trans->Append(stmt);
|
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
|
|
||||||
stmt->setUInt8(0, uint8(PET_SAVE_AS_CURRENT));
|
|
||||||
stmt->setUInt32(1, owner->GetGUID().GetCounter());
|
|
||||||
stmt->setUInt32(2, pet_number);
|
|
||||||
trans->Append(stmt);
|
|
||||||
|
|
||||||
CharacterDatabase.CommitTransaction(trans);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (summon_spell_id)
|
|
||||||
{
|
|
||||||
WorldPacket data(SMSG_SPELL_GO, (8 + 8 + 4 + 4 + 2));
|
|
||||||
data << owner->GetPackGUID();
|
|
||||||
data << owner->GetPackGUID();
|
|
||||||
data << uint8(0);
|
|
||||||
data << uint32(summon_spell_id);
|
|
||||||
data << uint32(256); // CAST_FLAG_UNKNOWN3
|
|
||||||
data << uint32(0);
|
|
||||||
owner->SendMessageToSet(&data, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do it as early as possible!
|
|
||||||
pet->InitTalentForLevel(); // set original talents points before spell loading
|
|
||||||
|
|
||||||
if (!is_temporary_summoned)
|
|
||||||
pet->GetCharmInfo()->InitPetActionBar();
|
|
||||||
|
|
||||||
map->AddToMap(pet->ToCreature(), true);
|
|
||||||
|
|
||||||
if (pet->getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
|
|
||||||
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pet->SetHealth(savedhealth > pet->GetMaxHealth() ? pet->GetMaxHealth() : savedhealth);
|
|
||||||
pet->SetPower(POWER_MANA, savedmana > pet->GetMaxPower(POWER_MANA) ? pet->GetMaxPower(POWER_MANA) : savedmana);
|
|
||||||
}
|
|
||||||
|
|
||||||
pet->SetAsynchLoadType(asynchLoadType);
|
|
||||||
|
|
||||||
AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this, info](SQLQueryHolderBase const& holder)
|
|
||||||
{
|
|
||||||
HandleLoadPetFromDBSecondCallback(static_cast<LoadPetFromDBQueryHolder const&>(holder), info);
|
|
||||||
});
|
|
||||||
|
|
||||||
return PET_LOAD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder const& holder, AsynchPetSummon* info)
|
|
||||||
{
|
|
||||||
if (!GetPlayer())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Player* owner = GetPlayer();
|
|
||||||
Pet* pet = owner->GetPet();
|
|
||||||
if (!pet)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pet->_LoadAuras(holder.GetPreparedResult(PET_LOAD_QUERY_LOADAURAS), holder.GetDiffTime());
|
|
||||||
bool current = holder.GetCurrent();
|
|
||||||
uint32 summon_spell_id = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
|
|
||||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon_spell_id); // CANT BE nullptr
|
|
||||||
bool is_temporary_summoned = spellInfo && spellInfo->GetDuration() > 0;
|
|
||||||
|
|
||||||
// load action bar, if data broken will fill later by default spells.
|
|
||||||
if (!is_temporary_summoned)
|
|
||||||
{
|
|
||||||
pet->GetCharmInfo()->LoadPetActionBar(holder.GetActionBar()); // action bar stored in already read string
|
|
||||||
pet->_LoadSpells(holder.GetPreparedResult(PET_LOAD_QUERY_LOADSPELLS));
|
|
||||||
pet->InitTalentForLevel(); // re-init to check talent count
|
|
||||||
pet->_LoadSpellCooldowns(holder.GetPreparedResult(PET_LOAD_QUERY_LOADSPELLCOOLDOWN));
|
|
||||||
pet->LearnPetPassives();
|
|
||||||
pet->InitLevelupSpellsForLevel();
|
|
||||||
pet->CastPetAuras(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
pet->CleanupActionBar(); // remove unknown spells from action bar after load
|
|
||||||
owner->PetSpellInitialize();
|
|
||||||
owner->SendTalentsInfoData(true);
|
|
||||||
|
|
||||||
if (owner->GetGroup())
|
|
||||||
owner->SetGroupUpdateFlag(GROUP_UPDATE_PET);
|
|
||||||
|
|
||||||
//set last used pet number (for use in BG's)
|
|
||||||
if (owner->GetTypeId() == TYPEID_PLAYER && pet->isControlled() && !pet->isTemporarySummoned() && (pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET))
|
|
||||||
{
|
|
||||||
owner->ToPlayer()->SetLastPetNumber(holder.GetPetNumber());
|
|
||||||
owner->SetLastPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pet->getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
|
|
||||||
{
|
|
||||||
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
|
|
||||||
pet->SetHealth(pet->GetMaxHealth());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!holder.GetSavedHealth() && pet->getPetType() == HUNTER_PET && pet->GetAsynchLoadType() != PET_LOAD_SUMMON_DEAD_PET)
|
|
||||||
pet->setDeathState(JUST_DIED);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pet->SetHealth(holder.GetSavedHealth() > pet->GetMaxHealth() ? pet->GetMaxHealth() : holder.GetSavedHealth());
|
|
||||||
pet->SetPower(POWER_MANA, holder.GetSavedMana() > pet->GetMaxPower(POWER_MANA) ? pet->GetMaxPower(POWER_MANA) : holder.GetSavedMana());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pet->SetLoading(false);
|
|
||||||
owner->SetTemporaryUnsummonedPetNumber(0); // clear this only if pet is loaded successfuly
|
|
||||||
|
|
||||||
// current
|
|
||||||
if (current && owner->IsPetNeedBeTemporaryUnsummoned())
|
|
||||||
{
|
|
||||||
owner->UnsummonPetTemporaryIfAny();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pet->HandleAsynchLoadSucceed();
|
|
||||||
|
|
||||||
if (info && info->m_healthPct)
|
|
||||||
{
|
|
||||||
pet->SetHealth(pet->CountPctFromMaxHealth(info->m_healthPct));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorldSession::HandleDismissCritter(WorldPacket& recvData)
|
void WorldSession::HandleDismissCritter(WorldPacket& recvData)
|
||||||
{
|
{
|
||||||
ObjectGuid guid;
|
ObjectGuid guid;
|
||||||
@@ -1193,12 +844,18 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
|
|||||||
recvData >> name;
|
recvData >> name;
|
||||||
recvData >> isdeclined;
|
recvData >> isdeclined;
|
||||||
|
|
||||||
|
PetStable* petStable = _player->GetPetStable();
|
||||||
|
|
||||||
Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
|
Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
|
||||||
|
|
||||||
// check it!
|
// check it!
|
||||||
if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
|
if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
|
||||||
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ||
|
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
|
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
|
||||||
if (res != PET_NAME_SUCCESS)
|
if (res != PET_NAME_SUCCESS)
|
||||||
@@ -1221,6 +878,9 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
|
|||||||
|
|
||||||
pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
|
pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
|
||||||
|
|
||||||
|
petStable->CurrentPet->Name = name;
|
||||||
|
petStable->CurrentPet->WasRenamed = true;
|
||||||
|
|
||||||
if (isdeclined)
|
if (isdeclined)
|
||||||
{
|
{
|
||||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||||
|
|||||||
@@ -687,18 +687,13 @@ public: // opcodes handlers
|
|||||||
void HandleBinderActivateOpcode(WorldPacket& recvPacket);
|
void HandleBinderActivateOpcode(WorldPacket& recvPacket);
|
||||||
void HandleListStabledPetsOpcode(WorldPacket& recvPacket);
|
void HandleListStabledPetsOpcode(WorldPacket& recvPacket);
|
||||||
void HandleStablePet(WorldPacket& recvPacket);
|
void HandleStablePet(WorldPacket& recvPacket);
|
||||||
void HandleStablePetCallback(PreparedQueryResult result);
|
|
||||||
void HandleUnstablePet(WorldPacket& recvPacket);
|
void HandleUnstablePet(WorldPacket& recvPacket);
|
||||||
void HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result);
|
|
||||||
void HandleBuyStableSlot(WorldPacket& recvPacket);
|
void HandleBuyStableSlot(WorldPacket& recvPacket);
|
||||||
void HandleStableRevivePet(WorldPacket& recvPacket);
|
void HandleStableRevivePet(WorldPacket& recvPacket);
|
||||||
void HandleStableSwapPet(WorldPacket& recvPacket);
|
void HandleStableSwapPet(WorldPacket& recvPacket);
|
||||||
void HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result);
|
|
||||||
void HandleOpenWrappedItemCallback(uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID, PreparedQueryResult result);
|
void HandleOpenWrappedItemCallback(uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID, PreparedQueryResult result);
|
||||||
void HandleLoadActionsSwitchSpec(PreparedQueryResult result);
|
void HandleLoadActionsSwitchSpec(PreparedQueryResult result);
|
||||||
void HandleCharacterAuraFrozen(PreparedQueryResult result);
|
void HandleCharacterAuraFrozen(PreparedQueryResult result);
|
||||||
uint8 HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, uint8 asynchLoadType, AsynchPetSummon* info);
|
|
||||||
void HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder const& holder, AsynchPetSummon* info);
|
|
||||||
|
|
||||||
void HandleDuelAcceptedOpcode(WorldPacket& recvPacket);
|
void HandleDuelAcceptedOpcode(WorldPacket& recvPacket);
|
||||||
void HandleDuelCancelledOpcode(WorldPacket& recvPacket);
|
void HandleDuelCancelledOpcode(WorldPacket& recvPacket);
|
||||||
|
|||||||
@@ -5922,19 +5922,22 @@ SpellCastResult Spell::CheckCast(bool strict)
|
|||||||
}
|
}
|
||||||
case SPELL_EFFECT_RESURRECT_PET:
|
case SPELL_EFFECT_RESURRECT_PET:
|
||||||
{
|
{
|
||||||
if (Creature* pet = m_caster->GetGuardianPet())
|
Unit* unitCaster = m_caster->ToUnit();
|
||||||
{
|
if (!unitCaster)
|
||||||
if (pet->IsAlive())
|
return SPELL_FAILED_BAD_TARGETS;
|
||||||
{
|
|
||||||
|
Creature* pet = unitCaster->GetGuardianPet();
|
||||||
|
if (pet && pet->IsAlive())
|
||||||
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
|
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
|
||||||
}
|
|
||||||
}
|
Player* player = unitCaster->ToPlayer();
|
||||||
else if (Player* player = m_caster->ToPlayer())
|
if (player)
|
||||||
{
|
{
|
||||||
SpellCastResult loadResult = Pet::TryLoadFromDB(player, false, MAX_PET_TYPE, true);
|
// Check pet before resurrect
|
||||||
if (loadResult != SPELL_CAST_OK)
|
auto [petStable, petSlot] = Pet::GetLoadPetInfo(*player->GetPetStable(), 0, 0, false);
|
||||||
|
if (!petStable)
|
||||||
{
|
{
|
||||||
return loadResult;
|
return SPELL_FAILED_NO_PET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5972,6 +5975,10 @@ SpellCastResult Spell::CheckCast(bool strict)
|
|||||||
}
|
}
|
||||||
case SPELL_EFFECT_SUMMON_PET:
|
case SPELL_EFFECT_SUMMON_PET:
|
||||||
{
|
{
|
||||||
|
Unit* unitCaster = m_caster->ToUnit();
|
||||||
|
if (!unitCaster)
|
||||||
|
return SPELL_FAILED_BAD_TARGETS;
|
||||||
|
|
||||||
if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST))
|
if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST))
|
||||||
{
|
{
|
||||||
if (m_caster->GetPetGUID())
|
if (m_caster->GetPetGUID())
|
||||||
@@ -5983,6 +5990,40 @@ SpellCastResult Spell::CheckCast(bool strict)
|
|||||||
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK && strict)
|
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK && strict)
|
||||||
if (Pet* pet = m_caster->ToPlayer()->GetPet())
|
if (Pet* pet = m_caster->ToPlayer()->GetPet())
|
||||||
pet->CastSpell(pet, 32752, true, nullptr, nullptr, pet->GetGUID()); //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
|
pet->CastSpell(pet, 32752, true, nullptr, nullptr, pet->GetGUID()); //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
|
||||||
|
|
||||||
|
Player* playerCaster = unitCaster->ToPlayer();
|
||||||
|
if (playerCaster && playerCaster->GetPetStable())
|
||||||
|
{
|
||||||
|
std::pair<PetStable::PetInfo const*, PetSaveMode> info = Pet::GetLoadPetInfo(*playerCaster->GetPetStable(), m_spellInfo->Effects[i].MiscValue, 0, false);
|
||||||
|
if (info.first)
|
||||||
|
{
|
||||||
|
if (info.first->Type == HUNTER_PET)
|
||||||
|
{
|
||||||
|
if (!info.first->Health)
|
||||||
|
{
|
||||||
|
playerCaster->SendTameFailure(PET_TAME_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(PET_TAME_CANT_CONTROL_EXOTIC);
|
||||||
|
else
|
||||||
|
playerCaster->SendTameFailure(PET_TAME_NOPET_AVAILABLE);
|
||||||
|
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!m_spellInfo->Effects[i].MiscValue) // when miscvalue is present it is allowed to create new pets
|
||||||
|
{
|
||||||
|
playerCaster->SendTameFailure(PET_TAME_NOPET_AVAILABLE);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SPELL_EFFECT_SUMMON_PLAYER:
|
case SPELL_EFFECT_SUMMON_PLAYER:
|
||||||
|
|||||||
@@ -3141,7 +3141,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
|
|||||||
|
|
||||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||||
{
|
{
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, false);
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
m_caster->ToPlayer()->PetSpellInitialize();
|
m_caster->ToPlayer()->PetSpellInitialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3219,19 +3219,45 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (owner->GetTypeId() == TYPEID_PLAYER)
|
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
|
else
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
owner->GetClosePoint(x, y, z, owner->GetObjectSize());
|
owner->GetClosePoint(x, y, z, owner->GetObjectSize());
|
||||||
owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET, 0, m_spellInfo->Id, m_caster->GetGUID(), PET_LOAD_SUMMON_PET);
|
Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET);
|
||||||
//if (!pet)
|
if (!pet)
|
||||||
// return;
|
return;
|
||||||
|
|
||||||
// xinef: cant execute this... :( hope nothing relevant gets bugged
|
if (m_caster->GetTypeId() == TYPEID_UNIT)
|
||||||
//ExecuteLogEffectSummonObject(effIndex, pet);
|
{
|
||||||
|
if (m_caster->ToCreature()->IsTotem())
|
||||||
|
pet->SetReactState(REACT_AGGRESSIVE);
|
||||||
|
else
|
||||||
|
pet->SetReactState(REACT_DEFENSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
||||||
|
|
||||||
|
// Reset cooldowns
|
||||||
|
if (owner->getClass() != CLASS_HUNTER)
|
||||||
|
{
|
||||||
|
pet->m_CreatureSpellCooldowns.clear();
|
||||||
|
owner->PetSpellInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set health to max if new pet is summoned
|
||||||
|
// in this function old pet is saved with current health eg. 20% and new one is loaded from db with same amount
|
||||||
|
// pet should have full health
|
||||||
|
pet->SetHealth(pet->GetMaxHealth());
|
||||||
|
|
||||||
|
// generate new name for summon pet
|
||||||
|
std::string new_name = sObjectMgr->GeneratePetName(petentry);
|
||||||
|
if (!new_name.empty())
|
||||||
|
pet->SetName(new_name);
|
||||||
|
|
||||||
|
// ExecuteLogEffectSummonObject(effectInfo->EffectIndex, pet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spell::EffectLearnPetSpell(SpellEffIndex effIndex)
|
void Spell::EffectLearnPetSpell(SpellEffIndex effIndex)
|
||||||
@@ -3256,7 +3282,7 @@ void Spell::EffectLearnPetSpell(SpellEffIndex effIndex)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
pet->learnSpell(learn_spellproto->Id);
|
pet->learnSpell(learn_spellproto->Id);
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, false);
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
pet->GetOwner()->PetSpellInitialize();
|
pet->GetOwner()->PetSpellInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5362,41 +5388,55 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/)
|
|||||||
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
|
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (damage < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
Player* player = m_caster->ToPlayer();
|
Player* player = m_caster->ToPlayer();
|
||||||
if (!player)
|
if (!player)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Pet* pet = player->GetPet();
|
// Maybe player dismissed dead pet or pet despawned?
|
||||||
if (pet && pet->IsAlive())
|
bool hadPet = true;
|
||||||
return;
|
|
||||||
|
|
||||||
if (damage < 0)
|
if (!player->GetPet())
|
||||||
return;
|
|
||||||
|
|
||||||
float x, y, z;
|
|
||||||
player->GetPosition(x, y, z);
|
|
||||||
if (!pet)
|
|
||||||
{
|
{
|
||||||
player->SummonPet(0, x, y, z, player->GetOrientation(), SUMMON_PET, 0, 0, ObjectGuid((uint64)damage), PET_LOAD_SUMMON_DEAD_PET, damage);
|
// Position passed to SummonPet is irrelevant with current implementation,
|
||||||
return;
|
// pet will be relocated without using these coords in Pet::LoadPetFromDB
|
||||||
|
player->SummonPet(0, 0.0f, 0.0f, 0.0f, 0.0f, SUMMON_PET);
|
||||||
|
hadPet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pet->SetPosition(x, y, z, player->GetOrientation());
|
// TODO: Better to fail Hunter's "Revive Pet" at cast instead of here when casting ends
|
||||||
|
Pet* pet = player->GetPet(); // Attempt to get current pet
|
||||||
|
if (!pet || pet->IsAlive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If player did have a pet before reviving, teleport it
|
||||||
|
if (hadPet)
|
||||||
|
{
|
||||||
|
// Reposition the pet's corpse before reviving so as not to grab aggro
|
||||||
|
// We can use a different, more accurate version of GetClosePoint() since we have a pet
|
||||||
|
float x, y, z; // Will be used later to reposition the pet if we have one
|
||||||
|
player->GetClosePoint(x, y, z, pet->GetCombatReach(), PET_FOLLOW_DIST, pet->GetFollowAngle());
|
||||||
|
pet->NearTeleportTo(x, y, z, player->GetOrientation());
|
||||||
|
pet->Relocate(x, y, z, player->GetOrientation()); // This is needed so SaveStayPosition() will get the proper coords.
|
||||||
|
}
|
||||||
|
|
||||||
pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
|
pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
|
||||||
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
|
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
|
||||||
pet->setDeathState(ALIVE);
|
pet->setDeathState(ALIVE);
|
||||||
pet->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_POSSESSED))); // xinef: just in case
|
pet->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_POSSESSED))); // xinef: just in case
|
||||||
pet->SetHealth(pet->CountPctFromMaxHealth(damage));
|
pet->SetHealth(pet->CountPctFromMaxHealth(damage));
|
||||||
|
pet->SetDisplayId(pet->GetNativeDisplayId());
|
||||||
|
|
||||||
// xinef: restore movement
|
// xinef: restore movement
|
||||||
if (pet->GetCharmInfo())
|
if (auto ci = pet->GetCharmInfo())
|
||||||
{
|
{
|
||||||
pet->GetCharmInfo()->SetIsAtStay(false);
|
ci->SetIsAtStay(false);
|
||||||
pet->GetCharmInfo()->SetIsFollowing(false);
|
ci->SetIsFollowing(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, false);
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/)
|
void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/)
|
||||||
@@ -6015,7 +6055,7 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex)
|
|||||||
|
|
||||||
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
|
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
|
||||||
{
|
{
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, false);
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
unitTarget->ToPlayer()->PetSpellInitialize();
|
unitTarget->ToPlayer()->PetSpellInitialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1195,7 +1195,7 @@ public:
|
|||||||
// caster have pet now
|
// caster have pet now
|
||||||
player->SetMinion(pet, true);
|
player->SetMinion(pet, true);
|
||||||
|
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, false);
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
player->PetSpellInitialize();
|
player->PetSpellInitialize();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "SpellMgr.h"
|
#include "SpellMgr.h"
|
||||||
#include "SpellInfo.h"
|
#include "SpellInfo.h"
|
||||||
#include "WorldSession.h"
|
#include "WorldSession.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
|
|
||||||
using namespace Acore::ChatCommands;
|
using namespace Acore::ChatCommands;
|
||||||
|
|
||||||
@@ -79,42 +80,28 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Everything looks OK, create new pet
|
// Everything looks OK, create new pet
|
||||||
Pet* pet = new Pet(player, HUNTER_PET);
|
Pet* pet = player->CreateTamedPetFrom(creatureTarget);
|
||||||
if (!pet->CreateBaseAtCreature(creatureTarget))
|
|
||||||
{
|
|
||||||
delete pet;
|
|
||||||
handler->PSendSysMessage("Error 1");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
creatureTarget->setDeathState(JUST_DIED);
|
// "kill" original creature
|
||||||
creatureTarget->RemoveCorpse();
|
creatureTarget->DespawnOrUnsummon();
|
||||||
creatureTarget->SetHealth(0); // just for nice GM-mode view
|
|
||||||
|
|
||||||
pet->SetGuidValue(UNIT_FIELD_CREATEDBY, player->GetGUID());
|
uint8 level = (creatureTarget->getLevel() < (player->getLevel() - 5)) ? (player->getLevel() - 5) : player->getLevel();
|
||||||
pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, player->GetFaction());
|
|
||||||
|
|
||||||
if (!pet->InitStatsForLevel(creatureTarget->getLevel()))
|
|
||||||
{
|
|
||||||
LOG_ERROR("misc", "InitStatsForLevel() in EffectTameCreature failed! Pet deleted.");
|
|
||||||
handler->PSendSysMessage("Error 2");
|
|
||||||
delete pet;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare visual effect for levelup
|
// prepare visual effect for levelup
|
||||||
pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel() - 1);
|
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);
|
||||||
pet->GetCharmInfo()->SetPetNumber(sObjectMgr->GeneratePetNumber(), true);
|
|
||||||
|
|
||||||
// this enables pet details window (Shift+P)
|
// add to world
|
||||||
pet->InitPetCreateSpells();
|
|
||||||
pet->SetFullHealth();
|
|
||||||
pet->GetMap()->AddToMap(pet->ToCreature());
|
pet->GetMap()->AddToMap(pet->ToCreature());
|
||||||
|
|
||||||
// visual effect for levelup
|
// visual effect for levelup
|
||||||
pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel());
|
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
|
||||||
|
|
||||||
|
// caster have pet now
|
||||||
player->SetMinion(pet, true);
|
player->SetMinion(pet, true);
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT, false);
|
|
||||||
|
pet->InitTalentForLevel();
|
||||||
|
|
||||||
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
player->PetSpellInitialize();
|
player->PetSpellInitialize();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1679,7 +1679,31 @@ class spell_gen_pet_summoned : public SpellScript
|
|||||||
{
|
{
|
||||||
Player* player = GetCaster()->ToPlayer();
|
Player* player = GetCaster()->ToPlayer();
|
||||||
if (player->GetLastPetNumber() && player->CanResummonPet(player->GetLastPetSpell()))
|
if (player->GetLastPetNumber() && player->CanResummonPet(player->GetLastPetSpell()))
|
||||||
Pet::LoadPetFromDB(player, PET_LOAD_BG_RESURRECT, 0, player->GetLastPetNumber(), true);
|
{
|
||||||
|
PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET;
|
||||||
|
Pet* newPet = new Pet(player, newPetType);
|
||||||
|
if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true))
|
||||||
|
{
|
||||||
|
// revive the pet if it is dead
|
||||||
|
if (newPet->getDeathState() != ALIVE && newPet->getDeathState() != JUST_RESPAWNED)
|
||||||
|
newPet->setDeathState(JUST_RESPAWNED);
|
||||||
|
|
||||||
|
newPet->SetFullHealth();
|
||||||
|
newPet->SetPower(newPet->getPowerType(), newPet->GetMaxPower(newPet->getPowerType()));
|
||||||
|
|
||||||
|
switch (newPet->GetEntry())
|
||||||
|
{
|
||||||
|
case NPC_DOOMGUARD:
|
||||||
|
case NPC_INFERNAL:
|
||||||
|
newPet->SetEntry(NPC_IMP);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
delete newPet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Register() override
|
void Register() override
|
||||||
|
|||||||
@@ -948,7 +948,20 @@ class spell_hun_tame_beast : public SpellScript
|
|||||||
return SPELL_FAILED_DONT_REPORT;
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (caster->GetPetGUID() || player->GetTemporaryUnsummonedPetNumber() || player->IsPetDismissed() || player->GetCharmGUID())
|
PetStable const* petStable = player->GetPetStable();
|
||||||
|
if (petStable)
|
||||||
|
{
|
||||||
|
if (petStable->CurrentPet)
|
||||||
|
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
|
||||||
|
|
||||||
|
if (petStable->GetUnslottedHunterPet())
|
||||||
|
{
|
||||||
|
caster->SendTameFailure(PET_TAME_TOO_MANY);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->GetCharmGUID())
|
||||||
{
|
{
|
||||||
player->SendTameFailure(PET_TAME_ANOTHER_SUMMON_ACTIVE);
|
player->SendTameFailure(PET_TAME_ANOTHER_SUMMON_ACTIVE);
|
||||||
return SPELL_FAILED_DONT_REPORT;
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
|||||||
Reference in New Issue
Block a user