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:
Kargatum
2021-12-31 04:45:13 +07:00
committed by GitHub
parent 24ab99919a
commit e12494d993
24 changed files with 1173 additions and 1208 deletions

View File

@@ -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)

View File

@@ -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;

View File

@@ -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