From 6f85882eb9145c51433f0d77b6ba4b9cb360432b Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sat, 5 Oct 2024 09:11:54 -0500 Subject: [PATCH 1/2] reorganize pet classes --- src/server/game/AI/CoreAI/CombatAI.cpp | 128 +++++++++--------- .../game/Entities/Creature/Creature.cpp | 46 +------ src/server/game/Entities/Creature/Creature.h | 42 +----- .../Entities/Creature/TemporarySummon.cpp | 1 - .../game/Entities/Creature/TemporarySummon.h | 9 -- src/server/game/Entities/Pet/Pet.cpp | 27 ++-- src/server/game/Entities/Pet/Pet.h | 44 +++--- src/server/game/Entities/Pet/PetDefines.h | 73 ++++++++++ src/server/game/Entities/Player/Player.cpp | 2 +- src/server/game/Entities/Totem/Totem.cpp | 1 - src/server/game/Entities/Unit/StatSystem.cpp | 3 +- src/server/game/Maps/Map.cpp | 2 +- src/server/game/Spells/SpellEffects.cpp | 64 +++++---- 13 files changed, 222 insertions(+), 220 deletions(-) create mode 100644 src/server/game/Entities/Pet/PetDefines.h diff --git a/src/server/game/AI/CoreAI/CombatAI.cpp b/src/server/game/AI/CoreAI/CombatAI.cpp index 90157af2..501b4c9b 100644 --- a/src/server/game/AI/CoreAI/CombatAI.cpp +++ b/src/server/game/AI/CoreAI/CombatAI.cpp @@ -712,88 +712,90 @@ void AnyPetAI::UpdateAI(uint32 diff) TargetSpellList targetSpellStore; // TC_LOG_DEBUG("misc", "AnyPetAI::UpdateAI GetPetCastSpellSize %i owner %u victim %u target %u", me->GetPetCastSpellSize(), bool(owner), bool(owner ? owner->getAttackerForHelper() : 0), bool(target)); - for (uint8 i = 0; i < me->GetPetCastSpellSize(); ++i) + if (Pet* pet = me->ToPet()) { - uint32 spellID = me->GetPetCastSpellOnPos(i); - if (!spellID) - continue; + for (uint8 i = 0; i < pet->m_castspells.size(); ++i) + { + uint32 spellID = pet->m_castspells[i]; + if (!spellID) + continue; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); - if (!spellInfo) - continue; + SpellInfo const *spellInfo = sSpellMgr->GetSpellInfo(spellID); + if (!spellInfo) + continue; - // TC_LOG_DEBUG("misc", "AnyPetAI::UpdateAI spellID %i, Cooldown %i IsPositive %i CanBeUsedInCombat %i GUID %u", - // spellID, me->HasCreatureSpellCooldown(spellID), spellInfo->IsPositive(), spellInfo->CanBeUsedInCombat(), me->GetGUIDLow()); + // TC_LOG_DEBUG("misc", "AnyPetAI::UpdateAI spellID %i, Cooldown %i IsPositive %i CanBeUsedInCombat %i GUID %u", + // spellID, me->HasCreatureSpellCooldown(spellID), spellInfo->IsPositive(), spellInfo->CanBeUsedInCombat(), me->GetGUIDLow()); - if (me->HasCreatureSpellCooldown(spellID)) - continue; + if (me->HasCreatureSpellCooldown(spellID)) + continue; - if (spellInfo->IsPositive()) - { - if (spellInfo->CanBeUsedInCombat()) + if (spellInfo->IsPositive()) { - // Check if we're in combat - if (!me->isInCombat()) - continue; - } + if (spellInfo->CanBeUsedInCombat()) + { + // Check if we're in combat + if (!me->isInCombat()) + continue; + } - TriggerCastData triggerData; - auto spell = new Spell(me, spellInfo, triggerData); + TriggerCastData triggerData; + auto spell = new Spell(me, spellInfo, triggerData); - if (target) - { - if (me->IsWithinMeleeRange(target, me->GetAttackDist()) && spell->CanAutoCast(target)) + if (target) { - targetSpellStore.push_back(std::make_pair(target, spell)); - break; + if (me->IsWithinMeleeRange(target, me->GetAttackDist()) && spell->CanAutoCast(target)) + { + targetSpellStore.push_back(std::make_pair(target, spell)); + break; + } } - } - // No enemy, check friendly - bool spellUsed = false; - for (auto tar : m_AllySet) - { - Unit* ally = ObjectAccessor::GetUnit(*me, tar); + // No enemy, check friendly + bool spellUsed = false; + for (auto tar: m_AllySet) + { + Unit *ally = ObjectAccessor::GetUnit(*me, tar); - //only buff targets that are in combat, unless the spell can only be cast while out of combat - if (!ally) - continue; + //only buff targets that are in combat, unless the spell can only be cast while out of combat + if (!ally) + continue; - if (spell->CanAutoCast(ally)) - { - targetSpellStore.push_back(std::make_pair(ally, spell)); - spellUsed = true; - break; + if (spell->CanAutoCast(ally)) + { + targetSpellStore.push_back(std::make_pair(ally, spell)); + spellUsed = true; + break; + } } - } - // No valid targets at all - if (!spellUsed) - delete spell; - } - else if (spellInfo->IsTargetingAreaCast()) - { - if (!me->HasAuraType(SPELL_AURA_DISABLE_ATTACK_AND_CAST)) + // No valid targets at all + if (!spellUsed) + delete spell; + } else if (spellInfo->IsTargetingAreaCast()) { - if (target) - me->CastSpell(target, spellInfo, false); - else - me->CastSpell(me, spellInfo, false); + if (!me->HasAuraType(SPELL_AURA_DISABLE_ATTACK_AND_CAST)) + { + if (target) + me->CastSpell(target, spellInfo, false); + else + me->CastSpell(me, spellInfo, false); - me->AddCreatureSpellCooldown(spellInfo->Id); + me->AddCreatureSpellCooldown(spellInfo->Id); + } + } else if (target/* && me->IsWithinMeleeRange(target, me->GetAttackDist())*/ && + ((me->isInCombat() && spellInfo->CanBeUsedInCombat()) || !me->isInCombat())) + { + TriggerCastData triggerData; + auto spell = new Spell(me, spellInfo, triggerData); + if (spell->CanAutoCast(target)) + targetSpellStore.push_back(std::make_pair(target, spell)); + else + delete spell; } - } - else if (target/* && me->IsWithinMeleeRange(target, me->GetAttackDist())*/ && ((me->isInCombat() && spellInfo->CanBeUsedInCombat()) || !me->isInCombat())) - { - TriggerCastData triggerData; - auto spell = new Spell(me, spellInfo, triggerData); - if (spell->CanAutoCast(target)) - targetSpellStore.push_back(std::make_pair(target, spell)); - else - delete spell; - } - // else + // else // TC_LOG_DEBUG("misc", "AnyPetAI::UpdateAI not cast spellID %i", spellID); + } } //found units to cast on to diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 632b7b82..27336242 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -21,6 +21,7 @@ #include "CombatPackets.h" #include "Common.h" #include "Chat.h" +#include "CharmInfo.h" #include "Creature.h" #include #include "CreatureAI.h" @@ -2315,33 +2316,12 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const return false; } -uint8 Creature::GetPetAutoSpellSize() const -{ - return m_autospells.size(); -} - uint32 Creature::GetPetAutoSpellOnPos(uint8 pos) const { - if (pos >= m_autospells.size()) - return 0; - return m_autospells[pos]; -} - -uint8 Creature::GetPetCastSpellSize() const -{ - return m_castspells.size(); -} - -void Creature::AddPetCastSpell(uint32 spellid) -{ - m_castspells.push_back(spellid); -} - -uint32 Creature::GetPetCastSpellOnPos(uint8 pos) const -{ - if (pos >= m_castspells.size()) + if (pos >= MAX_SPELL_CHARM || m_charmInfo->GetCharmSpell(pos)->GetType() != ACT_ENABLED) return 0; - return m_castspells[pos]; + else + return m_charmInfo->GetCharmSpell(pos)->GetAction(); } void Creature::DeleteFromDB() @@ -3731,24 +3711,6 @@ bool Creature::IsPlayerRewarded(ObjectGuid targetGuid) const void Creature::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) { - for (uint8 i = 0; i < GetPetCastSpellSize(); ++i) - { - uint32 spellID = GetPetCastSpellOnPos(i); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); - if (!spellInfo) - continue; - - // Not send cooldown for this spells - if (spellInfo->HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE)) - continue; - - if (!(spellInfo->Categories.PreventionType & SPELL_PREVENTION_TYPE_SILENCE)) - continue; - - if ((idSchoolMask & spellInfo->GetSchoolMask()) && _GetSpellCooldownDelay(spellID) < unTimeMs) - _AddCreatureSpellCooldown(spellID, time(nullptr) + unTimeMs/IN_MILLISECONDS); - } - for (uint8 i = 0; (1 << i) < SPELL_SCHOOL_MASK_ALL; ++i) if ((1 << i) & idSchoolMask) m_CreatureSchoolCooldowns[(1 << i)] = time(nullptr) + unTimeMs / IN_MILLISECONDS; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 9f419225..5ba8e8fd 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -522,38 +522,6 @@ struct TrainerSpellData typedef std::map CreatureSpellCooldowns; -enum PetSpellState -{ - PETSPELL_UNCHANGED = 0, - PETSPELL_CHANGED = 1, - PETSPELL_NEW = 2, - PETSPELL_REMOVED = 3 -}; - -enum PetSpellType -{ - PETSPELL_NORMAL = 0, - PETSPELL_FAMILY = 1, - PETSPELL_TALENT = 2, -}; - -enum PetType -{ - SUMMON_PET = 0, - HUNTER_PET = 1, - MAX_PET_TYPE = 4, -}; - -struct PetSpell -{ - ActiveStates active; - PetSpellState state; - PetSpellType type; -}; - -typedef std::unordered_map PetSpellMap; -typedef std::vector AutoSpellList; - // max different by z coordinate for creature aggro reaction #define CREATURE_Z_ATTACK_RANGE 3 @@ -786,11 +754,8 @@ class Creature : public Unit, public GridObject, public MapObject bool isRegeneratingHealth() { return m_regenHealth; } void setRegeneratingHealth(bool regenHealth) { m_regenHealth = regenHealth; } - uint8 GetPetAutoSpellSize() const; - uint32 GetPetAutoSpellOnPos(uint8 pos) const; - uint8 GetPetCastSpellSize() const; - void AddPetCastSpell(uint32 spellid); - uint32 GetPetCastSpellOnPos(uint8 pos) const; + virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; } + virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const; void SetPosition(float x, float y, float z, float o); void SetPosition(const Position& pos); @@ -850,9 +815,6 @@ class Creature : public Unit, public GridObject, public MapObject void SetLockAI(bool lock) { m_AI_locked = lock; } - AutoSpellList m_autospells; - AutoSpellList m_castspells; - PetSpellMap m_spells; bool m_despawn; bool m_isHati; uint32 m_IfUpdateTimer; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index ecccabd7..8d5757eb 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -451,7 +451,6 @@ void TempSummon::UnSummon(uint32 msTime) onUnload = true; - CastPetAuras(false); //ASSERT(!isPet()); if (isPet()) { diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index 7139b474..66d15d09 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -59,17 +59,8 @@ class TempSummon : public Creature TempSummonType const& GetSummonType() { return m_type; } uint32 GetTimer() { return m_timer; } void AddDuration(uint32 time) { m_timer += time; } - void CastPetAuras(bool current, uint32 spellId = 0); - bool addSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL); - bool removeSpell(uint32 spell_id); - void LearnPetPassives(); - void InitLevelupSpellsForLevel(); void UpdateAttackPowerAndDamage(bool ranged = false) override; - bool learnSpell(uint32 spell_id); - bool unlearnSpell(uint32 spell_id); - void ToggleAutocast(SpellInfo const* spellInfo, bool apply); - PetType getPetType() const { return m_petType; } void setPetType(PetType type) { m_petType = type; } diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 58532483..66d75887 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -311,6 +311,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) _LoadSpells(); _LoadSpellCooldowns(); LearnPetPassives(); + InitLevelupSpellsForLevel(); if (owner->HasSpell(108415)) // active talent Soul Link CastSpell(this, 108446, true); @@ -399,7 +400,7 @@ void Pet::SavePetToDB(bool isDelete) PetSlot curentSlot = GetSlot(); - //not delete, just remove from curent slot + //not delete, just remove from current slot if((m_owner->getClass() == CLASS_WARLOCK || m_owner->getClass() == CLASS_DEATH_KNIGHT) && isDelete) owner->m_currentPetNumber = 0; @@ -739,6 +740,7 @@ void Pet::GivePetLevel(uint8 level) } InitStatsForLevel(level); + InitLevelupSpellsForLevel(); } bool Pet::CreateBaseAtCreature(Creature* creature) @@ -902,14 +904,11 @@ bool Guardian::InitStatsForLevel(uint8 petlevel, bool initSpells) else SetUInt32Value(UNIT_FIELD_PET_NEXT_LEVEL_EXPERIENCE, 2147483647); - if (initSpells) - InitLevelupSpellsForLevel(); - SetFullHealth(); return true; } -void TempSummon::ToggleAutocast(SpellInfo const* spellInfo, bool apply) +void Pet::ToggleAutocast(SpellInfo const* spellInfo, bool apply) { if (!spellInfo->IsAutocastable()) return; @@ -1297,7 +1296,7 @@ void Pet::_SaveAuras(CharacterDatabaseTransaction& trans) } } -bool TempSummon::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpellState state /*= PETSPELL_NEW*/, PetSpellType type /*= PETSPELL_NORMAL*/) +bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpellState state /*= PETSPELL_NEW*/, PetSpellType type /*= PETSPELL_NORMAL*/) { //TC_LOG_ERROR("misc", "TempSummon::addSpell spellId %i Entry %i", spellId, GetEntry()); @@ -1378,7 +1377,7 @@ bool TempSummon::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, if (spellInfo->IsPassive() && (!spellInfo->AuraRestrictions.CasterAuraState || HasAuraState(AuraStateType(spellInfo->AuraRestrictions.CasterAuraState)))) CastSpell(this, spellId, true); else - AddPetCastSpell(spellId); + m_castspells.push_back(spellId); if(GetCasterPet() && spellInfo->GetMaxRange(false) > GetAttackDist() && (spellInfo->AttributesCu[0] & SPELL_ATTR0_CU_DIRECT_DAMAGE) && !spellInfo->IsTargetingAreaCast()) SetAttackDist(spellInfo->GetMaxRange(false)); @@ -1399,7 +1398,7 @@ bool TempSummon::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, return true; } -bool TempSummon::learnSpell(uint32 spellID) +bool Pet::learnSpell(uint32 spellID) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) @@ -1417,7 +1416,7 @@ bool TempSummon::learnSpell(uint32 spellID) return true; } -void TempSummon::InitLevelupSpellsForLevel() +void Pet::InitLevelupSpellsForLevel() { uint8 level = getLevel(); @@ -1475,7 +1474,7 @@ void TempSummon::InitLevelupSpellsForLevel() } } -bool TempSummon::unlearnSpell(uint32 spellID) +bool Pet::unlearnSpell(uint32 spellID) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) @@ -1493,7 +1492,7 @@ bool TempSummon::unlearnSpell(uint32 spellID) return false; } -bool TempSummon::removeSpell(uint32 spell_id) +bool Pet::removeSpell(uint32 spell_id) { auto itr = m_spells.find(spell_id); if (itr == m_spells.end()) @@ -1548,6 +1547,8 @@ void Pet::InitPetCreateSpells() LearnPetPassives(); InitLevelupSpellsForLevel(); + + CastPetAuras(false); } bool Pet::IsPermanentPetFor(Player* owner) @@ -1600,7 +1601,7 @@ bool Pet::HasSpell(uint32 spell) return itr != m_spells.end() && itr->second.state != PETSPELL_REMOVED; } -void TempSummon::LearnPetPassives() +void Pet::LearnPetPassives() { CreatureTemplate const* cInfo = GetCreatureTemplate(); if (!cInfo) @@ -1616,7 +1617,7 @@ void TempSummon::LearnPetPassives() addSpell(spell, ACT_DECIDE, PETSPELL_NEW, PETSPELL_FAMILY); } -void TempSummon::CastPetAuras(bool apply, uint32 spellId) +void Pet::CastPetAuras(bool apply, uint32 spellId) { // TC_LOG_DEBUG("spells", "Pet::CastPetAuras guid %u, apply %u, GetEntry() %u IsInWorld %u", GetGUIDLow(), apply, GetEntry(), IsInWorld()); diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index b7bc261a..03265622 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -19,23 +19,9 @@ #ifndef PET_H #define PET_H -#include "Unit.h" +#include "PetDefines.h" #include "TemporarySummon.h" -enum ActionFeedback -{ - FEEDBACK_NONE = 0, - FEEDBACK_PET_DEAD = 1, - FEEDBACK_NOTHING_TO_ATT = 2, - FEEDBACK_CANT_ATT_TARGET = 3 -}; - -enum PetTalk -{ - PET_TALK_SPECIAL_SPELL = 0, - PET_TALK_ATTACK = 1 -}; - enum PetNameInvalidReason { // custom, not send @@ -58,8 +44,15 @@ enum PetNameInvalidReason #define ACTIVE_SPELLS_MAX 4 -#define PET_FOLLOW_DIST 1.0f -#define PET_FOLLOW_ANGLE (M_PI/2) +struct PetSpell +{ + ActiveStates active; + PetSpellState state; + PetSpellType type; +}; + +typedef std::unordered_map PetSpellMap; +typedef std::vector AutoSpellList; class Player; @@ -100,10 +93,16 @@ class Pet : public Guardian void SetDuration(int32 dur) { m_duration = dur; } int32 GetDuration() { return m_duration; } + void ToggleAutocast(SpellInfo const* spellInfo, bool apply); + bool HasSpell(uint32 spell) override; + void LearnPetPassives(); + void CastPetAuras(bool current, uint32 spellId = 0); bool IsPetAura(Aura const* aura); + void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override; + void _LoadSpellCooldowns(); void _SaveSpellCooldowns(CharacterDatabaseTransaction& trans); void _LoadAuras(uint32 timediff); @@ -111,12 +110,20 @@ class Pet : public Guardian void _LoadSpells(); void _SaveSpells(CharacterDatabaseTransaction& trans); + bool addSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL); + bool learnSpell(uint32 spell_id); + void InitLevelupSpellsForLevel(); + bool unlearnSpell(uint32 spell_id); + bool removeSpell(uint32 spell_id); void CleanupActionBar(); void InitPetCreateSpells(); DeclinedName const* GetDeclinedNames() const { return m_declinedname; } + AutoSpellList m_autospells; + AutoSpellList m_castspells; + PetSpellMap m_spells; bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved) Unit* GetOwner() { return m_owner; } @@ -126,8 +133,7 @@ class Pet : public Guardian void LearnSpecializationSpell(); void UnlearnSpecializationSpell(); void CheckSpecialization(); - void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override; - + uint32 GetGroupUpdateFlag() const { return m_groupUpdateMask; } void SetGroupUpdateFlag(uint32 flag); void ResetGroupUpdateFlag(); diff --git a/src/server/game/Entities/Pet/PetDefines.h b/src/server/game/Entities/Pet/PetDefines.h new file mode 100644 index 00000000..a3322306 --- /dev/null +++ b/src/server/game/Entities/Pet/PetDefines.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITYCORE_PET_DEFINES_H +#define TRINITYCORE_PET_DEFINES_H + +#define MAX_PET_STABLES 4 + +enum PetType +{ + SUMMON_PET = 0, + HUNTER_PET = 1, + MAX_PET_TYPE = 4, +}; + +// stored in character_pet.slot +enum PetSaveMode +{ + PET_SAVE_AS_DELETED = -1, // not saved in fact + PET_SAVE_AS_CURRENT = 0, // in current slot (with player) + PET_SAVE_FIRST_STABLE_SLOT = 1, + PET_SAVE_LAST_STABLE_SLOT = MAX_PET_STABLES, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT + PET_SAVE_NOT_IN_SLOT = 100 // for avoid conflict with stable size grow will use 100 +}; + +enum PetSpellState +{ + PETSPELL_UNCHANGED = 0, + PETSPELL_CHANGED = 1, + PETSPELL_NEW = 2, + PETSPELL_REMOVED = 3 +}; + +enum PetSpellType +{ + PETSPELL_NORMAL = 0, + PETSPELL_FAMILY = 1, + PETSPELL_TALENT = 2, +}; + +enum ActionFeedback +{ + FEEDBACK_NONE = 0, + FEEDBACK_PET_DEAD = 1, + FEEDBACK_NOTHING_TO_ATT = 2, + FEEDBACK_CANT_ATT_TARGET = 3 +}; + +enum PetTalk +{ + PET_TALK_SPECIAL_SPELL = 0, + PET_TALK_ATTACK = 1 +}; + +#define PET_FOLLOW_DIST 1.0f +#define PET_FOLLOW_ANGLE (M_PI/2) + +#endif \ No newline at end of file diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index cb439a60..95382783 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -26581,7 +26581,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy pet->Relocate(x, y, z, ang); - //summoned pets always non-curent! + //summoned pets always non-current! if (petType == SUMMON_PET || petType == HUNTER_PET) { // This check done in LoadPetFromDB, but we should not continue this function if pet not alowed diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index f4af8d7d..f597d06f 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -139,7 +139,6 @@ void Totem::UnSummon(uint32 msTime) CombatStop(); RemoveAurasDueToSpell(GetSpell(), GetGUID()); - CastPetAuras(false); // clear owner's totem slot for (int i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 7727926d..2299aa11 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -2461,7 +2461,8 @@ void Player::UpdateCRSpeed() SetFloatValue(PLAYER_FIELD_SPEED, std::max(0.0f, val)); for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) - UpdateSpeed(UnitMoveType(i), true); + if (i != MOVE_TURN_RATE && i != MOVE_PITCH_RATE) // unsupported in UpdateSpeed + UpdateSpeed(UnitMoveType(i), true); } void Player::UpdateLifesteal() diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 6eca5140..15703e2d 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -4828,7 +4828,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert AddToMap(summon->ToCreature()); summon->InitSummon(); - summon->CastPetAuras(true); + //summon->CastPetAuras(true); //TC_LOG_DEBUG("misc", "Map::SummonCreature summoner %u entry %i mask %i", summoner ? summoner->GetGUID() : 0, entry, mask); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 8222ea8b..034d3717 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -7734,6 +7734,9 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* if (caster->isTotem()) caster = caster->ToTotem()->GetOwner(); + // in another case summon new + uint8 level = caster->getLevel(); + float radius = 5.0f; int32 duration = m_spellInfo->GetDuration(m_diffMode); if (!numGuardians) @@ -7766,20 +7769,22 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* return; if (summon->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) { - for (uint8 idx = 0; idx < summon->GetPetCastSpellSize(); ++idx) - { - if (SpellInfo const* sInfo = sSpellMgr->GetSpellInfo(summon->GetPetCastSpellOnPos(idx))) - { - if (sInfo->GetMaxRange(false) >= 30.0f && sInfo->GetMaxRange(false) > summon->GetAttackDist() && (sInfo->AttributesCu[0] & SPELL_ATTR0_CU_DIRECT_DAMAGE) && !sInfo->IsTargetingAreaCast()) - { - PetStats const* pStats = sObjectMgr->GetPetStats(entry); - if (!pStats) - summon->SetCasterPet(true); - if (!sInfo->IsPositive()) - summon->SetAttackDist(sInfo->GetMaxRange(false)); - } - } - } + ((Guardian*)summon)->InitStatsForLevel(level); + +// for (uint8 idx = 0; idx < summon->GetPetCastSpellSize(); ++idx) +// { +// if (SpellInfo const* sInfo = sSpellMgr->GetSpellInfo(summon->GetPetCastSpellOnPos(idx))) +// { +// if (sInfo->GetMaxRange(false) >= 30.0f && sInfo->GetMaxRange(false) > summon->GetAttackDist() && (sInfo->AttributesCu[0] & SPELL_ATTR0_CU_DIRECT_DAMAGE) && !sInfo->IsTargetingAreaCast()) +// { +// PetStats const* pStats = sObjectMgr->GetPetStats(entry); +// if (!pStats) +// summon->SetCasterPet(true); +// if (!sInfo->IsPositive()) +// summon->SetAttackDist(sInfo->GetMaxRange(false)); +// } +// } +// } } if (properties && properties->Control == SUMMON_CATEGORY_ALLY) @@ -7796,21 +7801,22 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* summon->SetDisplayId(1126); } - if (summon->GetEntry() == 100820 && m_caster->IsPlayer()) - { - switch (summon->GetDisplayId()) - { - case 66843: - summon->addSpell(224125); - break; - case 66844: - summon->addSpell(224126); - break; - case 66845: - summon->addSpell(224127); - break; - } - } + // TODO: fix spirit wolf in waycrest manor +// if (summon->GetEntry() == 100820 && m_caster->IsPlayer()) +// { +// switch (summon->GetDisplayId()) +// { +// case 66843: +// summon->addSpell(224125); +// break; +// case 66844: +// summon->addSpell(224126); +// break; +// case 66845: +// summon->addSpell(224127); +// break; +// } +// } if (Player* plr = caster->ToPlayer()) { From a0868f7e5e74efba350a3a54aed07a5493ed51d8 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:21:12 -0500 Subject: [PATCH 2/2] rework pet loading and saving --- .../characters/0006_character_pet_slot.sql | 11 + .../Implementation/CharacterDatabase.cpp | 37 +- .../Implementation/CharacterDatabase.h | 22 +- src/server/game/Battlegrounds/Arena.cpp | 2 +- .../game/Battlegrounds/Battleground.cpp | 2 +- .../Entities/Creature/TemporarySummon.cpp | 2 +- src/server/game/Entities/Pet/Pet.cpp | 412 ++++++++-------- src/server/game/Entities/Pet/Pet.h | 7 +- src/server/game/Entities/Player/Player.cpp | 441 +++++------------- src/server/game/Entities/Player/Player.h | 38 +- src/server/game/Entities/Unit/Unit.cpp | 29 +- src/server/game/Entities/Unit/Unit.h | 2 +- src/server/game/Handlers/CharacterHandler.cpp | 15 +- src/server/game/Handlers/LoginQueryHolder.cpp | 4 - src/server/game/Handlers/MiscHandler.cpp | 2 +- src/server/game/Handlers/PetHandler.cpp | 264 ++++++----- src/server/game/Maps/Map.cpp | 2 +- src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.cpp | 5 +- src/server/game/Server/WorldSession.h | 2 +- src/server/game/Spells/SpellEffects.cpp | 19 +- .../game/Tools/CharacterDatabaseCleaner.cpp | 38 +- src/server/game/Tools/PlayerDump.cpp | 2 +- src/server/scripts/Commands/cs_misc.cpp | 6 +- src/server/scripts/Commands/cs_npc.cpp | 2 +- src/server/scripts/Spells/spell_generic.cpp | 2 +- 26 files changed, 595 insertions(+), 775 deletions(-) create mode 100644 sql/updates/characters/0006_character_pet_slot.sql diff --git a/sql/updates/characters/0006_character_pet_slot.sql b/sql/updates/characters/0006_character_pet_slot.sql new file mode 100644 index 00000000..d3f917fd --- /dev/null +++ b/sql/updates/characters/0006_character_pet_slot.sql @@ -0,0 +1,11 @@ +ALTER TABLE `character_pet` + ADD COLUMN `slot` tinyint(3) unsigned NOT NULL DEFAULT '0'; + +ALTER TABLE `character_pet` + ADD KEY `idx_slot` (`slot`); + +-- current pet is now stored in character_pet.slot 0 +ALTER TABLE `characters` + DROP COLUMN `currentpetnumber`, + DROP COLUMN `petslot`, + ADD COLUMN `stable_slots` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `extra_flags`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index c803cab7..3af3fd91 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -50,16 +50,18 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_MAIL_LIST_INFO, "SELECT id, sender, (SELECT name FROM characters WHERE guid = sender) AS sendername, receiver, (SELECT name FROM characters WHERE guid = receiver) AS receivername," "SUBJECT, deliver_time, expire_time, money, has_items FROM mail WHERE receiver = ? ", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_MAIL_LIST_ITEMS, "SELECT itemEntry,count FROM item_instance WHERE guid = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.blindfold, c.gender, c.tattoo, c.horn, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, c.logout_time, c.specialization FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.id = c.currentpetnumber LEFT JOIN guild_member AS gm ON c.guid = gm.guid LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_ENUM_DELETED, "SELECT c.guid, c.name, c.race, c.class, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.blindfold, c.gender, c.tattoo, c.horn, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, c.logout_time, c.specialization FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.id = c.currentpetnumber LEFT JOIN guild_member AS gm ON c.guid = gm.guid LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.deleteInfos_Account = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.blindfold, c.gender, c.tattoo, c.horn, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, cd.genitive, c.logout_time, c.specialization FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.id = c.currentpetnumber LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.blindfold, c.gender, c.tattoo, c.horn, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, c.logout_time, c.specialization FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_ENUM_DELETED, "SELECT c.guid, c.name, c.race, c.class, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.blindfold, c.gender, c.tattoo, c.horn, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, c.logout_time, c.specialization FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.deleteInfos_Account = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.blindfold, c.gender, c.tattoo, c.horn, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, cd.genitive, c.logout_time, c.specialization FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ?", CONNECTION_ASYNC); // Pet - PrepareStatement(CHAR_INS_PET, "REPLACE INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_DETAIL, "SELECT id, entry, level, name, modelid FROM character_pet WHERE owner = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_BY_ID, "SELECT entry, id, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET, "SELECT `id`, `entry`, `owner`, `modelid`, `level`, `exp`, `Reactstate`, `name`, `renamed`, `curhealth`, `curmana`, `abdata`, `savetime`, `CreatedBySpell`, `PetType`, `specialization` FROM character_pet WHERE `owner` = ?", CONNECTION_ASYNC); + 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_PET_SLOTS_DETAIL, "SELECT owner, id, entry, level, name, modelid FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PET, "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, entry FROM character_pet 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_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_PET_DECLINED_BY_ID, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_PET_AURA_BY_ID, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_PET_SPELL_BY_ID, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC); @@ -95,10 +97,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Start LoginQueryHolder content PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, blindfold, gender, tattoo, horn, inventorySlots, bankSlots, drunk, playerFlags, " "playerFlagsEx, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, " - "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, " + "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, " "totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, " "health, mana, instance_id, activespec, specialization, lootspecialization, exploredZones, equipmentCache, knownTitles, actionBars, " - "currentpetnumber, petslot, grantableLevels, lfgBonusFaction, raidDifficulty, legacyRaidDifficulty, created_time, killPoints FROM characters WHERE guid = ?", CONNECTION_ASYNC); + "grantableLevels, lfgBonusFaction, raidDifficulty, legacyRaidDifficulty, created_time, killPoints FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT caster_guid, slot, spell, effect_mask, recalculate_mask, stackcount, maxduration, remaintime, remaincharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURAS_EFFECTS, "SELECT slot, effect, baseamount, amount FROM character_aura_effect WHERE guid = ?", CONNECTION_ASYNC); @@ -463,17 +465,17 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Player saving PrepareStatement(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, blindfold, gender, tattoo, horn, inventorySlots, bankSlots, drunk, playerFlags, " "playerFlagsEx, map, instance_id, dungeonDifficulty, raidDifficulty, legacyRaidDifficulty, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, " - "taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, extra_flags, at_login, zone, " + "taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, extra_flags, stable_slots, at_login, zone, " "death_expire_time, taxi_path, totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, health, mana, " - "latency, activespec, specialization, lootspecialization, exploredZones, equipmentCache, knownTitles, actionBars, currentpetnumber, petslot, grantableLevels, created_time, killPoints) VALUES " - "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); + "latency, activespec, specialization, lootspecialization, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, created_time, killPoints) VALUES " + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,level=?,xp=?,money=?,skin=?,face=?,hairStyle=?,hairColor=?,facialStyle=?,blindfold=?,gender=?,tattoo=?,horn=?,inventorySlots=?,bankSlots=?,drunk=?,playerFlags=?," "playerFlagsEx=?,map=?,instance_id=?,dungeonDifficulty=?,raidDifficulty=?,legacyRaidDifficulty=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?," - "logout_time=?,is_logout_resting=?,extra_flags=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," + "logout_time=?,is_logout_resting=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," "totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?," "watchedFaction=?,health=?,mana=?,latency=?,activespec=?,specialization=?,lootspecialization=?,exploredZones=?," - "equipmentCache=?,knownTitles=?,actionBars=?,currentpetnumber=?,petslot=?,grantableLevels=?,online=?,lfgBonusFaction=?,killPoints=? WHERE guid=?", CONNECTION_ASYNC); + "equipmentCache=?,knownTitles=?,actionBars=?,grantableLevels=?,online=?,lfgBonusFaction=?,killPoints=? WHERE guid=?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG, "UPDATE characters SET at_login = at_login | ? WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); @@ -566,6 +568,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() 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_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_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_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC); @@ -578,6 +583,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_PET_SPELL, "INSERT IGNORE INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PET_AURA, "INSERT IGNORE INTO pet_aura (guid, slot, caster_guid, spell, effect_mask, recalculate_mask, stackcount, maxduration, remaintime, remaincharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PET_AURA_EFFECT, "INSERT IGNORE INTO pet_aura_effect (guid, slot, effect, baseamount, amount) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_FACTION_OR_RACE, "UPDATE characters SET name = ?, race = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 92a3392a..27b5668e 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -52,16 +52,27 @@ enum CharacterDatabaseStatements : uint32 CHAR_SEL_ENUM_DECLINED_NAME, // Pet - CHAR_SEL_PET, - CHAR_INS_PET, - CHAR_SEL_PET_DETAIL, - CHAR_SEL_PET_BY_ID, + CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, + CHAR_SEL_PET_SLOTS, + CHAR_SEL_PET_SLOTS_DETAIL, + CHAR_SEL_PET_SLOT_BY_ID, + CHAR_SEL_CHAR_PET_BY_ENTRY, + CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, + CHAR_SEL_CHAR_PET_BY_SLOT, CHAR_DEL_CHAR_PET_BY_ID, + CHAR_DEL_CHAR_PET_BY_SLOT, CHAR_DEL_CHAR_PET_DECLINED_BY_ID, CHAR_DEL_CHAR_PET_AURA_BY_ID, CHAR_DEL_CHAR_PET_SPELL_BY_ID, CHAR_DEL_CHAR_PET_SPELL_COOLDOWN_BY_ID, CHAR_DEL_INVALID_PET_SPELL, + CHAR_DEL_CHAR_PET_DECLINEDNAME, + CHAR_ADD_CHAR_PET_DECLINEDNAME, + CHAR_UPD_CHAR_PET_NAME, + CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, + CHAR_UPD_CHAR_PET_SLOT_BY_SLOT, + CHAR_UPD_CHAR_PET_SLOT_BY_ID, + CHAR_INS_PET, CHAR_SEL_FREE_NAME, CHAR_SEL_GUID_RACE_ACC_BY_NAME, @@ -471,9 +482,6 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, CHAR_DEL_MAIL_ITEM_BY_ID, - CHAR_DEL_CHAR_PET_DECLINEDNAME, - CHAR_ADD_CHAR_PET_DECLINEDNAME, - CHAR_UPD_CHAR_PET_NAME, CHAR_INS_PETITION, CHAR_DEL_PETITION_BY_GUID, CHAR_DEL_PETITION_SIGNATURE_BY_GUID, diff --git a/src/server/game/Battlegrounds/Arena.cpp b/src/server/game/Battlegrounds/Arena.cpp index cbe2ccdf..ae8e4665 100644 --- a/src/server/game/Battlegrounds/Arena.cpp +++ b/src/server/game/Battlegrounds/Arena.cpp @@ -111,7 +111,7 @@ void Arena::AddPlayer(Player* player) if (player->HasSpell(155228) || player->HasSpell(205024) || player->GetSpecializationId() == SPEC_MAGE_FIRE && player->GetSpecializationId() == SPEC_MAGE_ARCANE || player->GetSpecializationId() == SPEC_DK_BLOOD || player->GetSpecializationId() == SPEC_DK_FROST) - player->RemovePet(pet); + player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); } player->RemoveArenaEnchantments(TEMP_ENCHANTMENT_SLOT); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index e0120f8a..185b000b 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1268,7 +1268,7 @@ void Battleground::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool Sen if (IsArena() || IsRBG()) { bgTypeId = IsArena() ? MS::Battlegrounds::BattlegroundTypeId::ArenaAll : MS::Battlegrounds::BattlegroundTypeId::RatedBattleground; - player->RemovePet(nullptr); + player->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT); player->ResummonPetTemporaryUnSummonedIfAny(); if (IsRated() && GetStatus() == STATUS_IN_PROGRESS) diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 8d5757eb..805875d2 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -454,7 +454,7 @@ void TempSummon::UnSummon(uint32 msTime) //ASSERT(!isPet()); if (isPet()) { - ToPet()->Remove(); + ToPet()->Remove(PET_SAVE_NOT_IN_SLOT); ASSERT(!IsInWorld()); return; } diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 66d75887..ec6e4390 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -103,159 +103,148 @@ void Pet::RemoveFromWorld() } } -bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) +bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current) { - if (owner->getClass() == CLASS_WARLOCK) - if (owner->HasAura(196099)) - owner->RemoveAura(196099); + m_loading = true; - if (owner->IsPlayer() && isControlled() && !isTemporarySummoned()) - owner->SetLastPetEntry(petentry); + ObjectGuid::LowType ownerid = owner->GetGUID().GetCounter(); - m_loading = true; - uint32 ownerid = owner->GetGUIDLow(); + CharacterDatabasePreparedStatement* stmt; + PreparedQueryResult result; - // find number - if (!petnumber && !petentry) + if (petnumber) { - if(owner->m_currentSummonedSlot < PET_SLOT_HUNTER_FIRST) - { - TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error m_currentSummonedSlot < 0"); - m_loading = false; - return false; - } - petnumber = owner->getPetIdBySlot(owner->m_currentSummonedSlot); + // known petnumber entry + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY); + stmt->setUInt64(0, ownerid); + stmt->setUInt32(1, petnumber); } - - TC_LOG_DEBUG("spells", "LoadPetFromDB petentry %i, petnumber %i, ownerid %i slot %i m_currentPet %i", petentry, petnumber, ownerid, owner->m_currentSummonedSlot, owner->m_currentPetNumber); - - PetInfoData* petInfo = owner->GetPetInfo(petentry, petnumber); - if (!petInfo) + else if (current) { - TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error result"); - m_loading = false; - return false; + // current pet (slot 0) + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT); + stmt->setUInt64(0, ownerid); + stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT)); } - - // update for case of current pet "slot = 0" - petentry = petInfo->entry; - if (!petentry) + else if (petEntry) { - TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error petentry"); - m_loading = false; - return false; + // known petEntry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2); + stmt->setUInt64(0, ownerid); + stmt->setUInt32(1, petEntry); + stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT)); + stmt->setUInt8(3, uint8(PET_SAVE_LAST_STABLE_SLOT)); } - - // Double call if use Player::SummonPet - // But this option for check through Player::LoadPet - if (!owner->CanSummonPet(petentry)) + else { - TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error CanSummonPet"); - m_loading = false; - return false; + // Any current or other non-stabled pet (for hunter "call pet") + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_SLOT); + stmt->setUInt64(0, ownerid); + stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT)); + stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT)); } - uint32 summon_spell_id = petInfo->CreatedBySpell; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon_spell_id); - - bool is_temporary_summoned = spellInfo && spellInfo->GetDuration(GetSpawnMode()) > 0; + result = CharacterDatabase.Query(stmt); - // check temporary summoned pets like mage water elemental - if (is_temporary_summoned) + if (!result) { m_loading = false; - TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error is_temporary_summoned %i", is_temporary_summoned); return false; } - setPetType(petInfo->pet_type); + Field* fields = result->Fetch(); + + // update for case of current pet "slot = 0" + petEntry = fields[1].GetUInt32(); + if (!petEntry) + return false; + + uint32 summonSpellId = fields[14].GetUInt32(); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summonSpellId); - if (owner->IsPlayer() && isControlled() && !isTemporarySummoned()) - owner->SetLastPetEntry(petentry); + bool isTemporarySummon = spellInfo && spellInfo->GetDuration(GetSpawnMode()) > 0; + if (current && isTemporarySummon) + return false; - if (petInfo->pet_type == HUNTER_PET) + PetType petType = PetType(fields[15].GetUInt8()); + if (petType == HUNTER_PET) { - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petentry); + CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry); if (!creatureInfo || !creatureInfo->isTameable(owner)) return false; } - if (owner->IsPetNeedBeTemporaryUnsummoned()) + uint32 petId = fields[0].GetUInt32(); + + if (current && owner->IsPetNeedBeTemporaryUnsummoned()) { - TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error IsPetNeedBeTemporaryUnsummoned"); - owner->SetTemporaryUnsummonedPetNumber(petInfo->id); + owner->SetTemporaryUnsummonedPetNumber(petId); return false; } Map* map = owner->GetMap(); - ObjectGuid::LowType guid = sObjectMgr->GetGenerator()->Generate(); - if (!Create(guid, map, owner->GetPhaseMask(), petentry, petInfo->id)) + if (!Create(sObjectMgr->GetGenerator()->Generate(), map, owner->GetPhaseMask(), petEntry, petId)) { TC_LOG_DEBUG("misc", "Pet::LoadPetFromDB error Create"); return false; } - if(petnumber) - { - Position pos; - owner->GetFirstCollisionPosition(pos, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - Relocate(pos.m_positionX, pos.m_positionY, pos.m_positionZ, owner->GetOrientation()); - - if (petInfo->pet_type == HUNTER_PET) - owner->m_currentPetNumber = petnumber; - } - - if (!IsPositionValid()) - { - TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", - GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); - return false; - } - SetPhaseId(owner->GetPhases(), false); + setPetType(petType); setFaction(owner->getFaction()); - SetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL, summon_spell_id); + SetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL, summonSpellId); - CreatureTemplate const* cinfo = GetCreatureTemplate(); - if (cinfo->Type == CREATURE_TYPE_CRITTER) + if (GetCreatureType() == CREATURE_TYPE_CRITTER) { - SetTratsport(owner->GetTransport()); + float px, py, pz; + owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, GetFollowAngle()); + Relocate(px, py, pz, owner->GetOrientation()); + + if (!IsPositionValid()) + { + TC_LOG_ERROR("entities.pet", "Pet (%s, entry %u) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", + GetGUID().ToString().c_str(), GetEntry(), GetPositionX(), GetPositionY()); + return false; + } + map->AddToMap(this->ToCreature()); return true; } - m_charmInfo->SetPetNumber(petInfo->id, IsPermanentPetFor(owner)); +// CreatureTemplate const* cinfo = GetCreatureTemplate(); +// if (cinfo->Type == CREATURE_TYPE_CRITTER) +// { +// SetTratsport(owner->GetTransport()); +// map->AddToMap(this->ToCreature()); +// return true; +// } - SetDisplayId(petInfo->modelid); - SetNativeDisplayId(petInfo->modelid); - SetUInt32Value(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - SetName(petInfo->name); + m_charmInfo->SetPetNumber(petId, IsPermanentPetFor(owner)); - // ignore model info data for - SetFloatValue(UNIT_FIELD_BOUNDING_RADIUS, DEFAULT_WORLD_OBJECT_SIZE); - SetFloatValue(UNIT_FIELD_COMBAT_REACH, DEFAULT_COMBAT_REACH); + SetDisplayId(fields[3].GetUInt32()); + SetNativeDisplayId(fields[3].GetUInt32()); + uint32 petlevel = fields[4].GetUInt16(); + SetUInt64Value(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + SetName(fields[8].GetString()); switch (getPetType()) { case SUMMON_PET: { - petInfo->level = owner->GetEffectiveLevel(); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - // this enables popup window (pet dismiss, cancel) + petlevel = owner->GetEffectiveLevel(); + SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // this enables popup window (pet dismiss, cancel) if (owner->getClass() == CLASS_WARLOCK) SetClass(CLASS_ROGUE); break; } case HUNTER_PET: { - SetSheath(SHEATH_STATE_MELEE); - SetClass(CLASS_HUNTER); + SetClass(CLASS_WARRIOR); SetGender(GENDER_NONE); + SetSheath(SHEATH_STATE_MELEE); - SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, petInfo->renamed ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - // this enables popup window (pet abandon, cancel) - SetSpecialization(petInfo->specialization); + SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); + SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // this enables popup window (pet abandon, cancel) break; } default: @@ -267,21 +256,30 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped here SetCreatorGUID(owner->GetGUID()); + InitStatsForLevel(petlevel); + SetUInt32Value(UNIT_FIELD_PET_EXPERIENCE, fields[5].GetUInt32()); + if (owner->IsPlayer()) AddUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER); - SetReactState(petInfo->reactstate); - if (GetReactState() == REACT_AGGRESSIVE) - SetReactState(REACT_DEFENSIVE); + SynchronizeLevelWithOwner(); - InitStatsForLevel(petInfo->level); - SetUInt32Value(UNIT_FIELD_PET_EXPERIENCE, petInfo->exp); + // Set pet's position after setting level, its size depends on it + float px, py, pz; + owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, GetFollowAngle()); + Relocate(px, py, pz, owner->GetOrientation()); + if (!IsPositionValid()) + { + TC_LOG_ERROR("entities.pet", "Pet (%s, entry %u) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", + GetGUID().ToString().c_str(), GetEntry(), GetPositionX(), GetPositionY()); + return false; + } - SynchronizeLevelWithOwner(); + SetReactState(ReactStates(fields[6].GetUInt8())); SetCanModifyStats(true); - uint32 savedhealth = petInfo->curhealth; - uint32 savedmana = petInfo->curmana; + uint32 savedhealth = fields[10].GetUInt32(); + uint32 savedmana = fields[11].GetUInt32(); if (!savedhealth && getPetType() == HUNTER_PET) setDeathState(JUST_DIED); @@ -293,25 +291,51 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) SetTratsport(owner->GetTransport()); + // 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 (fields[7].GetUInt8()) + { + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID); + stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT)); + stmt->setUInt64(1, ownerid); + stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT)); + stmt->setUInt32(3, m_charmInfo->GetPetNumber()); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID); + stmt->setUInt8(0, uint8(PET_SAVE_AS_CURRENT)); + stmt->setUInt64(1, ownerid); + stmt->setUInt32(2, m_charmInfo->GetPetNumber()); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); + } + owner->SetMinion(this, true); map->AddToMap(this->ToCreature()); setActive(true); - SetSlot(owner->m_currentSummonedSlot); - - auto timediff = uint32(time(nullptr) - petInfo->savetime); + uint32 timediff = uint32(time(nullptr) - fields[13].GetUInt32()); _LoadAuras(timediff); - if (owner->IsPlayer() && owner->ToPlayer()->InArena()) - RemoveArenaAuras(); - // load action bar, if data broken will fill later by default spells. - m_charmInfo->LoadPetActionBar(petInfo->abdata); + if (!isTemporarySummon) + { + m_charmInfo->LoadPetActionBar(fields[12].GetString()); - _LoadSpells(); - _LoadSpellCooldowns(); - LearnPetPassives(); - InitLevelupSpellsForLevel(); + _LoadSpells(); + _LoadSpellCooldowns(); + LearnPetPassives(); + InitLevelupSpellsForLevel(); + if (map->IsBattleArena()) + RemoveArenaAuras(); + + CastPetAuras(current); + } if (owner->HasSpell(108415)) // active talent Soul Link CastSpell(this, 108446, true); @@ -331,8 +355,18 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) CastCustomSpell(this, 218955, &bp0, nullptr, nullptr, true); } - CleanupActionBar(); // remove unknown spells from action bar after load - owner->PetSpellInitialize(); + uint16 specId = fields[16].GetUInt16(); + if (ChrSpecializationEntry const* petSpec = sChrSpecializationStore.LookupEntry(specId)) + specId = sDB2Manager.GetChrSpecializationByIndex(owner->HasAuraType(SPELL_AURA_OVERRIDE_PET_SPECS) ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0, petSpec->OrderIndex)->ID; + + SetSpecialization(specId); + + // The SetSpecialization function will run these functions if the pet's spec is not 0 + if (!GetSpecializationId()) + { + CleanupActionBar(); // remove unknown spells from action bar after load + owner->PetSpellInitialize(); + } TC_LOG_DEBUG("misc", "New Pet has guid %u", GetGUIDLow()); @@ -342,10 +376,10 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) if (getPetType() == HUNTER_PET) { - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME); stmt->setUInt64(0, owner->GetGUIDLow()); stmt->setUInt32(1, GetCharmInfo()->GetPetNumber()); - PreparedQueryResult result = CharacterDatabase.Query(stmt); + result = CharacterDatabase.Query(stmt); if (result) { @@ -357,21 +391,19 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber) m_declinedname->name[i] = fields2[i].GetString(); } } - CheckSpecialization(); } //set last used pet number (for use in BG's) if (owner->IsPlayer() && isControlled() && !isTemporarySummoned() && getPetType() == HUNTER_PET) - owner->ToPlayer()->SetLastPetNumber(petInfo->id); + owner->ToPlayer()->SetLastPetNumber(petId); - CastPetAuras(getDeathState() == ALIVE); m_loading = false; //owner->SetFlag(PLAYER_FIELD_LOCAL_FLAGS, PLAYER_LOCAL_FLAG_CONTROLLING_PET); return true; } -void Pet::SavePetToDB(bool isDelete) +void Pet::SavePetToDB(PetSaveMode mode) { //TC_LOG_DEBUG("spells", "Pet SavePetToDB"); if (!GetEntry()) @@ -381,44 +413,30 @@ void Pet::SavePetToDB(bool isDelete) if (!isControlled()) return; - //Don`t save temporary pets - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL)); - bool is_temporary_summoned = spellInfo && spellInfo->GetDuration(GetSpawnMode()) > 0; - if (is_temporary_summoned) - return; - - if (!GetOwner()) + // not save not player pets + if (!GetOwnerGUID().IsPlayer()) return; - auto owner = GetOwner()->ToPlayer(); - if (!owner || !owner->GetGUID().IsPlayer()) // not save not player pets - return; - - // In some cases pet could get 0 id (stamped case) - if (!m_charmInfo->GetPetNumber()) - return; - - PetSlot curentSlot = GetSlot(); - - //not delete, just remove from current slot - if((m_owner->getClass() == CLASS_WARLOCK || m_owner->getClass() == CLASS_DEATH_KNIGHT) && isDelete) - owner->m_currentPetNumber = 0; + Player* owner = GetOwner()->ToPlayer(); // not save pet as current if another pet temporary unsummoned - if (owner->GetTemporaryUnsummonedPetNumber() && owner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber()) + if (mode == PET_SAVE_AS_CURRENT && owner->GetTemporaryUnsummonedPetNumber() && + owner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber()) { // pet will lost anyway at restore temporary unsummoned if (getPetType() == HUNTER_PET) return; + + // for warlock case + mode = PET_SAVE_NOT_IN_SLOT; } uint32 curhealth = GetHealth(); uint32 curmana = GetPower(POWER_MANA); CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - // save auras before possibly removing them - if (getPetType() == HUNTER_PET) - _SaveAuras(trans); + _SaveAuras(trans); + _SaveSpells(trans); _SaveSpellCooldowns(trans); CharacterDatabase.CommitTransaction(trans); @@ -426,47 +444,64 @@ void Pet::SavePetToDB(bool isDelete) // TC_LOG_DEBUG("spells", "SavePetToDB petentry %i, petnumber %i, slotID %i ownerid %i GetNativeDisplayId %u", GetEntry(), m_charmInfo->GetPetNumber(), curentSlot, GetOwnerGUID().GetGUIDLow(), GetNativeDisplayId()); // current/stable/not_in_slot - if (!isDelete) + if (mode >= PET_SAVE_AS_CURRENT) { - // save pet - uint8 index = 0; - std::ostringstream ss; + ObjectGuid::LowType ownerLowGUID = GetOwnerGUID().GetCounter(); + trans = CharacterDatabase.BeginTransaction(); + // remove current data - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET); - stmt->setUInt32(index++, m_charmInfo->GetPetNumber()); - stmt->setUInt32(index++, GetEntry()); - stmt->setUInt64(index++, GetOwnerGUID().GetGUIDLow()); - stmt->setUInt32(index++, GetNativeDisplayId()); - stmt->setUInt32(index++, getLevel()); - stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_PET_EXPERIENCE)); - stmt->setUInt8(index++, GetReactState()); - stmt->setString(index++, GetName()); - stmt->setUInt8(index++, HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ? 0 : 1); - stmt->setUInt32(index++, curhealth); - stmt->setUInt32(index++, curmana); - - ss.str(""); - for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i) + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); + stmt->setUInt32(0, m_charmInfo->GetPetNumber()); + trans->Append(stmt); + + // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) + if (mode <= PET_SAVE_LAST_STABLE_SLOT) { - ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' ' - << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' '; - }; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT); + stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT)); + stmt->setUInt64(1, ownerLowGUID); + stmt->setUInt8(2, uint8(mode)); + trans->Append(stmt); + } - stmt->setString(index++, ss.str()); + // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT + if (getPetType() == HUNTER_PET && (mode == PET_SAVE_AS_CURRENT || mode > PET_SAVE_LAST_STABLE_SLOT)) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_SLOT); + stmt->setUInt64(0, ownerLowGUID); + stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT)); + stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT)); + trans->Append(stmt); + } - stmt->setUInt32(index++, time(nullptr)); - stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL)); - stmt->setUInt8(index++, getPetType()); - stmt->setUInt32(index++, GetSpecializationId()); + // save pet + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET); + stmt->setUInt32(0, m_charmInfo->GetPetNumber()); + stmt->setUInt32(1, GetEntry()); + stmt->setUInt64(2, ownerLowGUID); + stmt->setUInt32(3, GetNativeDisplayId()); + stmt->setUInt8(4, getLevel()); + stmt->setUInt32(5, GetUInt32Value(UNIT_FIELD_PET_EXPERIENCE)); + stmt->setUInt8(6, GetReactState()); + stmt->setInt16(7, mode); + 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->setUInt32(10, curhealth); + stmt->setUInt32(11, curmana); + + stmt->setString(12, GenerateActionBarData()); + + stmt->setUInt32(13, time(nullptr)); + stmt->setUInt32(14, GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL)); + stmt->setUInt8(15, getPetType()); + stmt->setUInt16(16, m_specialization); + trans->Append(stmt); - CharacterDatabase.Execute(stmt); + CharacterDatabase.CommitTransaction(trans); } // delete else { - owner->DeletePetInfo(m_charmInfo->GetPetNumber()); - if((curentSlot >= PET_SLOT_HUNTER_FIRST && curentSlot <= owner->GetMaxCurentPetSlot())) - owner->cleanPetSlotForMove(curentSlot, m_charmInfo->GetPetNumber()); //could be already remove by early call this function RemoveAllAuras(); DeleteFromDB(m_charmInfo->GetPetNumber()); } @@ -547,7 +582,7 @@ void Pet::Update(uint32 diff) { if (getPetType() != HUNTER_PET || m_corpseRemoveTime <= time(nullptr)) { - Remove(); //hunters' pets never get removed because of death, NEVER! + Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER! m_isUpdate = false; return; } @@ -561,7 +596,7 @@ void Pet::Update(uint32 diff) { if (!petOwner || !GetTransport() || GetTransport() != petOwner->GetTransport()) // waiting full teleport owner { - Remove(); + Remove(PET_SAVE_NOT_IN_SLOT, true); m_isUpdate = false; return; } @@ -572,7 +607,7 @@ void Pet::Update(uint32 diff) if (petOwner->GetPetGUID() != GetGUID()) // Stampede { TC_LOG_ERROR("misc", "Pet %u is not pet of owner %s, removed", GetEntry(), m_owner->GetName()); - Remove(); + Remove(getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT); m_isUpdate = false; return; } @@ -584,7 +619,7 @@ void Pet::Update(uint32 diff) m_duration -= diff; else { - Remove(); + Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT); m_isUpdate = false; return; } @@ -722,10 +757,10 @@ void Creature::Regenerate(Powers power, uint32 diff) } } -void Pet::Remove() +void Pet::Remove(PetSaveMode mode, bool returnreagent) { if (Player* plr = m_owner->ToPlayer()) - plr->RemovePet(this); + plr->RemovePet(this, mode, returnreagent); } void Pet::GivePetLevel(uint8 level) @@ -2219,3 +2254,16 @@ bool Pet::isTemporarySummoned() const { return m_duration > 0; } + +std::string Pet::GenerateActionBarData() const +{ + std::ostringstream ss; + + for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i) + { + ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' ' + << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' '; + } + + return ss.str(); +} \ No newline at end of file diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 03265622..40204fee 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -74,10 +74,10 @@ class Pet : public Guardian bool CreateBaseAtCreature(Creature* creature); bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner); bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phaseMask); - bool LoadPetFromDB(Player* owner, uint32 petentry = 0, uint32 petnumber = 0); + bool LoadPetFromDB(Player* owner, uint32 petEntry = 0, uint32 petnumber = 0, bool current = false); bool isBeingLoaded() const override { return m_loading;} - void SavePetToDB(bool isDelete = false); - void Remove(); + void SavePetToDB(PetSaveMode mode); + void Remove(PetSaveMode mode, bool returnreagent = false); static void DeleteFromDB(uint32 guidlow); void setDeathState(DeathState s) override; // overwrite virtual Creature::setDeathState and Unit::setDeathState @@ -116,6 +116,7 @@ class Pet : public Guardian bool unlearnSpell(uint32 spell_id); bool removeSpell(uint32 spell_id); void CleanupActionBar(); + std::string GenerateActionBarData() const; void InitPetCreateSpells(); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 95382783..80c40779 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -181,9 +181,6 @@ m_achievementMgr(sf::safe_ptr>(this)) m_speakCount = 0; CustomMultiDonate = 0; - m_currentPetNumber = 0; - m_currentSummonedSlot = PET_SLOT_HUNTER_FIRST; - m_objectType |= TYPEMASK_PLAYER; m_objectTypeId = TYPEID_PLAYER; @@ -298,7 +295,6 @@ m_achievementMgr(sf::safe_ptr>(this)) //when dying/logging out m_oldpetspell = 0; m_lastpetnumber = 0; - m_LastPetEntry = 0; //kill honor sistem m_flushKills = false; @@ -314,6 +310,8 @@ m_achievementMgr(sf::safe_ptr>(this)) for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) m_forced_speed_changes[i] = 0; + m_stableSlots = 0; + /////////////////// Instance System ///////////////////// m_HomebindTimer = 0; @@ -401,7 +399,6 @@ m_achievementMgr(sf::safe_ptr>(this)) memset(_voidStorageItems, 0, sizeof(_voidStorageItems)); - m_PetSlots.resize(PET_SLOT_LAST, 0); realmTransferid = 0; m_scenarioId = 0; @@ -546,7 +543,6 @@ Player::~Player() m_itemDuration.clear(); m_itemSoulboundTradeable.clear(); m_channels.clear(); - m_PetSlots.clear(); _battlePets.clear(); AllArtifacts.clear(); _oldPetBattleSpellToMerge.clear(); @@ -1982,7 +1978,7 @@ void Player::setDeathState(DeathState s) ClearResurrectRequestData(); //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD) - RemovePet(nullptr); + RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true); // save value before aura remove in Unit::setDeathState InitializeSelfResurrectionSpells(); @@ -3598,7 +3594,7 @@ void Player::SetSpectate(bool on) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); if (auto pet = GetPet()) - RemovePet(pet); + RemovePet(pet, PET_SAVE_NOT_IN_SLOT); UnsummonPetTemporaryIfAny(); ResetContestedPvP(); @@ -5332,8 +5328,7 @@ void Player::learnSpell(uint32 spell_id, bool dependent, uint32 fromSkill, bool } case 2: //remove pet { - RemovePet(NULL); - m_currentPetNumber = 0; + RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT); m_temporaryUnsummonedPetNumber = 0; break; } @@ -5509,8 +5504,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo } case 2: //remove pet { - RemovePet(NULL); - m_currentPetNumber = 0; + RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT); m_temporaryUnsummonedPetNumber = 0; break; } @@ -6118,7 +6112,7 @@ bool Player::ResetTalents(bool no_cost) } } - RemovePet(NULL); + RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true); for (TalentEntry const* talentInfo : sTalentStore) if (talentInfo->ClassID == getClass() && talentInfo->SpellID) @@ -21636,21 +21630,6 @@ void Player::_LoadTransmogOutfits(PreparedQueryResult result) } while (result->NextRow()); } -void Player::_LoadPetData(PreparedQueryResult result) -{ - if (!result) - return; - - do - { - Field* fields = result->Fetch(); - m_petInfo[fields[0].GetUInt32()].UpdateData(fields); - - TC_LOG_ERROR("misc", "_LoadPetData: pet_number %u", fields[0].GetUInt32()); - - } while (result->NextRow()); -} - void Player::_LoadBGData(PreparedQueryResult result) { if (!result) @@ -21876,10 +21855,10 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder) { f_guid, f_account, f_name, f_race, f_class, f_level, f_xp, f_money, f_skin, f_face, f_hairstyle, f_haircolor, f_facialhair, f_blindfold, f_gender, f_tattoo, f_horn, f_inventorySlots, f_bankslots, f_drunk, f_playerFlags, f_playerFlagsEx, f_position_x, f_position_y, f_position_z, f_map, f_orientation, f_taximask, f_cinematic, f_totaltime, f_leveltime, f_rest_bonus, f_logout_time, f_is_logout_resting, - f_trans_x, f_trans_y, f_trans_z, f_trans_o, f_transguid, f_extra_flags, f_at_login, f_zone, f_online, f_death_expire_time, f_taxi_path, f_dungeonDifficulty, + f_trans_x, f_trans_y, f_trans_z, f_trans_o, f_transguid, f_extra_flags, f_stable_slots, f_at_login, f_zone, f_online, f_death_expire_time, f_taxi_path, f_dungeonDifficulty, f_totalKills, f_todayKills, f_yesterdayKills, f_chosenTitle, f_watchedFaction, f_health, f_mana, f_instance_id, f_activespec, f_specialization, f_lootspecialization, f_exploredZones, f_equipmentCache, f_knownTitles, f_actionBars, - f_currentpetnumber, f_petslot, f_grantableLevels, f_lfgBonusFaction, f_raidDifficulty, f_legacyRaidDifficulty, f_created_time, + f_grantableLevels, f_lfgBonusFaction, f_raidDifficulty, f_legacyRaidDifficulty, f_created_time, f_killPoints }; @@ -21994,9 +21973,6 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder) SetByteValue(PLAYER_FIELD_BYTES_4, PLAYER_BYTES_4_ACTION_BAR_TOGGLES, fields[f_actionBars].GetUInt8()); - m_currentPetNumber = fields[f_currentpetnumber].GetInt32(); - LoadPetSlot(fields[f_petslot].GetCString()); - InitDisplayIds(); // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory) @@ -22065,8 +22041,6 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder) _LoadBoundInstances(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES)); _LoadBGData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBGDATA)); - _LoadPetData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_PETS)); - GetSession()->SetPlayer(this); MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); @@ -22358,6 +22332,14 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder* holder) uint32 extraflags = fields[f_extra_flags].GetUInt16(); + m_stableSlots = fields[43].GetUInt8(); + if (m_stableSlots > MAX_PET_STABLES) + { + TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) can't have more stable slots than %u, but has %u in DB", + GetGUID().ToString().c_str(), MAX_PET_STABLES, uint32(m_stableSlots)); + m_stableSlots = MAX_PET_STABLES; + } + // Honor system // Update Honor kills data m_lastHonorUpdateTime = logoutTime; @@ -23799,10 +23781,10 @@ void Player::LoadPet() { //fixme: the pet should still be loaded if the player is not in world // just not added to the map - if (m_currentPetNumber && IsInWorld()) + if (IsInWorld()) { Pet* pet = new Pet(this); - if (!pet->LoadPetFromDB(this, 0, m_currentPetNumber)) + if (!pet->LoadPetFromDB(this, 0, 0, true)) delete pet; } } @@ -24828,25 +24810,6 @@ bool Player::CheckInstanceLoginValid() return sMapMgr->CanPlayerEnter(GetMap()->GetId(), this, true); } -PetInfoData* Player::GetPetInfo(uint32 petentry, uint32 petnumber) -{ - if (petnumber) - return Trinity::Containers::MapGetValuePtr(m_petInfo, petnumber); - - if (petentry) - for (auto itr = m_petInfo.begin(); itr != m_petInfo.end(); ++itr) - if (itr->second.entry == petentry) - return &itr->second; - - return nullptr; -} - -void Player::AddPetInfo(Pet* pet) -{ - m_petInfo[pet->GetCharmInfo()->GetPetNumber()].UpdateData(pet); - setPetSlot(pet->GetSlot(), pet->GetCharmInfo()->GetPetNumber()); -} - bool Player::_LoadHomeBind(PreparedQueryResult result) { PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); @@ -25013,6 +24976,7 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, uint32(time(nullptr))); stmt->setUInt8(index++, (HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0)); stmt->setUInt16(index++, (uint16)m_ExtraFlags); + stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); stmt->setUInt16(index++, m_zoneId ? m_zoneId : m_oldZoneId); stmt->setUInt32(index++, uint32(m_deathExpireTime)); @@ -25064,13 +25028,6 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setString(index++, ss.str()); stmt->setUInt8(index++, GetActionBarTogglesValue()); - stmt->setInt32(index++, m_currentPetNumber); - - ss.str(""); - for (uint32 i = 0; i < PET_SLOT_LAST; ++i) - ss << m_PetSlots[i] << ' '; - - stmt->setString(index++, ss.str()); stmt->setUInt32(index++, m_grantableLevels); stmt->setUInt32(index++, m_createdtime); @@ -25146,6 +25103,7 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt8(index++, (HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0)); stmt->setUInt16(index++, (uint16)m_ExtraFlags); + stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); stmt->setUInt16(index++, m_zoneId ? m_zoneId : m_oldZoneId); stmt->setUInt32(index++, uint32(m_deathExpireTime)); @@ -25199,13 +25157,6 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setString(index++, ss.str()); stmt->setUInt8(index++, GetActionBarTogglesValue()); - stmt->setInt32(index++, m_currentPetNumber); - - ss.str(""); - for (uint32 i = 0; i < PET_SLOT_LAST; ++i) - ss << m_PetSlots[i] << ' '; - - stmt->setString(index++, ss.str()); stmt->setUInt32(index++, m_grantableLevels); @@ -25291,7 +25242,7 @@ void Player::SaveToDB(bool create /*=false*/) // save pet (hunter pet level and experience and all type pets health/mana). if (Pet* pet = GetPet()) - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); } bool Player::HandleChangeSlotModel(uint32 newItem, uint16 pos) @@ -26574,30 +26525,14 @@ Pet* Player::GetPet() const Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration, uint32 spellId) { - if (getClass() == CLASS_HUNTER) - petType = HUNTER_PET; - Pet* pet = new Pet(this, petType); - pet->Relocate(x, y, z, ang); - - //summoned pets always non-current! - if (petType == SUMMON_PET || petType == HUNTER_PET) + if (petType == SUMMON_PET && pet->LoadPetFromDB(this, entry)) { - // This check done in LoadPetFromDB, but we should not continue this function if pet not alowed - if (!CanSummonPet(entry)) - { - delete pet; - return NULL; - } - - if (pet->LoadPetFromDB(this, entry, 0)) - { - if (duration > 0) - pet->SetDuration(duration); + if (duration > 0) + pet->SetDuration(duration); - return pet; - } + return nullptr; } // petentry == 0 for hunter "call pet" (current pet summoned if any) @@ -26605,14 +26540,15 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy { TC_LOG_ERROR("misc", "no such entry %u", entry); delete pet; - return NULL; + return nullptr; } + pet->Relocate(x, y, z, ang); if (!pet->IsPositionValid()) { TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)", pet->GetGUIDLow(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY()); delete pet; - return NULL; + return nullptr; } Map* map = GetMap(); @@ -26621,14 +26557,16 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy { TC_LOG_ERROR("misc", "no such creature entry %u", entry); delete pet; - return NULL; + return nullptr; } pet->SetTratsport(GetTransport()); pet->SetCreatorGUID(GetGUID()); pet->SetUInt32Value(UNIT_FIELD_FACTION_TEMPLATE, getFaction()); - pet->SetUInt32Value(UNIT_FIELD_NPC_FLAGS, 0); + + pet->SetUInt32Value(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_NONE); pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pet->SetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL, spellId); pet->InitStatsForLevel(GetEffectiveLevel(), petType != SUMMON_PET); @@ -26643,51 +26581,108 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy map->AddToMap(pet->ToCreature()); - if (petType == SUMMON_PET) - { - pet->SynchronizeLevelWithOwner(); - pet->InitPetCreateSpells(); - pet->SavePetToDB(); - PetSpellInitialize(); - SendTalentsInfoData(true); + switch (petType) + { + case SUMMON_PET: + // this enables pet details window (Shift+P) + pet->GetCharmInfo()->SetPetNumber(pet_number, true); + pet->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_MAGE); + pet->SetUInt32Value(UNIT_FIELD_PET_EXPERIENCE, 0); + pet->SetUInt32Value(UNIT_FIELD_PET_NEXT_LEVEL_EXPERIENCE, 1000); + pet->SetFullHealth(); + pet->SetPower(getPowerType(), GetMaxPower(getPowerType())); + pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped in this case + break; + default: + break; } - if (getClass() == CLASS_WARLOCK) - if (HasAura(196099)) - RemoveAura(196099); + switch (petType) + { + case SUMMON_PET: + pet->InitPetCreateSpells(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + PetSpellInitialize(); + break; + default: + break; + } - if (pet->isControlled() && !pet->isTemporarySummoned()) - SetLastPetEntry(entry); +// if (getClass() == CLASS_WARLOCK) +// if (HasAura(196099)) +// RemoveAura(196099); if (duration > 0) pet->SetDuration(duration); //TC_LOG_DEBUG("spells", "SummonPet entry %i, petType %i, spellId %i", entry, petType, spellId); - pet->CastPetAuras(true); + //pet->CastPetAuras(true); return pet; } -void Player::RemovePet(Pet* pet, bool isDelete) +void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) { if (!pet) pet = GetPet(); if (pet) { - TC_LOG_DEBUG("misc", "RemovePet %u, isDelete %u", pet->GetEntry(), isDelete); + TC_LOG_DEBUG("entities.pet", "Player::RemovePet: Player '%s' (%s), Pet (Entry: %u, Mode: %u, ReturnReagent: %u)", + GetName(), GetGUID().ToString().c_str(), pet->GetEntry(), mode, returnreagent); if (pet->m_removed) return; } + if (returnreagent && (pet || m_temporaryUnsummonedPetNumber) && !InBattleground()) + { + //returning of reagents only for players, so best done here + uint32 spellId = pet ? pet->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL) : m_oldpetspell; + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + + if (spellInfo) + { + for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i) + { + if (spellInfo->Reagents.Reagent[i] > 0) + { + ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout) + InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, spellInfo->Reagents.Reagent[i], spellInfo->Reagents.ReagentCount[i]); + if (msg == EQUIP_ERR_OK) + { + Item* item = StoreNewItem(dest, spellInfo->Reagents.Reagent[i], true); + if (IsInWorld()) + SendNewItem(item, spellInfo->Reagents.ReagentCount[i], true, false); + } + } + } + } + m_temporaryUnsummonedPetNumber = 0; + } + if (!pet || pet->GetOwnerGUID() != GetGUID()) return; pet->CastPetAuras(false); pet->CombatStop(); + + if (returnreagent) + { + switch (pet->GetEntry()) + { + //warlock pets except imp are removed(?) when logging out + case 1860: + case 1863: + case 417: + case 17252: + mode = PET_SAVE_NOT_IN_SLOT; + break; + } + } + // only if current pet in slot - pet->SavePetToDB(isDelete); + pet->SavePetToDB(mode); SetMinion(pet, false); @@ -26697,7 +26692,9 @@ void Player::RemovePet(Pet* pet, bool isDelete) if (pet->isControlled()) { SendRemoveControlBar(); - SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET); + + if (GetGroup()) + SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET); } WorldPackets::PetPackets::PetDismissSound packet; @@ -29547,8 +29544,8 @@ void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/) template<> inline void BeforeVisibilityDestroy(Creature* t, Player* p) { - if (p->GetPetGUID() == t->GetGUID() && t->ToCreature()->isPet()) - ((Pet*)t)->Remove(); + if (p->GetPetGUID() == t->GetGUID() && t->isPet()) + t->ToPet()->Remove(PET_SAVE_NOT_IN_SLOT, true); } void Player::UpdateVisibilityOf(WorldObject* target) @@ -33453,7 +33450,7 @@ void Player::UnsummonPetTemporaryIfAny() m_oldpetspell = pet->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL); } - RemovePet(pet); + RemovePet(pet, PET_SAVE_AS_CURRENT); } void Player::ResummonPetTemporaryUnSummonedIfAny() @@ -33908,7 +33905,7 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) // TO-DO: We need more research to know what happens with warlock's reagent if (Pet* pet = GetPet()) - RemovePet(pet); + RemovePet(pet, PET_SAVE_NOT_IN_SLOT); ClearAllReactives(); UnsummonAllTotems(); @@ -33968,8 +33965,7 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) } case 2: //remove pet { - RemovePet(NULL); - m_currentPetNumber = 0; + RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT); m_temporaryUnsummonedPetNumber = 0; break; } @@ -35930,126 +35926,6 @@ std::vector Player::GetPvPRewardItem(uint32& itemID, uint8 type, uint32 return sObjectMgr->GetItemBonusTree(itemID, GetMap()->GetDifficultyLootItemContext(), getLevel(), 0, 0, needLevel); } -void Player::LoadPetSlot(std::string const &data) -{ - Tokenizer tokens(data, ' '); - - uint8 index = 0; - for (Tokenizer::const_iterator iter = tokens.begin(); index < PET_SLOT_LAST && iter != tokens.end(); ++iter, ++index) - m_PetSlots[index] = uint32(atol(*iter)); - - //Clean bug slots - for (uint32 i = PET_SLOT_HUNTER_FIRST; i < PET_SLOT_STABLE_LAST; ++i) - { - for (uint32 j = PET_SLOT_HUNTER_FIRST; j < PET_SLOT_STABLE_LAST; ++j) - { - if (m_PetSlots[i] && i != j && m_PetSlots[i] == m_PetSlots[j]) //Clear duplicate pet - m_PetSlots[j]= 0; - } - } -} - -void Player::cleanPetSlotForMove(PetSlot slot, uint32 petID) -{ - ASSERT(slot < PET_SLOT_LAST); - - // Clean only if we have corrcet Id, for lost link privention - if (m_PetSlots[slot] == petID) - m_PetSlots[slot] = 0; -} - -void Player::setPetSlot(PetSlot slot, uint32 petID) -{ - ASSERT(slot < PET_SLOT_LAST); - - if (m_PetSlots[slot] && m_PetSlots[slot] != petID) - { - bool find = false; - for (uint32 i = PET_SLOT_STABLE_FIRST; i < PET_SLOT_STABLE_LAST; ++i) - { - if (m_PetSlots[i] == petID) - { - find = true; - SwapPetSlot(slot, (PetSlot)i); - break; - } - } - if(!find) - { - for (uint32 i = PET_SLOT_STABLE_FIRST; i < PET_SLOT_STABLE_LAST; ++i) - { - if (!m_PetSlots[i]) - { - m_PetSlots[i] = m_PetSlots[slot]; - break; - } - } - } - } - - m_PetSlots[slot] = petID; -} - -void Player::SwapPetSlot(PetSlot oldSlot, PetSlot newSlot) -{ - std::swap(m_PetSlots[newSlot], m_PetSlots[oldSlot]); -} - -int16 Player::SetOnAnyFreeSlot(uint32 petID) -{ - for (uint32 i = PET_SLOT_HUNTER_FIRST; i < PET_SLOT_STABLE_LAST; ++i) - if (!m_PetSlots[i]) - { - m_PetSlots[i] = petID; - return i; - } - return PET_SLOT_FULL_LIST; -} - - -PetSlot Player::getSlotForNewPet(bool full/*=false*/) -{ - uint32 last_known = PET_SLOT_OTHER_PET; //check PET_SLOT_STABLE_LAST for last - // Call Pet Spells - // 883 83242 83243 83244 83245 - // 1 2 3 4 5 - if (!full) - { - if (HasSpell(83245)) - last_known = 5; - else if (HasSpell(83244)) - last_known = 4; - else if (HasSpell(83243)) - last_known = 3; - else if (HasSpell(83242)) - last_known = 2; - else if (HasSpell(883)) - last_known = 1; - else - last_known = 0; - } - - for (uint32 i = uint32(PET_SLOT_HUNTER_FIRST); i < last_known; ++i) - if (!m_PetSlots[i]) - return PetSlot(i); - - return PET_SLOT_FULL_LIST; -} - -PetSlot Player::GetSlotForPetId(uint32 petID) -{ - for (uint32 i = uint32(PET_SLOT_HUNTER_FIRST); i < uint32(PET_SLOT_STABLE_LAST); ++i) - if (m_PetSlots[i] == petID) - return PetSlot(i); - - return PET_SLOT_FULL_LIST; -} - -PetSlot Player::GetMaxCurentPetSlot() const -{ - return PET_SLOT_HUNTER_LAST; -} - uint8 Player::GetClassFamily() const { switch (getClass()) @@ -38571,7 +38447,6 @@ void Player::Clear() m_itemDuration.clear(); m_itemSoulboundTradeable.clear(); m_channels.clear(); - m_PetSlots.clear(); _battlePets.clear(); AllArtifacts.clear(); _oldPetBattleSpellToMerge.clear(); @@ -38661,7 +38536,6 @@ void Player::PrintPlayerSize() size += m_itemDuration.size() * sizeof(ItemDurationList); size += m_itemSoulboundTradeable.size() * sizeof(ItemDurationList); size += m_channels.size() * sizeof(JoinedChannelsList); - size += m_PetSlots.size() * sizeof(PlayerPetSlotList); size += _battlePets.size() * sizeof(BattlePetMap); size += AllArtifacts.size() * sizeof(std::set>); size += _oldPetBattleSpellToMerge.size() * sizeof(std::set>); @@ -38782,93 +38656,6 @@ void Player::ApplyDotMod(uint32 spellId, SpellModOp op, float& basemod, float& b } } -void Player::CreateDefaultPet() -{ - if (getClass() == CLASS_WARLOCK) - CastSpell(this, 688, true); - - if (getClass() == CLASS_HUNTER) - { - uint32 spellID = 13481; - uint32 creatureEntry = 0; - switch (getRace()) - { - case RACE_HUMAN: - creatureEntry = 299; - break; - case RACE_DWARF: - creatureEntry = 42713; - break; - case RACE_ORC: - creatureEntry = 42719; - break; - case RACE_NIGHTELF: - creatureEntry = 42718; - break; - case RACE_UNDEAD_PLAYER: - creatureEntry = 51107; - break; - case RACE_TAUREN: - creatureEntry = 42720; - break; - case RACE_TROLL: - creatureEntry = 42721; - break; - case RACE_GOBLIN: - creatureEntry = 42715; - break; - case RACE_BLOODELF: - creatureEntry = 42710; - break; - case RACE_DRAENEI: - creatureEntry = 42712; - break; - case RACE_WORGEN: - creatureEntry = 42722; - break; - case RACE_PANDAREN_NEUTRAL: - creatureEntry = 57239; - break; - case RACE_GNOME: - creatureEntry = 76199; - break; - case RACE_NIGHTBORNE: - creatureEntry = 131069; - break; - case RACE_HIGHMOUNTAIN_TAUREN: - creatureEntry = 131076; - break; - case RACE_VOID_ELF: - creatureEntry = 131072; - break; - case RACE_LIGHTFORGED_DRAENEI: - creatureEntry = 131074; - break; - default: - break; - } - - if (creatureEntry) - { - PetSlot slot = getSlotForNewPet(); - Pet* pet = CreateTamedPetFrom(creatureEntry, spellID); - if (!pet) - return; - - // add to world - pet->GetMap()->AddToMap(pet->ToCreature()); - pet->SetSlot(slot); - - SetMinion(pet, true); - - pet->SavePetToDB(); - ToPlayer()->PetSpellInitialize(); - - ToPlayer()->GetSession()->SendStablePet(); - } - } -} - void Player::_LoadChallengeKey(PreparedQueryResult result) { if (!result) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 97499e87..f8a6dfa7 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -998,7 +998,6 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFITS, PLAYER_LOGIN_QUERY_LOAD_PVP_TALENTS, PLAYER_LOGIN_QUERY_ADVENTURE_QUEST, - PLAYER_LOGIN_QUERY_PETS, PLAYER_LOGIN_QUERY_LOAD_VOIDSTORAGE_ITEM, PLAYER_LOGIN_QUERY_LOADWORLDQUESTSTATUS, PLAYER_LOGIN_QUERY_BATTLE_PETS, @@ -1261,7 +1260,6 @@ struct SpecializationInfo }; typedef std::unordered_map BracketList; -typedef std::vector PlayerPetSlotList; #pragma pack(push, 1) struct PlayerDynamicFieldSpellModByLabel @@ -1610,7 +1608,7 @@ class Player : public Unit, public GridObject Pet* GetPet() const; Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime, uint32 spellId = 0); - void RemovePet(Pet* pet, bool isDelete = false); + void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false); PhaseMgr& GetPhaseMgr() { return phaseMgr; } @@ -1894,6 +1892,8 @@ class Player : public Unit, public GridObject bool AddItem(uint32 itemId, uint32 count, uint32* noSpaceForCount = nullptr, ObjectGuid guid = ObjectGuid::Empty ); + uint32 m_stableSlots; + /*********************************************************/ /*** GOSSIP SYSTEM ***/ /*********************************************************/ @@ -2896,20 +2896,6 @@ class Player : public Unit, public GridObject WorldLocation GetStartPosition() const; - // current pet slot - uint32 m_currentPetNumber; - PetSlot m_currentSummonedSlot; - - void setPetSlot(PetSlot slot, uint32 petID); - int16 SetOnAnyFreeSlot(uint32 petID); - void cleanPetSlotForMove(PetSlot slot, uint32 petID); - void SwapPetSlot(PetSlot oldSlot, PetSlot newSlot); - uint32 getPetIdBySlot(uint32 slot) const { return m_PetSlots[slot]; } - const PlayerPetSlotList &GetPetSlotList() { return m_PetSlots; } - PetSlot getSlotForNewPet(bool full = false); - PetSlot GetSlotForPetId(uint32 petID); - PetSlot GetMaxCurentPetSlot() const; - uint8 GetClassFamily() const; bool CanSummonPet(uint32 entry) const; @@ -3008,18 +2994,10 @@ class Player : public Unit, public GridObject bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false); bool CheckInstanceLoginValid(); - PetInfoData* GetPetInfo(uint32 petentry, uint32 petnumber); - void AddPetInfo(Pet* pet); - PetInfoDataMap* GetPetInfoData() { return &m_petInfo; } - void DeletePetInfo(uint32 petnumber) { m_petInfo.erase(petnumber); } - // last used pet number (for BG's) uint32 GetLastPetNumber() const { return m_lastpetnumber; } void SetLastPetNumber(uint32 petnumber) { m_lastpetnumber = petnumber; } - uint32 GetLastPetEntry() const { return m_LastPetEntry; } - void SetLastPetEntry(uint32 entry) { m_LastPetEntry = entry; } - uint32 GetEncounterMask(lfg::LFGDungeonData const* dungeonData, lfg::LfgReward const* reward); /*********************************************************/ @@ -3255,8 +3233,6 @@ class Player : public Unit, public GridObject void DeliveryRewardPack(uint32 rewardPackID); - void CreateDefaultPet(); - RestMgr& GetRestMgr() const { return *_restMgr; } protected: @@ -3519,11 +3495,6 @@ class Player : public Unit, public GridObject bool m_canTitanGrip; uint8 m_swingErrorMsg; - ////////////////////Pet System///////////////////// - void LoadPetSlot(std::string const &data); - PlayerPetSlotList m_PetSlots; - ////////////////////Rest System///////////////////// - public: void SendCustomMessage(std::string const& n); void SendCustomMessage(std::string const& n, std::ostringstream const& data); @@ -3623,7 +3594,6 @@ class Player : public Unit, public GridObject // last used pet number (for BG's) uint32 m_lastpetnumber; - uint32 m_LastPetEntry; // Player summoning time_t m_summon_expire; @@ -3751,8 +3721,6 @@ class Player : public Unit, public GridObject uint16 m_scenarioId = 0; uint16 m_adventure_questID = 0; - PetInfoDataMap m_petInfo; - WargameRequest* _wargameRequest; std::unordered_map> GlobalArtifactData; DeathMatchScore dmScore; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 84894f5b..ed6540ff 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11838,7 +11838,7 @@ void Unit::SetMinion(Minion *minion, bool apply) { // remove existing minion pet if (oldPet->isPet()) - oldPet->ToPet()->Remove(); + oldPet->ToPet()->Remove(PET_SAVE_AS_CURRENT); else oldPet->UnSummon(); SetPetGUID(minion->GetGUID()); @@ -19189,11 +19189,6 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spellID) if (!player) return nullptr; - player->m_currentSummonedSlot = player->getSlotForNewPet(); - - if (player->m_currentSummonedSlot == PET_SLOT_FULL_LIST) - return nullptr; - Pet* pet = new Pet(player, HUNTER_PET); if (!pet->CreateBaseAtCreature(creatureTarget)) @@ -19202,9 +19197,9 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spellID) return nullptr; } - InitTamedPet(pet, spellID); - pet->SetSlot(player->m_currentSummonedSlot); - player->AddPetInfo(pet); + uint8 level = creatureTarget->getLevelForTarget(this) + 5 < getLevel() ? (getLevel() - 5) : creatureTarget->getLevelForTarget(this); + + InitTamedPet(pet, level, spellID); return pet; } @@ -19215,34 +19210,26 @@ Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spellID) if (!player) return nullptr; - player->m_currentSummonedSlot = player->getSlotForNewPet(); - - if (player->m_currentSummonedSlot == PET_SLOT_FULL_LIST) - return nullptr; - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry); if (!creatureInfo) return nullptr; Pet* pet = new Pet(player, HUNTER_PET); - if (!pet->CreateBaseAtCreatureInfo(creatureInfo, this) || !InitTamedPet(pet, spellID)) + if (!pet->CreateBaseAtCreatureInfo(creatureInfo, this) || !InitTamedPet(pet, getLevel(), spellID)) { delete pet; return nullptr; } - pet->SetSlot(player->m_currentSummonedSlot); - player->AddPetInfo(pet); - return pet; } -bool Unit::InitTamedPet(Pet* pet, uint32 spellID) +bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) { pet->SetCreatorGUID(GetGUID()); pet->setFaction(getFaction()); - pet->SetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL, spellID); + pet->SetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL, spell_id); if (IsPlayer()) { @@ -19250,7 +19237,7 @@ bool Unit::InitTamedPet(Pet* pet, uint32 spellID) pet->AddUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER); } - if (!pet->InitStatsForLevel(GetEffectiveLevel(), false)) + if (!pet->InitStatsForLevel(level, false)) { TC_LOG_ERROR("entities.unit", "Pet::InitStatsForLevel() failed for creature (Entry: %u)!", pet->GetEntry()); return false; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index f97f12db..0627fbf5 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1661,7 +1661,7 @@ class Unit : public WorldObject Pet* CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id = 0); Pet* CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id = 0); - bool InitTamedPet(Pet* pet, uint32 spell_id); + bool InitTamedPet(Pet* pet, uint8 level, uint32 spell_id); // aura apply/remove helpers - you should better not use these Aura* _TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint32 effMask, Unit* caster, float* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index dd295d39..efbf3192 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -147,7 +147,8 @@ void WorldSession::SendCharacterEnum(bool deleted /*= false*/) else stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ENUM); - stmt->setUInt32(0, GetAccountId()); + stmt->setUInt8(0, PET_SAVE_AS_CURRENT); + stmt->setUInt32(1, GetAccountId()); _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleCharEnum, this, std::placeholders::_1, deleted))); } @@ -837,7 +838,6 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) SendPacket(artifactKnowledgeFishingPole.Write()); //Show cinematic at the first time that player login - bool firstLogin = !player->getCinematic(); // it's needed below if (!player->getCinematic()) { player->setCinematic(1); @@ -967,10 +967,15 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) SendNotification(LANG_RESET_TALENTS); } - if (player->HasAtLoginFlag(AT_LOGIN_FIRST)) + bool firstLogin = pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST); + if (firstLogin) { - player->RemoveAtLoginFlag(AT_LOGIN_FIRST); - player->CreateDefaultPet(); + pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST); + + PlayerInfo const* info = sObjectMgr->GetPlayerInfo(pCurrChar->getRace(), pCurrChar->getClass()); + // TODO: default pets should come from cast spells +// for (uint32 spellId : info->castSpells) +// pCurrChar->CastSpell(pCurrChar, spellId, true); } // show time before shutdown if shutdown planned. diff --git a/src/server/game/Handlers/LoginQueryHolder.cpp b/src/server/game/Handlers/LoginQueryHolder.cpp index 16a72462..0d5c9080 100644 --- a/src/server/game/Handlers/LoginQueryHolder.cpp +++ b/src/server/game/Handlers/LoginQueryHolder.cpp @@ -295,10 +295,6 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_ADVENTURE_QUEST, stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET); - stmt->setUInt64(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_PETS, stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_WORLDQUESTSTATUS); stmt->setUInt32(0, m_accountId); stmt->setUInt64(1, lowGuid); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 83498375..30a8b59b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -54,7 +54,7 @@ void WorldSession::HandleRepopRequest(WorldPackets::Misc::RepopRequest& /*packet } //this is spirit release confirm? - GetPlayer()->RemovePet(nullptr); + GetPlayer()->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true); GetPlayer()->BuildPlayerRepop(); GetPlayer()->RepopAtGraveyard(); } diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index f40ed2fd..e222f606 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -306,7 +306,7 @@ void WorldSession::HandlePetAbandon(WorldPackets::PetPackets::PetAbandon& packet { if (pet->isPet()) { - _player->RemovePet(pet->ToPet(), true); + _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); _player->GetSession()->SendStablePet(); } else if (pet->GetGUID() == _player->GetCharmGUID()) @@ -469,83 +469,83 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& } } -void WorldSession::HanleSetPetSlot(WorldPackets::PetPackets::SetPetSlot& packet) +void WorldSession::HandleSetPetSlot(WorldPackets::PetPackets::SetPetSlot& packet) { - if (!GetPlayer()->GetNPCIfCanInteractWith(packet.NpcGUID, UNIT_NPC_FLAG_STABLEMASTER)) - { - TC_LOG_DEBUG("network", "Stablemaster (GUID:%u) not found or you can't interact with him.", packet.NpcGUID.GetGUIDLow()); - SendStableResult(STABLE_ERR_STABLE); - return; - } - - if (packet.NewSlot > MAX_PET_STABLES) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - Pet* pet = _player->GetPet(); - if (pet && pet->GetCharmInfo() && pet->GetCharmInfo()->GetPetNumber() == packet.PetIndex) - _player->RemovePet(pet); - - PetSlot curentSlot = GetPlayer()->GetSlotForPetId(GetPlayer()->m_currentPetNumber); - if (pet && curentSlot == packet.NewSlot) - _player->RemovePet(pet); - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_BY_ID); - stmt->setUInt64(0, _player->GetGUIDLow()); - stmt->setUInt32(1, packet.PetIndex); - - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableChangeSlotCallback, this, std::placeholders::_1, packet.PetIndex))); +// if (!GetPlayer()->GetNPCIfCanInteractWith(packet.NpcGUID, UNIT_NPC_FLAG_STABLEMASTER)) +// { +// TC_LOG_DEBUG("network", "Stablemaster (GUID:%u) not found or you can't interact with him.", packet.NpcGUID.GetGUIDLow()); +// SendStableResult(STABLE_ERR_STABLE); +// return; +// } +// +// if (packet.NewSlot > MAX_PET_STABLES) +// { +// SendStableResult(STABLE_ERR_STABLE); +// return; +// } +// +// if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) +// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); +// +// Pet* pet = _player->GetPet(); +// if (pet && pet->GetCharmInfo() && pet->GetCharmInfo()->GetPetNumber() == packet.PetIndex) +// _player->RemovePet(pet); +// +// PetSlot currentSlot = GetPlayer()->GetSlotForPetId(GetPlayer()->m_currentPetNumber); +// if (pet && currentSlot == packet.NewSlot) +// _player->RemovePet(pet); +// +// CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_BY_ID); +// stmt->setUInt64(0, _player->GetGUIDLow()); +// stmt->setUInt32(1, packet.PetIndex); +// +// _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableChangeSlotCallback, this, std::placeholders::_1, packet.PetIndex))); } void WorldSession::HandleStableChangeSlotCallback(PreparedQueryResult const& result, uint8 new_slot) { - if (!GetPlayer()) - return; - - if (!result) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - Field *fields = result->Fetch(); - - uint32 pet_entry = fields[0].GetUInt32(); - uint32 pet_number = fields[1].GetUInt32(); - //bool isHunter = fields[2].GetUInt8() == HUNTER_PET; - - PetSlot slot = GetPlayer()->GetSlotForPetId(pet_number); - - if (!pet_entry) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(pet_entry); - if (!creatureInfo || !creatureInfo->isTameable(_player)) - { - // if problem in exotic pet - if (creatureInfo && creatureInfo->isTameable(_player)) - SendStableResult(STABLE_ERR_EXOTIC); - else - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // Update if its a Hunter pet - if (new_slot != 100) - { - // We need to remove and add the new pet to there diffrent slots - GetPlayer()->SwapPetSlot(slot, static_cast(new_slot)); - } - - SendStableResult(STABLE_SUCCESS_STABLE); +// if (!GetPlayer()) +// return; +// +// if (!result) +// { +// SendStableResult(STABLE_ERR_STABLE); +// return; +// } +// +// Field *fields = result->Fetch(); +// +// uint32 pet_entry = fields[0].GetUInt32(); +// uint32 pet_number = fields[1].GetUInt32(); +// //bool isHunter = fields[2].GetUInt8() == HUNTER_PET; +// +// PetSlot slot = GetPlayer()->GetSlotForPetId(pet_number); +// +// if (!pet_entry) +// { +// SendStableResult(STABLE_ERR_STABLE); +// return; +// } +// +// CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(pet_entry); +// if (!creatureInfo || !creatureInfo->isTameable(_player)) +// { +// // if problem in exotic pet +// if (creatureInfo && creatureInfo->isTameable(_player)) +// SendStableResult(STABLE_ERR_EXOTIC); +// else +// SendStableResult(STABLE_ERR_STABLE); +// return; +// } +// +// // Update if its a Hunter pet +// if (new_slot != 100) +// { +// // We need to remove and add the new pet to there diffrent slots +// GetPlayer()->SwapPetSlot(slot, static_cast(new_slot)); +// } +// +// SendStableResult(STABLE_SUCCESS_STABLE); } void WorldSession::SendStableResult(StableResultCode res) @@ -664,11 +664,19 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid petGuid, uint32 s _player->StopCastingCharm(); else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID()) { - ASSERT(pet->IsCreature()); + ASSERT(pet->GetTypeId() == TYPEID_UNIT); if (pet->isPet()) - GetPlayer()->RemovePet(pet->ToPet()); + { + if (((Pet*)pet)->getPetType() == HUNTER_PET) + GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); + else + //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) + pet->setDeathState(CORPSE); + } else if (pet->HasUnitTypeMask(UNIT_MASK_MINION)) - dynamic_cast(pet)->UnSummon(); + { + ((Minion*)pet)->UnSummon(); + } } break; case COMMAND_MOVE_TO: @@ -777,56 +785,56 @@ void WorldSession::SendPetNameInvalid(uint32 error, ObjectGuid const& guid, std: void WorldSession::SendStablePet(ObjectGuid const& /*guid*/ /*= ObjectGuid::Empty*/) { - Player* player = GetPlayer(); - if (!player) - return; - - WorldPackets::PetPackets::StableList list; - - std::set stableNumber; - PetInfoDataMap* petMap = player->GetPetInfoData(); - if (!petMap->empty()) - { - for (auto& petData : *petMap) - { - PetSlot petSlot = player->GetSlotForPetId(petData.second.id); - stableNumber.insert(petData.second.id); - - if (petSlot > PET_SLOT_STABLE_LAST) - continue; - - if (petSlot == PET_SLOT_FULL_LIST) - petSlot = static_cast(player->SetOnAnyFreeSlot(petData.second.id)); - - if (petSlot >= PET_SLOT_HUNTER_FIRST && petSlot < PET_SLOT_STABLE_LAST) - { - WorldPackets::PetPackets::StableInfo info; - info.PetSlot = petSlot; - info.PetNumber = petData.second.id; - info.CreatureID = petData.second.entry; - info.DisplayID = petData.second.modelid; - info.ExperienceLevel = petData.second.level; - info.PetFlags = petSlot < PET_SLOT_STABLE_FIRST ? 1 : 3; - info.PetName = petData.second.name; - list.Stables.push_back(info); - } - } - } - - if (player->getClass() == CLASS_HUNTER) - { - SendPacket(list.Write()); - SendStableResult(STABLE_ERR_NONE); - } - - PlayerPetSlotList const& petSlots = player->GetPetSlotList(); - for (uint32 i = uint32(PET_SLOT_HUNTER_FIRST); i < uint32(PET_SLOT_STABLE_LAST); ++i) - { - if (!petSlots[i]) - continue; - - auto find = stableNumber.find(petSlots[i]); - if (find == stableNumber.end()) - player->cleanPetSlotForMove(PetSlot(i), petSlots[i]); - } +// Player* player = GetPlayer(); +// if (!player) +// return; +// +// WorldPackets::PetPackets::StableList list; +// +// std::set stableNumber; +// PetInfoDataMap* petMap = player->GetPetInfoData(); +// if (!petMap->empty()) +// { +// for (auto& petData : *petMap) +// { +// PetSlot petSlot = player->GetSlotForPetId(petData.second.id); +// stableNumber.insert(petData.second.id); +// +// if (petSlot > PET_SLOT_STABLE_LAST) +// continue; +// +// if (petSlot == PET_SLOT_FULL_LIST) +// petSlot = static_cast(player->SetOnAnyFreeSlot(petData.second.id)); +// +// if (petSlot >= PET_SLOT_HUNTER_FIRST && petSlot < PET_SLOT_STABLE_LAST) +// { +// WorldPackets::PetPackets::StableInfo info; +// info.PetSlot = petSlot; +// info.PetNumber = petData.second.id; +// info.CreatureID = petData.second.entry; +// info.DisplayID = petData.second.modelid; +// info.ExperienceLevel = petData.second.level; +// info.PetFlags = petSlot < PET_SLOT_STABLE_FIRST ? 1 : 3; +// info.PetName = petData.second.name; +// list.Stables.push_back(info); +// } +// } +// } +// +// if (player->getClass() == CLASS_HUNTER) +// { +// SendPacket(list.Write()); +// SendStableResult(STABLE_ERR_NONE); +// } +// +// PlayerPetSlotList const& petSlots = player->GetPetSlotList(); +// for (uint32 i = uint32(PET_SLOT_HUNTER_FIRST); i < uint32(PET_SLOT_STABLE_LAST); ++i) +// { +// if (!petSlots[i]) +// continue; +// +// auto find = stableNumber.find(petSlots[i]); +// if (find == stableNumber.end()) +// player->cleanPetSlotForMove(PetSlot(i), petSlots[i]); +// } } \ No newline at end of file diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 15703e2d..6b56f2b4 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1699,7 +1699,7 @@ void Map::MoveAllCreaturesInMoveList() //TODO: pets will disappear if this is outside CreatureRespawnRelocation //need to check why pet is frequently relocated to an unloaded cell if (c->isPet()) - c->ToPet()->Remove(); + c->ToPet()->Remove(PET_SAVE_NOT_IN_SLOT, true); else AddObjectToRemoveList(c); } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 4443d547..d5efdf2c 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -831,7 +831,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_SET_LOOT_SPECIALIZATION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetLootSpecialization); DEFINE_HANDLER(CMSG_SET_PARTY_ASSIGNMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPartyAssignment); DEFINE_HANDLER(CMSG_SET_PARTY_LEADER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPartyLeader); - DEFINE_HANDLER(CMSG_SET_PET_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HanleSetPetSlot); + DEFINE_HANDLER(CMSG_SET_PET_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPetSlot); DEFINE_HANDLER(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPlayerDeclinedNames); DEFINE_HANDLER(CMSG_SET_PREFERRED_CEMETERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSetCemetryPreferrence); DEFINE_HANDLER(CMSG_SET_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPvP); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 059c18c2..27262e4c 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -663,10 +663,7 @@ void WorldSession::LogoutPlayer(bool Save) _player->UnsummonCurrentBattlePetIfAny(true); ///- Remove pet - if (_player->getClass() != CLASS_WARLOCK) - _player->RemovePet(nullptr); - else if (Pet* _pet = _player->GetPet()) - _pet->SavePetToDB(); + _player->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true); ///- empty buyback items and save the player in the database // some save parts only correctly work in case player present in map/player_lists (pets, etc) diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index c9756011..c3c15aef 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1506,7 +1506,7 @@ class WorldSession void HandleQueryNPCText(WorldPackets::Query::QueryNPCText& packet); void HandleBinderActivate(WorldPackets::NPC::Hello& packet); void HandleRequestStabledPets(WorldPackets::NPC::RequestStabledPets& packet); - void HanleSetPetSlot(WorldPackets::PetPackets::SetPetSlot& packet); + void HandleSetPetSlot(WorldPackets::PetPackets::SetPetSlot& packet); void HandleStableChangeSlotCallback(PreparedQueryResult const& result, uint8 new_slot); void SendTrainerService(ObjectGuid guid, uint32 spellId, uint32 trainState); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 034d3717..02fdc97b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -384,7 +384,7 @@ void Spell::EffectInstaKill(SpellEffIndex /*effIndex*/) { if (!unitTarget->GetHealth() || !unitTarget->isAlive()) { - unitTarget->ToPet()->Remove(); + unitTarget->ToPet()->Remove(PET_SAVE_NOT_IN_SLOT, false); return; } @@ -4632,7 +4632,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) // caster have pet now m_caster->SetMinion(pet, true); - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); player->PetSpellInitialize(); player->GetSession()->SendStablePet(); } @@ -4682,13 +4682,12 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) } if (owner->IsPlayer()) - owner->ToPlayer()->RemovePet(OldSummon); + owner->ToPlayer()->RemovePet(OldSummon, (OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT), false); else return; } auto slot = PetSlot(RoundingFloatValue(m_spellInfo->GetEffect(effIndex, m_diffMode)->BasePoints)); - owner->m_currentSummonedSlot = slot; Position pos; if (canHitTargetInLOS && owner->ToCreature()) @@ -4742,7 +4741,7 @@ void Spell::EffectLearnPetSpell(SpellEffIndex effIndex) return; pet->learnSpell(learn_spellproto->Id); - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); if (Player* player = pet->GetOwner()->ToPlayer()) player->PetSpellInitialize(); } @@ -6403,10 +6402,7 @@ void Spell::EffectDismissPet(SpellEffIndex effIndex) ExecuteLogEffectUnsummonObject(effIndex, pet); if (Player* player = pet->GetOwner()->ToPlayer()) - { - player->RemovePet(pet); - player->m_currentPetNumber = 0; - } + player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); } void Spell::EffectSummonObject(SpellEffIndex effIndex) @@ -7157,7 +7153,7 @@ void Spell::EffectSummonDeadPet(SpellEffIndex /*effIndex*/) //pet->AIM_Initialize(); //player->PetSpellInitialize(); - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); } void Spell::EffectDurabilityDamage(SpellEffIndex effIndex) @@ -7648,7 +7644,7 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex) unitTarget->SetMinion(pet, true); - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); player->PetSpellInitialize(); player->GetSession()->SendStablePet(); } @@ -7939,7 +7935,6 @@ void Spell::EffectActivateSpec(SpellEffIndex /*effIndex*/) pet->UnlearnSpecializationSpell(); pet->SetSpecialization(m_miscData[0]); pet->LearnSpecializationSpell(); - player->AddPetInfo(pet); player->SendTalentsInfoData(true); break; } diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.cpp b/src/server/game/Tools/CharacterDatabaseCleaner.cpp index ec056395..9769ed0c 100644 --- a/src/server/game/Tools/CharacterDatabaseCleaner.cpp +++ b/src/server/game/Tools/CharacterDatabaseCleaner.cpp @@ -159,25 +159,25 @@ void CharacterDatabaseCleaner::CleanCharacterQuestStatus() CharacterDatabase.DirectExecute("DELETE FROM character_queststatus WHERE status = 0"); } -uint8 CheckSlot(PlayerPetSlotList &list, uint8 slot, uint32 id) -{ - uint32 index = 0; - for(PlayerPetSlotList::iterator itr = list.begin(); itr != list.end(); ++itr, ++index) - { - if ((*itr) == id) - { - TC_LOG_ERROR("misc", "Warning! CheckSlot. Pet Id:%u with Slot: %u already on slot %u", id, slot, index); - return 1; - } - - if (slot == index && (*itr) > 0 && (*itr) != id) - { - TC_LOG_ERROR("misc", "Warning! CheckSlot. Pet Id:%u with Slot: %u has another pet %u", id, slot, *itr); - return 2; - } - } - return 0; -} +//uint8 CheckSlot(PlayerPetSlotList &list, uint8 slot, uint32 id) +//{ +// uint32 index = 0; +// for(PlayerPetSlotList::iterator itr = list.begin(); itr != list.end(); ++itr, ++index) +// { +// if ((*itr) == id) +// { +// TC_LOG_ERROR("misc", "Warning! CheckSlot. Pet Id:%u with Slot: %u already on slot %u", id, slot, index); +// return 1; +// } +// +// if (slot == index && (*itr) > 0 && (*itr) != id) +// { +// TC_LOG_ERROR("misc", "Warning! CheckSlot. Pet Id:%u with Slot: %u has another pet %u", id, slot, *itr); +// return 2; +// } +// } +// return 0; +//} void CharacterDatabaseCleaner::CleanPetSlots() { diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index d7018553..575253a0 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -42,7 +42,7 @@ struct DumpTable //REMEMBER TO UPDATE DUMP_TABLE_COUNT!!! static DumpTable dumpTables[DUMP_TABLE_COUNT] = { - { "characters", DTT_CHARACTER, "`guid`, `account`, `name`, `slot`, `race`, `class`, `gender`, `level`, `xp`, `money`, `skin`, `face`, `hairStyle`, `hairColor`, `tattoo`, `horn`, `inventorySlots`, `blindfold`, `facialStyle`, `bankSlots`, `drunk`, `playerFlags`, `playerFlagsEx`, `position_x`, `position_y`, `position_z`, `map`, `instance_id`, `dungeonDifficulty`, `raidDifficulty`, `legacyRaidDifficulty`, `orientation`, `taximask`, `online`, `cinematic`, `totaltime`, `leveltime`, `created_time`, `logout_time`, `is_logout_resting`, `rest_bonus`, `trans_x`, `trans_y`, `trans_z`, `trans_o`, `transguid`, `extra_flags`, `at_login`, `zone`, `death_expire_time`, `taxi_path`, `totalKills`, `todayKills`, `yesterdayKills`, `killPoints`, `chosenTitle`, `watchedFaction`, `lfgBonusFaction`, `health`, `mana`, `latency`, `activespec`, `specialization`, `lootspecialization`, `exploredZones`, `equipmentCache`, `knownTitles`, `actionBars`, `currentpetnumber`, `petslot`, `grantableLevels`, `deleteInfos_Account`, `deleteInfos_Name`, `deleteDate`, `LastCharacterUndelete`, `transfer`, `transfer_request`, `transfer_history`"}, + { "characters", DTT_CHARACTER, "`guid`, `account`, `name`, `slot`, `race`, `class`, `gender`, `level`, `xp`, `money`, `skin`, `face`, `hairStyle`, `hairColor`, `tattoo`, `horn`, `inventorySlots`, `blindfold`, `facialStyle`, `bankSlots`, `drunk`, `playerFlags`, `playerFlagsEx`, `position_x`, `position_y`, `position_z`, `map`, `instance_id`, `dungeonDifficulty`, `raidDifficulty`, `legacyRaidDifficulty`, `orientation`, `taximask`, `online`, `cinematic`, `totaltime`, `leveltime`, `created_time`, `logout_time`, `is_logout_resting`, `rest_bonus`, `trans_x`, `trans_y`, `trans_z`, `trans_o`, `transguid`, `extra_flags`, `stable_slots`, `at_login`, `zone`, `death_expire_time`, `taxi_path`, `totalKills`, `todayKills`, `yesterdayKills`, `killPoints`, `chosenTitle`, `watchedFaction`, `lfgBonusFaction`, `health`, `mana`, `latency`, `activespec`, `specialization`, `lootspecialization`, `exploredZones`, `equipmentCache`, `knownTitles`, `actionBars`, `grantableLevels`, `deleteInfos_Account`, `deleteInfos_Name`, `deleteDate`, `LastCharacterUndelete`, `transfer`, `transfer_request`, `transfer_history`"}, { "character_donate", DTT_DONA_TABLE, "`owner_guid`, `itemguid`, `type`, `itemEntry`, `efircount`, `count`, `state`, `date`, `deletedate`, `account`"}, { "character_achievement", DTT_CHAR_TABLE, "`guid`, `achievement`, `date`"}, { "character_achievement_progress", DTT_CHAR_TABLE, "`guid`, `criteria`, `counter`, `date`"}, diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 4ffd507c..e33e8a2d 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -2928,7 +2928,7 @@ class misc_commandscript : public CommandScript pet->SetEffectiveLevel(creatureTarget->GetEffectiveLevel()); player->SetMinion(pet, true); - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); player->PetSpellInitialize(); return true; @@ -3057,10 +3057,10 @@ class misc_commandscript : public CommandScript { if (Pet* pet = player->GetPet()) { - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); // not let dismiss dead pet if (pet && pet->isAlive()) - player->RemovePet(pet); + player->RemovePet(pet, PET_SAVE_AS_CURRENT, true); } } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 6e24ef61..027af102 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -1656,7 +1656,7 @@ class npc_commandscript : public CommandScript // caster have pet now player->SetMinion(pet, true); - pet->SavePetToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); player->PetSpellInitialize(); return true; diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 403d462b..c82dd431 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -2854,7 +2854,7 @@ class spell_gen_summon_elemental : public SpellScriptLoader if (GetCaster()) if (Unit* owner = GetCaster()->GetOwner()) if (owner->GetTypeId() == TYPEID_PLAYER) // todo: this check is maybe wrong - owner->ToPlayer()->RemovePet(NULL); + owner->ToPlayer()->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true); } void Register() override