diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 5 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/TemporarySummon.cpp | 85 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/TemporarySummon.h | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Totem/Totem.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Handlers/SpellHandler.cpp | 6 |
6 files changed, 93 insertions, 20 deletions
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index de885dc7598..f917f66b1bc 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -2941,7 +2941,7 @@ TaxiPathEntry const* DB2Manager::GetTaxiPath(uint32 from, uint32 to) const return Trinity::Containers::MapGetValuePtr(_taxiPaths, { from, to }); } -bool DB2Manager::IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +bool DB2Manager::IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId, bool requireAllTotems /*= true*/) { if (requiredTotemCategoryId == 0) return true; @@ -2958,7 +2958,8 @@ bool DB2Manager::IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint3 if (itemEntry->TotemCategoryType != reqEntry->TotemCategoryType) return false; - return (itemEntry->TotemCategoryMask & reqEntry->TotemCategoryMask) == reqEntry->TotemCategoryMask; + int32 sharedMask = itemEntry->TotemCategoryMask & reqEntry->TotemCategoryMask; + return requireAllTotems ? sharedMask == reqEntry->TotemCategoryMask : sharedMask != 0; } bool DB2Manager::IsToyItem(uint32 toy) const diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 1748d31abdf..f7281022a04 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -501,7 +501,7 @@ public: std::vector<SpellVisualMissileEntry const*> const* GetSpellVisualMissiles(int32 spellVisualMissileSetId) const; std::vector<TalentEntry const*> const& GetTalentsByPosition(uint32 class_, uint32 tier, uint32 column) const; TaxiPathEntry const* GetTaxiPath(uint32 from, uint32 to) const; - static bool IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); + static bool IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId, bool requireAllTotems = true); bool IsToyItem(uint32 toy) const; TransmogIllusionEntry const* GetTransmogIllusionForEnchantment(uint32 spellItemEnchantmentId) const; std::vector<TransmogSetEntry const*> const* GetTransmogSetsForItemModifiedAppearance(uint32 itemModifiedAppearanceId) const; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 2c7e5c61367..26f8418d1e9 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -18,7 +18,7 @@ #include "TemporarySummon.h" #include "CellImpl.h" #include "CreatureAI.h" -#include "DB2Structure.h" +#include "DB2Stores.h" #include "GameObject.h" #include "GameObjectAI.h" #include "GridNotifiers.h" @@ -29,6 +29,7 @@ #include "Pet.h" #include "Player.h" #include "SmoothPhasing.h" +#include "SpellMgr.h" #include <boost/container/small_vector.hpp> #include <sstream> @@ -42,6 +43,8 @@ m_timer(0ms), m_lifetime(0ms), m_canFollowOwner(true) m_unitTypeMask |= UNIT_MASK_SUMMON; } +TempSummon::~TempSummon() = default; + WorldObject* TempSummon::GetSummoner() const { return !m_summonerGUID.IsEmpty() ? ObjectAccessor::GetWorldObject(*this, m_summonerGUID) : nullptr; @@ -212,8 +215,11 @@ void TempSummon::InitStats(WorldObject* summoner, Milliseconds duration) if (Unit* unitSummoner = ToUnit(summoner)) { - int32 slot = m_Properties->Slot; - if (slot > 0) + std::ptrdiff_t slot = m_Properties->Slot; + if (slot == SUMMON_SLOT_ANY_TOTEM) + slot = FindUsableTotemSlot(unitSummoner); + + if (slot != 0) { if (!unitSummoner->m_SummonSlot[slot].IsEmpty() && unitSummoner->m_SummonSlot[slot] != GetGUID()) { @@ -350,14 +356,11 @@ void TempSummon::RemoveFromWorld() if (!IsInWorld()) return; - if (m_Properties) - { - int32 slot = m_Properties->Slot; - if (slot > 0) - if (Unit* owner = GetSummonerUnit()) - if (owner->m_SummonSlot[slot] == GetGUID()) - owner->m_SummonSlot[slot].Clear(); - } + if (m_Properties && m_Properties->Slot != 0) + if (Unit* owner = GetSummonerUnit()) + for (ObjectGuid& summonSlot : owner->m_SummonSlot) + if (summonSlot == GetGUID()) + summonSlot.Clear(); //if (GetOwnerGUID()) // TC_LOG_ERROR("entities.unit", "Unit {} has owner guid when removed from world", GetEntry()); @@ -365,6 +368,66 @@ void TempSummon::RemoveFromWorld() Creature::RemoveFromWorld(); } +std::ptrdiff_t TempSummon::FindUsableTotemSlot(Unit const* summoner) const +{ + auto totemBegin = summoner->m_SummonSlot.begin() + SUMMON_SLOT_TOTEM; + auto totemEnd = summoner->m_SummonSlot.begin() + MAX_TOTEM_SLOT; + + // first try exact guid match + auto totemSlot = std::find_if(totemBegin, totemEnd, [&](ObjectGuid const& otherTotemGuid) + { + return otherTotemGuid == GetGUID(); + }); + + // then a slot that shares totem category with this new summon + if (totemSlot == totemEnd) + totemSlot = std::find_if(totemBegin, totemEnd, [&](ObjectGuid const& otherTotemGuid) { return IsSharingTotemSlotWith(otherTotemGuid); }); + + // any empty slot...? + if (totemSlot == totemEnd) + totemSlot = std::find_if(totemBegin, totemEnd, [](ObjectGuid const& otherTotemGuid) { return otherTotemGuid.IsEmpty(); }); + + // if no usable slot was found, try used slot by a summon with the same creature id + // we must not despawn unrelated summons + if (totemSlot == totemEnd) + totemSlot = std::find_if(totemBegin, totemEnd, [&](ObjectGuid const& otherTotemGuid) { return GetEntry() == otherTotemGuid.GetEntry(); }); + + // if no slot was found, this summon gets no slot and will not be stored in m_SummonSlot + if (totemSlot == totemEnd) + return 0; + + return totemSlot - summoner->m_SummonSlot.begin(); +} + +bool TempSummon::IsSharingTotemSlotWith(ObjectGuid objectGuid) const +{ + Creature const* otherSummon = GetMap()->GetCreature(objectGuid); + if (!otherSummon) + return false; + + SpellInfo const* mySummonSpell = sSpellMgr->GetSpellInfo(m_unitData->CreatedBySpell, DIFFICULTY_NONE); + if (!mySummonSpell) + return false; + + SpellInfo const* otherSummonSpell = sSpellMgr->GetSpellInfo(otherSummon->m_unitData->CreatedBySpell, DIFFICULTY_NONE); + if (!otherSummonSpell) + return false; + + for (uint16 myTotemCategory : mySummonSpell->TotemCategory) + if (myTotemCategory) + for (uint16 otherTotemCategory : otherSummonSpell->TotemCategory) + if (otherTotemCategory && DB2Manager::IsTotemCategoryCompatibleWith(myTotemCategory, otherTotemCategory, false)) + return true; + + for (int32 myTotemId : mySummonSpell->Totem) + if (myTotemId) + for (int32 otherTotemId : otherSummonSpell->Totem) + if (otherTotemId && myTotemId == otherTotemId) + return true; + + return false; +} + std::string TempSummon::GetDebugInfo() const { std::stringstream sstr; diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index f9b54ac7b28..2144c58320b 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -45,7 +45,7 @@ class TC_GAME_API TempSummon : public Creature { public: explicit TempSummon(SummonPropertiesEntry const* properties, WorldObject* owner, bool isWorldObject); - virtual ~TempSummon() { } + virtual ~TempSummon(); void Update(uint32 diff) override; virtual void InitStats(WorldObject* summoner, Milliseconds duration); virtual void InitSummon(WorldObject* summoner); @@ -72,7 +72,12 @@ class TC_GAME_API TempSummon : public Creature SummonPropertiesEntry const* const m_Properties; std::string GetDebugInfo() const override; + + protected: + std::ptrdiff_t FindUsableTotemSlot(Unit const* summoner) const; + private: + bool IsSharingTotemSlotWith(ObjectGuid objectGuid) const; TempSummonType m_type; Milliseconds m_timer; Milliseconds m_lifetime; diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index ea648eec73d..b99117ada94 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -55,11 +55,15 @@ void Totem::InitStats(WorldObject* summoner, Milliseconds duration) // client requires SMSG_TOTEM_CREATED to be sent before adding to world and before removing old totem if (Player* owner = GetOwner()->ToPlayer()) { - if (m_Properties->Slot >= SUMMON_SLOT_TOTEM && m_Properties->Slot < MAX_TOTEM_SLOT) + int32 slot = m_Properties->Slot; + if (slot == SUMMON_SLOT_ANY_TOTEM) + slot = FindUsableTotemSlot(owner); + + if (slot >= SUMMON_SLOT_TOTEM && slot < MAX_TOTEM_SLOT) { WorldPackets::Totem::TotemCreated data; data.Totem = GetGUID(); - data.Slot = m_Properties->Slot - SUMMON_SLOT_TOTEM; + data.Slot = slot - SUMMON_SLOT_TOTEM; data.Duration = duration; data.SpellID = m_unitData->CreatedBySpell; owner->SendDirectMessage(data.Write()); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index ef29453aa15..198094d6a93 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -38,7 +38,7 @@ #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "SpellPackets.h" -#include "Totem.h" +#include "TemporarySummon.h" #include "TotemPackets.h" #include "World.h" @@ -533,8 +533,8 @@ void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& tot return; Creature* totem = ObjectAccessor::GetCreature(*_player, _player->m_SummonSlot[slotId]); - if (totem && totem->IsTotem() && totem->GetGUID() == totemDestroyed.TotemGUID) - totem->ToTotem()->UnSummon(); + if (totem && totem->IsTotem() && (totemDestroyed.TotemGUID.IsEmpty() || totem->GetGUID() == totemDestroyed.TotemGUID)) + totem->DespawnOrUnsummon(); } void WorldSession::HandleSelfResOpcode(WorldPackets::Spells::SelfRes& selfRes) |