Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Core/Pets: updated hunter pet summoning and resurrection mechanics to…
Browse files Browse the repository at this point in the history
… match sniff and retail behavior
  • Loading branch information
Ovahlord committed Sep 29, 2023
1 parent 5bfc3d7 commit 9422e1e
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 39 deletions.
13 changes: 2 additions & 11 deletions src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,27 +124,18 @@ bool NewPet::CanBeDismissed() const

void NewPet::Dismiss(bool permanent /*= true*/)
{
printf("NewPet::Dismiss\n");
if (IsClassPet() || _playerPetDataKey.has_value())
{
Unit* summoner = GetInternalSummoner();
Unit const* summoner = GetInternalSummoner();
if (summoner && summoner->IsPlayer())
{
Player* player = summoner->ToPlayer();
Player const* player = summoner->ToPlayer();
// Update the pet data before unsummoning the pet
if (_playerPetDataKey.has_value())
{
UpdatePlayerPetData(player->GetPlayerPetData(_playerPetDataKey->first, _playerPetDataKey->second));
printf("set data\n");
}

if (IsClassPet())
{
if (!permanent)
player->SetActiveClassPetDataKey(_playerPetDataKey);
else
player->SetActiveClassPetDataKey(std::nullopt);

// Warlock pets do play dismiss sounds when releasing them permanently
if (permanent && player->getClass() == CLASS_WARLOCK)
{
Expand Down
6 changes: 3 additions & 3 deletions src/server/game/Entities/Creature/TemporarySummon/NewPet.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ class TC_GAME_API NewPet final : public NewGuardian
bool IsClassPet() const { return _isClassPet; }
// Returns true if the pet is a hunter class pet. This is the case when the pet has player pet data and is stored under creatureId = 0
bool IsHunterPet() const { return _playerPetDataKey.has_value() && _playerPetDataKey->second == 0; }
// Returns true if the pet has a player pet data entry
// Returns true if the pet has a player pet data key to acces a player's pet data
bool HasPlayerPetDataKey() { return _playerPetDataKey.has_value(); }
// Returns the player pet data map key for the element that is stored in summoner's player class
Optional<PlayerPetDataKey> const& GetPlayerPetDataKey() { return _playerPetDataKey; }
Optional<PlayerPetDataKey> const& GetPlayerPetDataKey() const { return _playerPetDataKey; }
// Returns true if the summoner is allowed to dismiss the pet via pet action
bool CanBeDismissed() const;
// Unsummons the pet. If permanent is set to false, the pet will be re-summoned after the player has performed certain actions (dismounting etc)
// Unsummons the pet. If permanent is set to true, some pets will play a dismiss sound (such as Warlock pets)
void Dismiss(bool permanent = true);
// Overriden method of TemporarySummon::Unsummon to ensure that Pet::Dismiss is always called.
void Unsummon(Milliseconds timeUntilDespawn = 0ms) override;
Expand Down
13 changes: 10 additions & 3 deletions src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17744,7 +17744,7 @@ 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 FROM character_pet WHERE Guid = ? ORDER BY Slot"
// "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;
Expand All @@ -17771,7 +17771,12 @@ void Player::_LoadPets(PreparedQueryResult result)
petData->Talents = fields[14].GetString();
petData->Status = PlayerPetDataStatus::UpToDate;

_playerPetDataMap.emplace(std::make_pair(petData->Slot, petData->CreatureId), std::move(petData));
std::pair key = std::make_pair(petData->Slot, petData->CreatureId);

if (petData->IsActive)
SetActiveClassPetDataKey(key);

_playerPetDataMap.emplace(key, std::move(petData));

} while (result->NextRow());
}
Expand Down Expand Up @@ -17857,6 +17862,7 @@ void Player::AbandonPet()
pet->Unsummon();
_deletedPlayerPetDataSet.insert(itr->second->PetNumber);
_playerPetDataMap.erase(itr);
_activeClassPetDataKey.reset();
}

void Player::_LoadQuestStatus(PreparedQueryResult result)
Expand Down Expand Up @@ -19812,11 +19818,12 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans)
// Insert new pets and update existing pet data
for (auto const& pair : _playerPetDataMap)
{
bool isActive = _activeClassPetDataKey.has_value() && _activeClassPetDataKey->first == pair.first.first && _activeClassPetDataKey->second == pair.first.second;
PlayerPetData* petData = pair.second.get();
if (petData->Status == PlayerPetDataStatus::UpToDate)
continue;

bool isActive = _activeClassPetDataKey.has_value() && _activeClassPetDataKey->first == pair.first.first && _activeClassPetDataKey->second == pair.first.second;

if (petData->Status == PlayerPetDataStatus::New)
{
// INSERT INTO character_pet (Guid, PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar, Talents) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Expand Down
5 changes: 4 additions & 1 deletion src/server/game/Entities/Player/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -2393,10 +2393,13 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
PlayerPetDataMap const& GetPlayerPetDataMap() const { return _playerPetDataMap; }
// Unsummons the currently active pet and removes the player pet data from its container. The database data will be erased on the next save cycle.
void AbandonPet();
// Sets the pet data key for the class pet that will be un- and resummoned by several actions
void SetActiveClassPetDataKey(Optional<PlayerPetDataKey> const& key) { _activeClassPetDataKey = key; }

// Returns a reference to the class pet player pet data key
Optional<PlayerPetDataKey> const& GetActiveClassPetDataKey() const { return _activeClassPetDataKey; }
private:
Optional<PlayerPetDataKey> _activeClassPetDataKey;

protected:
// Gamemaster whisper whitelist
GuidList WhisperList;
Expand Down
3 changes: 3 additions & 0 deletions src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5587,6 +5587,9 @@ void Unit::SetActivelyControlledSummon(NewPet* pet, bool apply)

player->ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_HIDE_PET_BAR, (apply && !canControlPet));
player->SendPetSpellsMessage(pet, !apply);

if (pet->IsClassPet() && apply)
player->SetActiveClassPetDataKey(pet->GetPlayerPetDataKey());
}
}

Expand Down
1 change: 1 addition & 0 deletions src/server/game/Handlers/PetHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ void HandlePetActionHelper(NewPet* pet, Player* owner, ObjectGuid targetGuid, ui
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);
break;
case COMMAND_MOVE_TO:
if (pet->IsAlive())
Expand Down
96 changes: 75 additions & 21 deletions src/server/game/Spells/SpellEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2603,21 +2603,53 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)

bool isClassPet = m_spellInfo->SpellFamilyName != SPELLFAMILY_GENERIC || petCreatureId == 0;

// Dead Hunter Pets cannot be summoned and must be brought back by a resurrect pet spell effect
if (petCreatureId == 0)
// If a Hunter tries to summon a Hunter pet while having a dead pet, that pet must be resurrected first
if (petCreatureId == 0 && unitCaster->IsPlayer())
{
if (Player* player = unitCaster->ToPlayer())
Player* player = unitCaster->ToPlayer();
Optional<PlayerPetDataKey> const& key = player->GetActiveClassPetDataKey();
if (key.has_value())
{
PlayerPetData* petData = player->GetPlayerPetData(petSlotIndex, petCreatureId);
if (petData && petData->SavedHealth == 0)
PlayerPetData* petData = player->GetPlayerPetData(key->first, key->second);
if (!petData)
{
WorldPackets::Pet::PetTameFailure packet;
packet.Result = PET_TAME_FAILURE_NO_PET_TO_SUMMON;
player->SendDirectMessage(packet.Write());
return;
}

if (petData->SavedHealth == 0)
{
WorldPackets::Pet::PetTameFailure packet;
packet.Result = PET_TAME_FAILURE_DEAD_PET;
player->SendDirectMessage(packet.Write());

unitCaster->GetSpellHistory()->ResetCooldown(GetSpellInfo()->Id, true);
return;
}
}

PlayerPetData* petData = player->GetPlayerPetData(petSlotIndex, petCreatureId);
if (!petData)
{
WorldPackets::Pet::PetTameFailure packet;
packet.Result = PET_TAME_FAILURE_NO_PET_TO_SUMMON;
player->SendDirectMessage(packet.Write());
return;
}
else if (petData->SavedHealth == 0)
{
// This is a fallback edge case to prevent Hunter from hard-locking pet summoning if database data is polluted or someone meddled with the mechanics
player->SetActiveClassPetDataKey(std::make_pair(petData->Slot, petData->CreatureId));
WorldPackets::Pet::PetTameFailure packet;
packet.Result = PET_TAME_FAILURE_DEAD_PET;
player->SendDirectMessage(packet.Write());

unitCaster->GetSpellHistory()->ResetCooldown(GetSpellInfo()->Id, true);
return;
}

}

if (NewPet* summon = unitCaster->SummonPet(petCreatureId, petSlotIndex, m_spellInfo->Id, isClassPet, *destTarget))
Expand Down Expand Up @@ -3998,6 +4030,8 @@ 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)
Expand Down Expand Up @@ -4644,25 +4678,45 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/)
return;

NewPet* pet = player->GetActivelyControlledSummon();
if (!pet || pet->IsAlive())
return;

pet->NearTeleportTo(m_targets.GetDstPos()->GetPosition());
pet->Relocate(m_targets.GetDstPos()->GetPosition()); // This is needed so SaveStayPosition() will get the proper coords.
pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
pet->setDeathState(ALIVE);
pet->ClearUnitState(UNIT_STATE_ALL_ERASABLE);
pet->SetHealth(pet->CountPctFromMaxHealth(damage));
if (pet && !pet->IsAlive())
{
pet->NearTeleportTo(m_targets.GetDstPos()->GetPosition());
pet->Relocate(m_targets.GetDstPos()->GetPosition()); // This is needed so SaveStayPosition() will get the proper coords.
pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
pet->setDeathState(ALIVE);
pet->ClearUnitState(UNIT_STATE_ALL_ERASABLE);
pet->SetHealth(pet->CountPctFromMaxHealth(damage));

// Reset things for when the AI to takes over
if (CharmInfo* charmInfo = pet->GetCharmInfo())
{
if (charmInfo->HasCommandState(COMMAND_FOLLOW))
pet->FollowTarget(player);

// Reset things for when the AI to takes over
if (CharmInfo* charmInfo = pet->GetCharmInfo())
if (charmInfo->HasCommandState(COMMAND_STAY))
charmInfo->SaveStayPosition();
}
}
else if (!pet)
{
if (charmInfo->HasCommandState(COMMAND_FOLLOW))
pet->FollowTarget(player);
Optional<PlayerPetDataKey> const& key = player->GetActiveClassPetDataKey();
if (!key.has_value())
return;

if (charmInfo->HasCommandState(COMMAND_STAY))
charmInfo->SaveStayPosition();
PlayerPetData* petData = player->GetPlayerPetData(key->first, key->second);
if (petData && petData->SavedHealth == 0)
{
if (pet = player->SummonPet(key->second, key->first, m_spellInfo->Id, true, m_targets.GetDstPos()->GetPosition()))
{
pet->Relocate(m_targets.GetDstPos()->GetPosition()); // This is needed so SaveStayPosition() will get the proper coords.
pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
pet->setDeathState(ALIVE);
pet->ClearUnitState(UNIT_STATE_ALL_ERASABLE);
pet->SetHealth(pet->CountPctFromMaxHealth(damage));
}
}
}
}

Expand Down

0 comments on commit 9422e1e

Please sign in to comment.