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:
Shauren
2023-08-15 00:49:30 +02:00
parent 792f8041bb
commit fefc719263
6 changed files with 93 additions and 20 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());

View File

@@ -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)