diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 5ddb472e87..0ab78573e5 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -318,9 +318,9 @@ void Creature::AddToWorld() GetVehicleKit()->Install(); // If the creature has been summoned, register it for the summoner - if (IsSummon()) - if (Unit* summoner = GetSummonInfo()->GetUnitSummoner()) - summoner->RegisterSummon(GetSummonInfo()); + if (SummonInfo* summonInfo = GetSummonInfo()) + if (Unit* summoner = summonInfo->GetUnitSummoner()) + summoner->RegisterSummon(summonInfo); if (GetZoneScript()) GetZoneScript()->OnCreatureCreate(this); @@ -332,9 +332,9 @@ void Creature::RemoveFromWorld() if (IsInWorld()) { // If the creature about to despawn, unregister it for the summoner - if (IsSummon()) - if (Unit* summoner = GetSummonInfo()->GetUnitSummoner()) - summoner->UnregisterSummon(GetSummonInfo()); + if (SummonInfo* summonInfo = GetSummonInfo()) + if (Unit* summoner = summonInfo->GetUnitSummoner()) + summoner->UnregisterSummon(summonInfo); if (GetZoneScript()) GetZoneScript()->OnCreatureRemove(this); diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp index c58fd6be6a..2dea277d29 100644 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp +++ b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp @@ -158,3 +158,8 @@ bool SummonInfo::IsControlledBySummoner() const { return _control > SummonPropertiesControl::None; } + +SummonPropertiesSlot SummonInfo::GetSummonSlot() const +{ + return _summonSlot; +} diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h index 7ed9c98280..a4bc08ddaf 100644 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h +++ b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h @@ -77,6 +77,8 @@ public: void SetUseSummonerFaction(bool set); // Returns true when the summon is either a Guardian, Pet, Vehicle or Possessed summon bool IsControlledBySummoner() const; + // Returns the summon slot that the summon is going to be stored in + SummonPropertiesSlot GetSummonSlot() const; private: Creature* _summonedCreature; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 03d37a987a..d0022d51fc 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -184,12 +184,6 @@ void TempSummon::InitStats(uint32 duration) int32 slot = m_Properties->Slot; if (slot > 0) { - if (owner->m_SummonSlot[slot] && owner->m_SummonSlot[slot] != GetGUID()) - { - Creature* oldSummon = GetMap()->GetCreature(owner->m_SummonSlot[slot]); - if (oldSummon && oldSummon->IsSummon()) - oldSummon->ToTempSummon()->UnSummon(); - } owner->m_SummonSlot[slot] = GetGUID(); } diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 0913356517..dfe0dff042 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -53,17 +53,8 @@ void Totem::Update(uint32 time) void Totem::InitStats(uint32 duration) { // client requires SMSG_TOTEM_CREATED to be sent before adding to world and before removing old totem - if (GetOwner()->GetTypeId() == TYPEID_PLAYER - && m_Properties->Slot >= SUMMON_SLOT_TOTEM_FIRE - && m_Properties->Slot < MAX_TOTEM_SLOT) + if (GetOwner()->GetTypeId() == TYPEID_PLAYER && m_Properties->Slot >= SUMMON_SLOT_TOTEM_FIRE && m_Properties->Slot < MAX_TOTEM_SLOT) { - WorldPacket data(SMSG_TOTEM_CREATED, 1 + 8 + 4 + 4); - data << uint8(m_Properties->Slot - 1); - data << uint64(GetGUID()); - data << uint32(duration); - data << uint32(GetUInt32Value(UNIT_CREATED_BY_SPELL)); - GetOwner()->ToPlayer()->SendDirectMessage(&data); - // set display id depending on caster's race SetDisplayId(GetOwner()->GetModelForTotem(PlayerTotemType(m_Properties->ID))); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 569508864f..00c0100d0a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -82,6 +82,7 @@ #include "SummonInfo.h" #include "TemporarySummon.h" #include "Totem.h" +#include "TotemPackets.h" #include "Transport.h" #include "UnitAI.h" #include "UpdateFieldFlags.h" @@ -379,6 +380,8 @@ Unit::Unit(bool isWorldObject) : _oldFactionId = 0; _isWalkingBeforeCharm = false; + + _slottedSummons.resize(AsUnderlyingType(SummonPropertiesSlot::Max), nullptr); } //////////////////////////////////////////////////////////// @@ -8824,8 +8827,57 @@ void Unit::RegisterSummon(SummonInfo* summon) if (!summon) return; - if (!advstd::ranges::contains(_activeSummons, summon)) - _activeSummons.push_back(summon); + SummonPropertiesSlot slot = summon->GetSummonSlot(); + + // Wild Summons usually don't need any tracking but some do have certain SummonPropertiesFlags which + // causes them to interact with the summoner in some way (e.g. despawning when the summoner died) + if (slot == SummonPropertiesSlot::None) + { + if (advstd::ranges::contains(_wildSummons, summon)) + return; + + _wildSummons.push_back(summon); + return; + } + + // Select any available totem slot if empty, otherwise they just replace the oldest summon of the same entry + if (slot == SummonPropertiesSlot::AnyAvailableTotem) + { + // @Todo + slot = SummonPropertiesSlot::Totem1; + } + + uint8 targetSlot = AsUnderlyingType(slot); + + // Another is occupying the slot. Unsummon that summon first + if (SummonInfo* activeSummon = _slottedSummons[targetSlot]) + activeSummon->GetSummonedCreature()->DespawnOrUnsummon(); + + _slottedSummons[targetSlot] = summon; + + switch (slot) + { + case SummonPropertiesSlot::Totem1: + case SummonPropertiesSlot::Totem2: + case SummonPropertiesSlot::Totem3: + case SummonPropertiesSlot::Totem4: + { + Creature const* summonedCreature = summon->GetSummonedCreature(); + if (Player* playerSummoner = Object::ToPlayer(summon->GetUnitSummoner())) + { + WorldPackets::Totem::TotemCreated totemCreated; + totemCreated.Totem = summonedCreature->GetGUID(); + totemCreated.SpellID = summonedCreature->GetUInt32Value(UNIT_CREATED_BY_SPELL); + totemCreated.Duration = summon->GetRemainingDuration().value_or(0ms).count(); + totemCreated.Slot = AsUnderlyingType(summon->GetSummonSlot()) - 1; + + playerSummoner->SendDirectMessage(totemCreated.Write()); + } + break; + } + default: + break; + } } void Unit::UnregisterSummon(SummonInfo* summon) @@ -8833,7 +8885,15 @@ void Unit::UnregisterSummon(SummonInfo* summon) if (!summon) return; - std::erase(_activeSummons, summon); + SummonPropertiesSlot slot = summon->GetSummonSlot(); + if (slot == SummonPropertiesSlot::None) + { + std::erase(_wildSummons, summon); + return; + } + + uint8 targetSlot = AsUnderlyingType(slot); + _slottedSummons[targetSlot] = nullptr; } void Unit::SetShapeshiftForm(ShapeshiftForm form) @@ -9626,7 +9686,8 @@ void Unit::RemoveFromWorld() } // Clear all active summon references. If we are about to switch map instances, we want to make sure that we leave all summons behind so we won't do threadunsafe operations - _activeSummons.clear(); + _wildSummons.clear(); + _slottedSummons.clear(); WorldObject::RemoveFromWorld(); m_duringRemoveFromWorld = false; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index e3efadc912..b37c6c571e 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1826,7 +1826,9 @@ class TC_GAME_API Unit : public WorldObject /* Player Movement fields END*/ - std::vector _activeSummons; + // SummonInfo slot handling + std::vector _wildSummons; + std::vector _slottedSummons; }; namespace Trinity