aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp5
-rw-r--r--src/server/game/DataStores/DB2Stores.h2
-rw-r--r--src/server/game/Entities/Creature/TemporarySummon.cpp85
-rw-r--r--src/server/game/Entities/Creature/TemporarySummon.h7
-rw-r--r--src/server/game/Entities/Totem/Totem.cpp8
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp6
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)