mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Creatures: Updated totem slot assignment logic
* Implemented SUMMON_SLOT_ANY_TOTEM * Fixed showing totems on player frames * Fixed removing totems from player frames * Fixed being able to summon unlimited windfury totems
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user