mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-22 19:54:36 +00:00
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:
@@ -48,6 +48,7 @@
|
||||
#include "WaypointMovementGenerator.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Pet.h"
|
||||
|
||||
// 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
|
||||
@@ -854,7 +855,7 @@ void Creature::Regenerate(Powers power)
|
||||
if (Powers((*i)->GetMiscValue()) == power)
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -33,12 +33,15 @@
|
||||
|
||||
#define MAX_KILL_CREDIT 2
|
||||
#define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS
|
||||
#define PET_FOCUS_REGEN_INTERVAL 4 * IN_MILLISECONDS
|
||||
|
||||
#define MAX_CREATURE_QUEST_ITEMS 6
|
||||
|
||||
#define MAX_EQUIPMENT_ITEMS 3
|
||||
|
||||
constexpr Milliseconds PET_FOCUS_REGEN_INTERVAL = 4s;
|
||||
|
||||
enum class VisibilityDistanceType : uint8;
|
||||
|
||||
// TODO: Implement missing flags from TC in places that custom flags from xinef&pussywizzard use flag values.
|
||||
// EnumUtils: DESCRIBE THIS
|
||||
enum CreatureFlagsExtra : uint32
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,8 @@
|
||||
#include "PetDefines.h"
|
||||
#include "TemporarySummon.h"
|
||||
|
||||
#define PET_FOCUS_REGEN_INTERVAL 4 * IN_MILLISECONDS
|
||||
#define PET_LOSE_HAPPINES_INTERVAL 7500
|
||||
#define HAPPINESS_LEVEL_SIZE 333000
|
||||
constexpr auto PET_LOSE_HAPPINES_INTERVAL = 7500;
|
||||
constexpr auto HAPPINESS_LEVEL_SIZE = 333000;
|
||||
|
||||
struct PetSpell
|
||||
{
|
||||
@@ -32,20 +31,6 @@ struct PetSpell
|
||||
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::vector<uint32> AutoSpellList;
|
||||
|
||||
@@ -55,7 +40,7 @@ class Pet : public Guardian
|
||||
{
|
||||
public:
|
||||
explicit Pet(Player* owner, PetType type = MAX_PET_TYPE);
|
||||
~Pet() override;
|
||||
~Pet() override = default;
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
@@ -65,7 +50,7 @@ public:
|
||||
PetType getPetType() const { return m_petType; }
|
||||
void setPetType(PetType type) { m_petType = type; }
|
||||
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
|
||||
|
||||
@@ -73,10 +58,11 @@ public:
|
||||
bool CreateBaseAtCreature(Creature* creature);
|
||||
bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner);
|
||||
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 bool LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry = 0, uint32 petnumber = 0, bool current = false, AsynchPetSummon* info = nullptr);
|
||||
bool isBeingLoaded() const override { return m_loading;}
|
||||
void SavePetToDB(PetSaveMode mode, bool logout);
|
||||
static std::pair<PetStable::PetInfo const*, PetSaveMode> GetLoadPetInfo(PetStable const& stable, uint32 petEntry, uint32 petnumber, bool current);
|
||||
bool LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current);
|
||||
bool isBeingLoaded() const override { return m_loading; }
|
||||
void SavePetToDB(PetSaveMode mode);
|
||||
void FillPetInfo(PetStable::PetInfo* petInfo) const;
|
||||
void Remove(PetSaveMode mode, bool returnreagent = false);
|
||||
static void DeleteFromDB(ObjectGuid::LowType guidlow);
|
||||
|
||||
@@ -99,19 +85,8 @@ public:
|
||||
void SynchronizeLevelWithOwner();
|
||||
bool HaveInDiet(ItemTemplate const* item) const;
|
||||
uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel) const;
|
||||
void SetDuration(int32 dur) { m_duration = dur; }
|
||||
int32 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 SetDuration(Milliseconds dur) { m_duration = dur; }
|
||||
Milliseconds GetDuration() const { return m_duration; }
|
||||
|
||||
void ToggleAutocast(SpellInfo const* spellInfo, bool apply);
|
||||
|
||||
@@ -124,8 +99,8 @@ public:
|
||||
void ClearCastWhenWillAvailable();
|
||||
void RemoveSpellCooldown(uint32 spell_id, bool update /* = false */);
|
||||
|
||||
void _SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout);
|
||||
void _SaveAuras(CharacterDatabaseTransaction trans, bool logout);
|
||||
void _SaveSpellCooldowns(CharacterDatabaseTransaction trans);
|
||||
void _SaveAuras(CharacterDatabaseTransaction trans);
|
||||
void _SaveSpells(CharacterDatabaseTransaction trans);
|
||||
|
||||
void _LoadSpellCooldowns(PreparedQueryResult result);
|
||||
@@ -139,6 +114,7 @@ public:
|
||||
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
void CleanupActionBar();
|
||||
std::string GenerateActionBarData() const;
|
||||
|
||||
PetSpellMap m_spells;
|
||||
AutoSpellList m_autospells;
|
||||
@@ -159,37 +135,30 @@ public:
|
||||
void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); }
|
||||
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 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; }
|
||||
|
||||
protected:
|
||||
Player* m_owner;
|
||||
int32 m_happinessTimer;
|
||||
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;
|
||||
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_tempoldTarget;
|
||||
bool m_tempspellIsPositive;
|
||||
uint32 m_tempspell;
|
||||
|
||||
uint8 asynchLoadType;
|
||||
|
||||
private:
|
||||
void SaveToDB(uint32, uint8, uint32) override // override of Creature::SaveToDB - must not be called
|
||||
{
|
||||
|
||||
@@ -18,17 +18,25 @@
|
||||
#ifndef 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,
|
||||
HUNTER_PET = 1,
|
||||
MAX_PET_TYPE = 4
|
||||
};
|
||||
|
||||
#define MAX_PET_STABLES 4
|
||||
constexpr auto MAX_PET_STABLES = 4;
|
||||
|
||||
// stored in character_pet.slot
|
||||
enum PetSaveMode
|
||||
enum PetSaveMode : int8
|
||||
{
|
||||
PET_SAVE_AS_DELETED = -1, // not saved in fact
|
||||
PET_SAVE_AS_CURRENT = 0, // in current slot (with player)
|
||||
@@ -73,24 +81,6 @@ enum PetTalk
|
||||
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
|
||||
{
|
||||
PET_LOAD_OK = 0,
|
||||
@@ -202,4 +192,39 @@ enum PetScalingSpells
|
||||
#define PET_FOLLOW_DIST 1.0f
|
||||
#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
|
||||
|
||||
@@ -282,8 +282,6 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
|
||||
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
|
||||
m_forced_speed_changes[i] = 0;
|
||||
|
||||
m_stableSlots = 0;
|
||||
|
||||
/////////////////// Instance System /////////////////////
|
||||
|
||||
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.
|
||||
// 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);
|
||||
PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
|
||||
|
||||
@@ -8703,7 +8701,18 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
|
||||
pet->CombatStop();
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -8898,7 +8907,7 @@ void Player::PetSpellInitialize()
|
||||
WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1);
|
||||
data << pet->GetGUID();
|
||||
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(charmInfo->GetCommandState());
|
||||
data << uint16(0); // Flags, mostly unknown
|
||||
@@ -13396,8 +13405,11 @@ void Player::ResummonPetTemporaryUnSummonedIfAny()
|
||||
if (!CanResummonPet(GetLastPetSpell()))
|
||||
return;
|
||||
|
||||
Pet::LoadPetFromDB(this, PET_LOAD_SUMMON_PET, 0, m_temporaryUnsummonedPetNumber, true);
|
||||
//m_temporaryUnsummonedPetNumber = 0;
|
||||
Pet* newPet = new Pet(this);
|
||||
if (!newPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
|
||||
delete newPet;
|
||||
|
||||
m_temporaryUnsummonedPetNumber = 0;
|
||||
}
|
||||
|
||||
bool Player::CanResummonPet(uint32 spellid)
|
||||
@@ -13878,7 +13890,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
||||
stmt->setUInt32(index++, m_resetTalentsCost);
|
||||
stmt->setUInt32(index++, uint32(m_resetTalentsTime));
|
||||
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
|
||||
stmt->setUInt8(index++, m_stableSlots);
|
||||
stmt->setUInt8(index++, m_petStable ? m_petStable->MaxStabledPets : 0);
|
||||
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
|
||||
stmt->setUInt16(index++, GetZoneId());
|
||||
stmt->setUInt32(index++, uint32(m_deathExpireTime));
|
||||
@@ -14017,7 +14029,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
||||
stmt->setUInt32(index++, m_resetTalentsCost);
|
||||
stmt->setUInt32(index++, uint32(m_resetTalentsTime));
|
||||
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
|
||||
stmt->setUInt8(index++, m_stableSlots);
|
||||
stmt->setUInt8(index++, m_petStable ? m_petStable->MaxStabledPets : 0);
|
||||
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
|
||||
stmt->setUInt16(index++, GetZoneId());
|
||||
stmt->setUInt32(index++, uint32(m_deathExpireTime));
|
||||
@@ -14468,6 +14480,7 @@ void Player::SetReputation(uint32 factionentry, uint32 value)
|
||||
{
|
||||
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value);
|
||||
}
|
||||
|
||||
uint32 Player::GetReputation(uint32 factionentry) const
|
||||
{
|
||||
return GetReputationMgr().GetReputation(sFactionStore.LookupEntry(factionentry));
|
||||
@@ -14701,6 +14714,14 @@ bool Player::AddItem(uint32 itemId, uint32 count)
|
||||
return true;
|
||||
}
|
||||
|
||||
PetStable& Player::GetOrInitPetStable()
|
||||
{
|
||||
if (!m_petStable)
|
||||
m_petStable = std::make_unique<PetStable>();
|
||||
|
||||
return *m_petStable;
|
||||
}
|
||||
|
||||
void Player::RefundItem(Item* item)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (_instanceResetTimes.empty())
|
||||
@@ -15119,30 +15192,134 @@ Guild* Player::GetGuild() const
|
||||
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};
|
||||
if (!pos.IsPositionValid())
|
||||
return;
|
||||
PetStable& petStable = GetOrInitPetStable();
|
||||
|
||||
AsynchPetSummon* asynchPetInfo = new AsynchPetSummon(entry, pos, petType, duration, createdBySpell, casterGUID, healthPct);
|
||||
Pet::LoadPetFromDB(this, asynchLoadType, entry, 0, false, asynchPetInfo);
|
||||
}
|
||||
Pet* pet = new Pet(this, petType);
|
||||
|
||||
bool Player::IsPetDismissed()
|
||||
{
|
||||
/*
|
||||
* Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever
|
||||
* Changes the slot flag, they will break this validation.
|
||||
*/
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
||||
return true;
|
||||
if (duration > 0s)
|
||||
pet->SetDuration(duration);
|
||||
|
||||
return false;
|
||||
// Generate a new name for the newly summoned ghoul
|
||||
if (pet->IsPetGhoul())
|
||||
{
|
||||
std::string new_name = sObjectMgr->GeneratePetName(entry);
|
||||
if (!new_name.empty())
|
||||
pet->SetName(new_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -877,6 +877,7 @@ enum PlayerLoginQueryIndex
|
||||
PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH = 34,
|
||||
PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 35,
|
||||
PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS = 36,
|
||||
PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS = 37,
|
||||
MAX_PLAYER_LOGIN_QUERY
|
||||
};
|
||||
|
||||
@@ -1162,9 +1163,12 @@ public:
|
||||
void RemoveRestFlag(RestFlag restFlag);
|
||||
[[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;
|
||||
bool IsPetDismissed();
|
||||
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);
|
||||
Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, Milliseconds duration = 0s);
|
||||
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
|
||||
|
||||
@@ -1345,8 +1349,6 @@ public:
|
||||
|
||||
bool AddItem(uint32 itemId, uint32 count);
|
||||
|
||||
uint32 m_stableSlots;
|
||||
|
||||
/*********************************************************/
|
||||
/*** GOSSIP SYSTEM ***/
|
||||
/*********************************************************/
|
||||
@@ -2684,6 +2686,7 @@ public:
|
||||
void _LoadInstanceTimeRestrictions(PreparedQueryResult result);
|
||||
void _LoadBrewOfTheMonth(PreparedQueryResult result);
|
||||
void _LoadCharacterSettings(PreparedQueryResult result);
|
||||
void _LoadPetStable(uint8 petStableSlots, PreparedQueryResult result);
|
||||
|
||||
/*********************************************************/
|
||||
/*** SAVE SYSTEM ***/
|
||||
@@ -2923,6 +2926,8 @@ private:
|
||||
bool m_bMustDelayTeleport;
|
||||
bool m_bHasDelayedTeleport;
|
||||
|
||||
std::unique_ptr<PetStable> m_petStable;
|
||||
|
||||
// Temporary removed pet cache
|
||||
uint32 m_temporaryUnsummonedPetNumber;
|
||||
uint32 m_oldpetspell;
|
||||
|
||||
@@ -5375,12 +5375,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
||||
|
||||
uint32 extraflags = fields[36].GetUInt16();
|
||||
|
||||
m_stableSlots = fields[37].GetUInt8();
|
||||
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;
|
||||
}
|
||||
_LoadPetStable(fields[37].GetUInt8(), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS));
|
||||
|
||||
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
|
||||
// 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).
|
||||
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
|
||||
|
||||
@@ -16609,11 +16609,10 @@ bool Unit::IsPetAura(Aura const* aura)
|
||||
return false;
|
||||
|
||||
// if the owner has that pet aura, return true
|
||||
for (PetAuraSet::const_iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->GetAura(GetEntry()) == aura->GetId())
|
||||
for (PetAura const* petAura : owner->m_petAuras)
|
||||
if (petAura->GetAura(GetEntry()) == aura->GetId())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16632,7 +16631,11 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id)
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -16659,6 +16662,11 @@ Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id)
|
||||
|
||||
bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
|
||||
{
|
||||
Player* player = ToPlayer();
|
||||
PetStable& petStable = player->GetOrInitPetStable();
|
||||
if (petStable.CurrentPet || petStable.GetUnslottedHunterPet())
|
||||
return false;
|
||||
|
||||
pet->SetCreatorGUID(GetGUID());
|
||||
pet->SetFaction(GetFaction());
|
||||
pet->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)
|
||||
pet->InitPetCreateSpells();
|
||||
pet->SetFullHealth();
|
||||
pet->FillPetInfo(&petStable.CurrentPet.emplace());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1146,7 +1146,7 @@ enum ActiveStates
|
||||
ACT_DECIDE = 0x00 // custom
|
||||
};
|
||||
|
||||
enum ReactStates
|
||||
enum ReactStates : uint8
|
||||
{
|
||||
REACT_PASSIVE = 0,
|
||||
REACT_DEFENSIVE = 1,
|
||||
|
||||
Reference in New Issue
Block a user