From 7232ead5820b8ea8e30d38e15f2f0aadd4707dfe Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Sat, 30 Sep 2023 17:23:19 +0200 Subject: [PATCH] Core/Pets: added methods for resummoning active class pets and use it when relogging --- src/server/game/AI/CoreAI/PetAI.cpp | 8 +- src/server/game/AI/CreatureAISelector.cpp | 4 +- .../Entities/Creature/TemporarySummon.cpp | 2 +- .../Creature/TemporarySummon/NewPet.cpp | 16 +- src/server/game/Entities/Pet/Pet.cpp | 139 +----------------- src/server/game/Entities/Pet/Pet.h | 2 - src/server/game/Entities/Pet/PetDefines.h | 20 +-- src/server/game/Entities/Player/Player.cpp | 133 +++++++++-------- src/server/game/Entities/Player/Player.h | 13 +- src/server/game/Entities/Unit/Unit.cpp | 7 + src/server/game/Handlers/CharacterHandler.cpp | 8 +- src/server/game/Handlers/NPCHandler.cpp | 2 +- src/server/game/Handlers/PetHandler.cpp | 6 +- src/server/game/Maps/Map.cpp | 11 +- src/server/game/Spells/SpellEffects.cpp | 11 -- src/server/scripts/Commands/cs_npc.cpp | 2 +- src/server/scripts/Commands/cs_pet.cpp | 2 +- src/server/scripts/Spells/spell_generic.cpp | 2 +- 18 files changed, 124 insertions(+), 264 deletions(-) diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 8416b5300a7..3cf39b99e15 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -33,12 +33,8 @@ int32 PetAI::Permissible(Creature const* creature) { - if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - { - if (reinterpret_cast(creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) - return PERMIT_BASE_PROACTIVE; - return PERMIT_BASE_REACTIVE; - } + if (creature->IsPet()) + return PERMIT_BASE_PROACTIVE; return PERMIT_BASE_NO; } diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp index 5e15135e773..91c8cb792de 100644 --- a/src/server/game/AI/CreatureAISelector.cpp +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -81,8 +81,8 @@ namespace FactorySelector CreatureAI* SelectAI(Creature* creature) { // special pet case, if a tamed creature uses AIName (example SmartAI) we need to override it - //if (creature->IsPet()) - // return ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"))->Create(creature); + if (creature->IsPet()) + return ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"))->Create(creature); // scriptname in db try diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 78a82a44cdd..1c5b90fa7ca 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -244,7 +244,7 @@ void TempSummon::UnSummon(uint32 msTime) //ASSERT(!IsPet()); if (IsPet()) { - ((Pet*)this)->Remove(PET_SAVE_DISMISS); + //((Pet*)this)->Remove(PET_SAVE_DISMISS); ASSERT(!IsInWorld()); return; } diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp index 67d98b0edc3..37a18f73423 100644 --- a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp @@ -114,7 +114,12 @@ void NewPet::HandlePostSummonActions() NewGuardian::HandlePostSummonActions(); if (IsHunterPet()) + { SendTalentsInfoUpdateToSummoner(); + CastSpell(nullptr, SPELL_PET_ENERGIZE); + } + else + CastSpell(nullptr, SPELL_SUMMON_HEAL); } bool NewPet::CanBeDismissed() const @@ -138,8 +143,13 @@ void NewPet::Dismiss(bool permanent /*= true*/) UpdatePlayerPetData(player->GetPlayerPetData(_playerPetDataKey->first, _playerPetDataKey->second)); // If the pet has been despawned while dead, we will mark the pet as inactive - if (permanent && !IsAlive()) - player->SetActiveClassPetDataKey(std::nullopt); + if (IsClassPet()) + { + if (permanent) + player->SetActiveClassPetDataKey(std::nullopt); + else + player->SetActiveClassPetDataKey(_playerPetDataKey); + } if (IsClassPet()) { @@ -151,7 +161,7 @@ void NewPet::Dismiss(bool permanent /*= true*/) WorldPackets::Pet::PetDismissSound dismissSound; dismissSound.ModelID = creatureDisplay->ModelID; dismissSound.ModelPosition = GetPosition(); - SendMessageToSet(dismissSound.Write(), false); + player->SendDirectMessage(dismissSound.Write()); } } } diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index c1d204e6713..c9055eab2f9 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -349,134 +349,6 @@ bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool cur return true; } -void Pet::SavePetToDB(PetSaveMode mode) -{ - return; - - if (!GetEntry()) - return; - - // save only fully controlled creature - if (!isControlled()) - return; - - // not save not player pets - if (!GetOwnerOrCreatorGUID().IsPlayer()) - return; - - uint32 curhealth = GetHealth(); - uint32 curmana = GetPower(POWER_MANA); - - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - // save auras before possibly removing them - _SaveAuras(trans); - - _SaveSpells(trans); - GetSpellHistory()->SaveToDB(trans); - CharacterDatabase.CommitTransaction(trans); - - //PlayerPetData* playerPetData = GetOwner()->GetPlayerPetDataById(m_charmInfo->GetPetNumber()); - - // save as new if no data for Pet in PlayerPetDataStore - //if (mode < PET_SAVE_NEW_PET && !playerPetData) - // mode = PET_SAVE_NEW_PET; - - /* - if (mode == PET_SAVE_NEW_PET) - { - Optional slot = IsHunterPet() ? GetOwner()->GetFirstUnusedActivePetSlot() : GetOwner()->GetFirstUnusedPetSlot(); - - if (slot) - { - SetSlot(*slot); - //playerPetData = new PlayerPetData(); - } - else - mode = PET_SAVE_AS_DELETED; - } - */ - - if (mode == PET_SAVE_DISMISS || mode == PET_SAVE_LOGOUT) - RemoveAllAuras(); - - /* - // whole pet is saved to DB - if (mode >= PET_SAVE_CURRENT_STATE) - { - ObjectGuid::LowType ownerLowGUID = GetOwnerOrCreatorGUID().GetCounter(); - std::string name = m_name; - CharacterDatabase.EscapeString(name); - trans = CharacterDatabase.BeginTransaction(); - // remove current data - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - trans->Append(stmt); - - uint32 petId = m_charmInfo->GetPetNumber(); - - // save pet - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET); - stmt->setUInt32(0, petId); - stmt->setUInt32(1, GetEntry()); - stmt->setUInt64(2, ownerLowGUID); - stmt->setUInt32(3, GetNativeDisplayId()); - stmt->setUInt8(4, getLevel()); - stmt->setUInt32(5, GetUInt32Value(UNIT_FIELD_PETEXPERIENCE)); - stmt->setUInt8(6, GetReactState()); - stmt->setInt16(7, m_petSlot); - stmt->setString(8, m_name); - stmt->setUInt8(9, HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ? 0 : 1); - stmt->setUInt8(10, mode == PET_SAVE_DISMISS ? 0 : 1); - stmt->setUInt32(11, curhealth); - stmt->setUInt32(12, curmana); - - stmt->setString(13, GenerateActionBarData()); - - stmt->setUInt32(14, GameTime::GetGameTime()); // unsure about this - stmt->setUInt32(15, GetUInt32Value(UNIT_CREATED_BY_SPELL)); - stmt->setUInt8(16, getPetType()); - trans->Append(stmt); - - CharacterDatabase.CommitTransaction(trans); - - if (m_petSlot > PET_SLOT_LAST) - TC_LOG_ERROR("sql.sql", "Pet::SavePetToDB: bad slot %u for pet %u!", m_petSlot, petId); - - playerPetData->PetId = petId; - playerPetData->CreatureId = GetEntry(); - playerPetData->Owner = ownerLowGUID; - playerPetData->DisplayId = GetNativeDisplayId(); - playerPetData->Petlevel = getLevel(); - playerPetData->PetExp = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE); - playerPetData->Reactstate = GetReactState(); - playerPetData->Slot = m_petSlot; - playerPetData->Name = m_name; - playerPetData->Renamed = HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ? 0 : 1; - playerPetData->Active = mode == PET_SAVE_DISMISS ? 0 : 1; - playerPetData->SavedHealth = curhealth; - playerPetData->SavedMana = curmana; - playerPetData->Actionbar = GenerateActionBarData(); - playerPetData->Timediff = GameTime::GetGameTime(); - playerPetData->SummonSpellId = GetUInt32Value(UNIT_CREATED_BY_SPELL); - playerPetData->Type = getPetType(); - - if (mode == PET_SAVE_NEW_PET) - GetOwner()->AddToPlayerPetDataStore(playerPetData); - - } - // delete - else - { - RemoveAllAuras(); - DeleteFromDB(m_charmInfo->GetPetNumber()); - - GetOwner()->DeleteFromPlayerPetDataStore(m_charmInfo->GetPetNumber()); - GetOwner()->GetSession()->SendStablePet(ObjectGuid::Empty); - } - */ -} - void Pet::DeleteFromDB(ObjectGuid::LowType guidlow) { /* @@ -541,7 +413,7 @@ void Pet::Update(uint32 diff) { //if (!IsHunterPet() || m_corpseRemoveTime <= GameTime::GetGameTime()) { - Remove(PET_SAVE_DISMISS); //hunters' pets never get removed because of death, NEVER! + //Remove(PET_SAVE_DISMISS); //hunters' pets never get removed because of death, NEVER! return; } break; @@ -553,7 +425,7 @@ void Pet::Update(uint32 diff) if ((!IsWithinDistInMap(owner, GetMap()->GetVisibilityRange()) && !isPossessed()) || (isControlled() && !owner->GetSummonGUID())) //if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID())) { - Remove(PET_SAVE_DISMISS, true); + //Remove(PET_SAVE_DISMISS, true); return; } @@ -573,7 +445,7 @@ void Pet::Update(uint32 diff) m_duration -= diff; else { - Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS); + //Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS); return; } } @@ -586,11 +458,6 @@ void Pet::Update(uint32 diff) Creature::Update(diff); } -void Pet::Remove(PetSaveMode mode, bool returnreagent) -{ - //GetOwner()->RemovePet(this, mode, returnreagent); -} - void Pet::GivePetXP(uint32 xp) { //if (!IsHunterPet()) diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index e34471bbaeb..b29d31a824e 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -66,8 +66,6 @@ class TC_GAME_API Pet : public Guardian bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map); bool LoadPetData(Player* owner, uint32 petentry = 0, uint32 petnumber = 0, bool current = false); bool IsLoading() const override { return m_loading;} - void SavePetToDB(PetSaveMode mode); - void Remove(PetSaveMode mode, bool returnreagent = false); static void DeleteFromDB(ObjectGuid::LowType guidlow); void setDeathState(DeathState s) override; // overwrite virtual Creature::setDeathState and Unit::setDeathState diff --git a/src/server/game/Entities/Pet/PetDefines.h b/src/server/game/Entities/Pet/PetDefines.h index a11a7da093b..1ca70d4966e 100644 --- a/src/server/game/Entities/Pet/PetDefines.h +++ b/src/server/game/Entities/Pet/PetDefines.h @@ -25,19 +25,7 @@ enum PetType MAX_PET_TYPE = 4 }; -#define MAX_PET_STABLES 20 - -// stored in character_pet.slot -enum PetSaveMode -{ - PET_SAVE_AS_DELETED = -1, // removes pet from DB and erases from playerPetDataStore - PET_SAVE_UPADTE_SLOT = 0, // not used yet - PET_SAVE_CURRENT_STATE = 1, // Saves everything like it is atm, current = true - PET_SAVE_DISMISS = 2, // Saves everything like it is atm, removes auras and current = false - PET_SAVE_LOGOUT = 3, // Saves everything like it is atm, removes auras and current = true - PET_SAVE_NEW_PET = 4, // Saves everything like it is atm, current = true and pushes new into playerPetDataStore - PET_SAVE_TEMP_UNSUMMON = PET_SAVE_LOGOUT -}; +static constexpr uint8 MAX_PET_STABLES = 20; enum PetCallSpells { @@ -105,8 +93,12 @@ enum StableResultCode : uint8 enum PetSummonSpellIds { // Hunter Pets - SPELL_PET_ENERGIZE = 99289 + SPELL_PET_ENERGIZE = 99289, + + // Pets + SPELL_SUMMON_HEAL = 36492 // serverside Spell }; + // Used by companions (minipets) and quest slot summons constexpr float DEFAULT_FOLLOW_DISTANCE = 2.5f; constexpr float DEFAULT_FOLLOW_DISTANCE_PET = 3.f; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c90422aed14..a9ae280087c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -17745,7 +17745,6 @@ void Player::_LoadMail(PreparedQueryResult mailsResult, PreparedQueryResult mail void Player::_LoadPets(PreparedQueryResult result) { // "SELECT PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar, Talents FROM character_pet WHERE Guid = ? ORDER BY Slot" - if (!result) return; @@ -17781,6 +17780,59 @@ void Player::_LoadPets(PreparedQueryResult result) } while (result->NextRow()); } +void Player::SendPetSpellsMessage(NewPet* pet, bool remove /*= false*/) +{ + CharmInfo* charmInfo = pet->GetCharmInfo(); + if (!charmInfo) + { + TC_LOG_ERROR("entities.pet", "Attempted to send pet spells message for a pet without charmInfo."); + return; + } + + WorldPackets::Pet::PetSpellsMessage packet; + if (!remove) + { + packet.PetGUID = pet->GetGUID(); + packet._CreatureFamily = pet->GetCreatureTemplate()->family; // creature family (required for pet talents) + packet.TimeLimit = pet->GetRemainingSummonDuration().count(); + packet.ReactState = pet->GetReactState(); + packet.CommandState = charmInfo->GetCommandState(); + packet.Flag = 0; + + charmInfo->BuildActionBar(packet.ActionButtons); + if (pet->IsClassPet()) + { + for (auto const& itr : pet->GetSpells()) + { + if (itr.second.State == PETSPELL_REMOVED) + continue; + + packet.Actions.push_back(MAKE_UNIT_ACTION_BUTTON(itr.first, itr.second.Active)); + } + + // Cooldowns + pet->GetSpellHistory()->WritePetSpellHistory(packet); + } + } + + SendDirectMessage(packet.Write()); +} + +void Player::SetCanControlClassPets() +{ + _canControlClassPets = true; + RemoveByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_HIDE_PET_BAR); +} + +bool Player::CanControlClassPets() const +{ + if (ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(getClass())) + if (clsEntry->GetFlags().HasFlag(ChrClassesFlags::PetBarInitiallyHidden)) + return _canControlClassPets; + + return true; +} + PlayerPetData* Player::GetPlayerPetData(uint8 slot, uint32 creatureId) const { auto itr = _playerPetDataMap.find(std::make_pair(slot, creatureId)); @@ -17865,6 +17917,22 @@ void Player::AbandonPet() _activeClassPetDataKey.reset(); } +void Player::ResummonActiveClassPet() +{ + if (!GetSummonGUID().IsEmpty() || !_activeClassPetDataKey.has_value()) + return; + + PlayerPetData* petData = GetPlayerPetData(_activeClassPetDataKey->first, _activeClassPetDataKey->second); + if (!petData) + { + _activeClassPetDataKey.reset(); + return; + } + + uint32 creatureId = petData->TamedCreatureId != 0 ? 0 : petData->CreatureId; + SummonPet(creatureId, petData->Slot, 0, true, GetPosition()); +} + void Player::_LoadQuestStatus(PreparedQueryResult result) { uint16 slot = 0; @@ -20389,59 +20457,6 @@ bool Player::RemoveMItem(uint32 id) return mMitems.erase(id) ? true : false; } -void Player::SendPetSpellsMessage(NewPet* pet, bool remove /*= false*/) -{ - CharmInfo* charmInfo = pet->GetCharmInfo(); - if (!charmInfo) - { - TC_LOG_ERROR("entities.pet", "Attempted to send pet spells message for a pet without charmInfo."); - return; - } - - WorldPackets::Pet::PetSpellsMessage packet; - if (!remove) - { - packet.PetGUID = pet->GetGUID(); - packet._CreatureFamily = pet->GetCreatureTemplate()->family; // creature family (required for pet talents) - packet.TimeLimit = pet->GetRemainingSummonDuration().count(); - packet.ReactState = pet->GetReactState(); - packet.CommandState = charmInfo->GetCommandState(); - packet.Flag = 0; - - charmInfo->BuildActionBar(packet.ActionButtons); - if (pet->IsClassPet()) - { - for (auto const& itr : pet->GetSpells()) - { - if (itr.second.State == PETSPELL_REMOVED) - continue; - - packet.Actions.push_back(MAKE_UNIT_ACTION_BUTTON(itr.first, itr.second.Active)); - } - - // Cooldowns - pet->GetSpellHistory()->WritePetSpellHistory(packet); - } - } - - SendDirectMessage(packet.Write()); -} - -void Player::SetCanControlClassPets() -{ - _canControlClassPets = true; - RemoveByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_HIDE_PET_BAR); -} - -bool Player::CanControlClassPets() const -{ - if (ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(getClass())) - if (clsEntry->GetFlags().HasFlag(ChrClassesFlags::PetBarInitiallyHidden)) - return _canControlClassPets; - - return true; -} - void Player::SendOnCancelExpectedVehicleRideAura() const { WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); @@ -22233,22 +22248,12 @@ inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Player* target, std template inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/) { } -template<> -inline void BeforeVisibilityDestroy(Creature* t, Player* p) -{ - if (p->GetSummonGUID() == t->GetGUID() && t->IsPet()) - t->ToPet()->Remove(PET_SAVE_DISMISS, true); -} - void Player::UpdateVisibilityOf(WorldObject* target) { if (HaveAtClient(target)) { if (!CanSeeOrDetect(target, false, true)) { - if (target->GetTypeId() == TYPEID_UNIT) - BeforeVisibilityDestroy(target->ToCreature(), this); - if (!target->IsDestroyedObject()) target->SendOutOfRangeForPlayer(this); else diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index e42d3723955..cb357d76181 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1564,11 +1564,6 @@ class TC_GAME_API Player : public Unit, public GridObject void AddMItem(Item* it); bool RemoveMItem(uint32 id); - // Pets - void SendPetSpellsMessage(NewPet* pet, bool remove = false); - void SetCanControlClassPets(); - bool CanControlClassPets() const; - public: void SendOnCancelExpectedVehicleRideAura() const; @@ -2381,6 +2376,12 @@ class TC_GAME_API Player : public Unit, public GridObject VoidStorageItem* GetVoidStorageItem(uint64 id, uint8& slot) const; // Pets + // Sends the pet spells message packet to the player, containing all action bar and spell information about the pet + void SendPetSpellsMessage(NewPet* pet, bool remove = false); + // Enables pet controls for classes who have to learn their control pet spell first + void SetCanControlClassPets(); + // Returns true when the player is allowed to perform pet actions with their pet + bool CanControlClassPets() const; // Returns the PlayerPetData of a pet by slot and creatureId. Hunter pets are stored under creatureId = 0, everything else has a valid creatureId. PlayerPetData* GetPlayerPetData(uint8 slot, uint32 creatureId) const; // Returns the PlayerPetData of a pet by pet number. This operation is rather expensive so please consider using Player::GetPlayerPetData(uint8 slot, uint32 creatureId) if possible. @@ -2395,6 +2396,8 @@ class TC_GAME_API Player : public Unit, public GridObject void AbandonPet(); // Sets the pet data key for the class pet that will be un- and resummoned by several actions void SetActiveClassPetDataKey(Optional const& key) { _activeClassPetDataKey = key; } + // Summons the temporarily dismissed pet at the player's location + void ResummonActiveClassPet(); // Returns a reference to the class pet player pet data key Optional const& GetActiveClassPetDataKey() const { return _activeClassPetDataKey; } private: diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 7c471264be1..80fc292b6ca 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -14299,6 +14299,13 @@ NewPet* Unit::SummonPet(uint32 creatureId, uint8 slot, uint32 spellId, bool asCl pet->HandlePostSummonActions(); + if (Player* player = ToPlayer()) + { + WorldPackets::Pet::PetGuids petGuids; + petGuids.PetGUIDs.push_back(pet->GetGUID()); + player->SendDirectMessage(petGuids.Write()); + } + // call MoveInLineOfSight for nearby creatures Trinity::AIRelocationNotifier notifier(*pet); Cell::VisitAllObjects(pet, notifier, GetVisibilityRange()); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 154b30abea8..94c64718f35 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -990,6 +990,10 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder) SendNotification(LANG_RESET_TALENTS); } + if (ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass())) + if (clsEntry->GetFlags().HasFlag(ChrClassesFlags::SendStableAtLogin)) + SendPetStableList(ObjectGuid::Empty); + bool firstLogin = pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST); if (firstLogin) { @@ -1078,9 +1082,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder) } } - if (ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass())) - if (clsEntry->GetFlags().HasFlag(ChrClassesFlags::SendStableAtLogin)) - SendPetStableList(ObjectGuid::Empty); + pCurrChar->ResummonActiveClassPet(); // show time before shutdown if shutdown planned. if (sWorld->IsShuttingDown()) diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 32215aea84a..2a0307fde3e 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -317,7 +317,7 @@ void WorldSession::SendPetStableList(ObjectGuid stableMasterGuid) { WorldPackets::Pet::SPetStableList packet; packet.StableMaster = stableMasterGuid; - packet.StableSlots = PET_SLOT_LAST_STABLE_SLOT; + packet.StableSlots = 20; for (auto const& pair : _player->GetPlayerPetDataMap()) { diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index eeb7f95a833..13c202c9d75 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -107,9 +107,9 @@ void HandlePetActionHelper(NewPet* pet, Player* owner, ObjectGuid targetGuid, ui printf("COMMAND_ATTACK -- %u\n", actionValue); break; case COMMAND_ABANDON: - // Despite its enum name, abandoning pets is done via extra Opcode. This here is merely unsummoning pets - pet->Dismiss(); - owner->SetActiveClassPetDataKey(std::nullopt); + // Despite its enum name, abandoning pets is done via extra Opcode. This here is merely dismissing pets + if (pet->CanBeDismissed()) + pet->Dismiss(); break; case COMMAND_MOVE_TO: if (pet->IsAlive()) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index cc52aa34201..991fd7a77f8 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1151,16 +1151,7 @@ void Map::MoveAllCreaturesInMoveList() #ifdef TRINITY_DEBUG TC_LOG_DEBUG("maps", "Creature (GUID: %u Entry: %u) cannot be move to unloaded respawn grid.", c->GetGUID().GetCounter(), c->GetEntry()); #endif - //AddObjectToRemoveList(Pet*) should only be called in Pet::Remove - //This may happen when a player just logs in and a pet moves to a nearby unloaded cell - //To avoid this, we can load nearby cells when player log in - //But this check is always needed to ensure safety - /// @todo pets will disappear if this is outside CreatureRespawnRelocation - //need to check why pet is frequently relocated to an unloaded cell - if (c->IsPet()) - ((Pet*)c)->Remove(PET_SAVE_DISMISS, true); - else - AddObjectToRemoveList(c); + AddObjectToRemoveList(c); } } } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 52b22cf4f5d..b2bf4650d10 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2645,16 +2645,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) } if (NewPet* summon = unitCaster->SummonPet(petCreatureId, petSlotIndex, m_spellInfo->Id, isClassPet, *destTarget)) - { ExecuteLogEffectSummonObject(effIndex, summon); - - if (unitCaster->IsPlayer() && summon->IsClassPet()) - { - WorldPackets::Pet::PetGuids packet; - packet.PetGUIDs.push_back(summon->GetGUID()); - unitCaster->ToPlayer()->SendDirectMessage(packet.Write()); - } - } } void Spell::EffectLearnPetSpell(SpellEffIndex effIndex) @@ -4022,8 +4013,6 @@ void Spell::EffectDismissPet(SpellEffIndex effIndex) ExecuteLogEffectUnsummonObject(effIndex, unitTarget); unitTarget->ToNewPet()->Dismiss(); - // Dismissing a class pet via dismiss spell or pet action is going to mark a pet as inactive so it needs to be re-summoned - m_caster->ToPlayer()->SetActiveClassPetDataKey(std::nullopt); } void Spell::EffectSummonObject(SpellEffIndex effIndex) diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index a2c40145901..25c6d4a59b1 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -1521,7 +1521,7 @@ class npc_commandscript : public CommandScript // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); - pet->SavePetToDB(PET_SAVE_NEW_PET); + //pet->SavePetToDB(PET_SAVE_NEW_PET); return true; } diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp index 2c0b8e88f7a..134389cda96 100644 --- a/src/server/scripts/Commands/cs_pet.cpp +++ b/src/server/scripts/Commands/cs_pet.cpp @@ -119,7 +119,7 @@ class pet_commandscript : public CommandScript // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel()); - pet->SavePetToDB(PET_SAVE_NEW_PET); + //pet->SavePetToDB(PET_SAVE_NEW_PET); return true; } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 99128829c07..98567a11cfe 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -3781,7 +3781,7 @@ class spell_gen_gm_freeze : public SpellScriptLoader { if (Pet* pet = player->GetPet()) { - pet->SavePetToDB(PET_SAVE_CURRENT_STATE); + //pet->SavePetToDB(PET_SAVE_CURRENT_STATE); // not let dismiss dead pet //if (pet->IsAlive()) // player->RemovePet(pet, PET_SAVE_DISMISS);