diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp index 7fde3f9f51..37ade72ccf 100644 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp +++ b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp @@ -32,7 +32,8 @@ SummonInfo::SummonInfo(Creature* summonedCreature, SummonInfoArgs const& args) : _summonedCreature(ASSERT_NOTNULL(summonedCreature)), _summonerGUID(args.Summoner ? args.Summoner->GetGUID() : ObjectGuid::Empty), _remainingDuration(args.Duration), _maxHealth(args.MaxHealth), _creatureLevel(args.CreatureLevel), - _flags(SummonPropertiesFlags::None), _control(SummonPropertiesControl::None), _summonSlot(SummonPropertiesSlot::None) + _flags(SummonPropertiesFlags::None), _control(SummonPropertiesControl::None), _summonSlot(SummonPropertiesSlot::None), + _hasBeenSummonedByCreature(args.Summoner ? args.Summoner->IsCreature() : false) { if (args.SummonPropertiesId.has_value()) InitializeSummonProperties(*args.SummonPropertiesId, Object::ToUnit(args.Summoner)); @@ -59,7 +60,7 @@ void SummonInfo::InitializeSummonProperties(uint32 summonPropertiesId, Unit cons if (_flags.HasFlag(SummonPropertiesFlags::UseSummonerFaction)) _factionId = summoner->GetFaction(); - if (_control != SummonPropertiesControl::None) + if (IsControlledBySummoner()) { // Controlled summons inherit the level of their summoner unless explicitly stated otherwise. // Level can be overridden either by SummonPropertiesFlags::UseCreatureLevel or by a spell effect value @@ -234,7 +235,20 @@ void SummonInfo::UpdateRemainingDuration(Milliseconds deltaTime) bool SummonInfo::DespawnsOnSummonerLogout() const { - return _flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerLogout); + if (_flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerLogout)) + return true; + + if (IsControlledBySummoner()) + { + // Controlled creatures summoned by a creature will only despawn when not engaged + if (_hasBeenSummonedByCreature) + return !_summonedCreature->IsEngaged(); + + // Player controlled summons, however, always despawn + return true; + } + + return false; } void SummonInfo::SetDespawnOnSummonerLogout(bool set) @@ -247,7 +261,20 @@ void SummonInfo::SetDespawnOnSummonerLogout(bool set) bool SummonInfo::DespawnsOnSummonerDeath() const { - return _flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerDeath); + if (_flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerDeath)) + return true; + + if (IsControlledBySummoner()) + { + // Controlled creatures summoned by a creature will only despawn when not engaged (they will despawn when reaching home) + if (_hasBeenSummonedByCreature) + return !_summonedCreature->IsEngaged(); + + // Player controlled summons, however, always despawn + return true; + } + + return false; } void SummonInfo::SetDespawnOnSummonerDeath(bool set) diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h index da61b3b089..8cc909e2e4 100644 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h +++ b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h @@ -114,6 +114,7 @@ private: EnumFlag _flags; SummonPropertiesControl _control; SummonPropertiesSlot _summonSlot; + bool _hasBeenSummonedByCreature; }; #endif // _SummonInfo_h__ diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 161cefd452..6a2c6ace7d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -8606,7 +8606,8 @@ void Unit::setDeathState(DeathState s) ExitVehicle(); // Exit vehicle before calling RemoveAllControlled // vehicles use special type of charm that is not removed by the next function // triggering an assert - UnsummonAllTotems(); + + DespawnSummonsOnSummonerDeath(); RemoveAllControlled(); RemoveAllAurasOnDeath(); } @@ -8889,6 +8890,19 @@ void Unit::DespawnSummonsOnSummonerLogout() summon->GetSummonedCreature()->DespawnOrUnsummon(); } +void Unit::DespawnSummonsOnSummonerDeath() +{ + std::vector slottedSummons = _slottedSummons; + for (SummonInfo* summon : slottedSummons) + if (summon && summon->DespawnsOnSummonerDeath()) + summon->GetSummonedCreature()->DespawnOrUnsummon(); + + std::vector wildSummons = _wildSummons; + for (SummonInfo* summon : wildSummons) + if (summon->DespawnsOnSummonerDeath()) + summon->GetSummonedCreature()->DespawnOrUnsummon(); +} + SummonInfo* Unit::GetSummonInSlot(SummonPropertiesSlot slot) const { if (slot == SummonPropertiesSlot::None || diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index e83f809aa2..f8327f7b02 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1339,6 +1339,8 @@ class TC_GAME_API Unit : public WorldObject void UnregisterSummon(SummonInfo* summon); // Despawns all summons that should despawn when the summoner logs out or despawns void DespawnSummonsOnSummonerLogout(); + // Despawns all summons that should despawn when the summoner dies + void DespawnSummonsOnSummonerDeath(); // Returns the currently active summon that is the summoner's specified summon slot SummonInfo* GetSummonInSlot(SummonPropertiesSlot slot) const; diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index 7ca8596e6a..64302c16a8 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -21,6 +21,7 @@ #include "MoveSpline.h" #include "MoveSplineInit.h" #include "PathGenerator.h" +#include "SummonInfo.h" #include "Vehicle.h" template @@ -81,6 +82,21 @@ void HomeMovementGenerator::DoFinalize(Creature* owner) { if (_arrived) { + // Creature controlled summons may persist for the remainder of a combat interaction. + // However, they do despawn upon reaching home when the summoner is either dead or gone + if (SummonInfo* summonInfo = owner->GetSummonInfo()) + { + if (summonInfo->IsControlledBySummoner()) + { + Unit* summoner = summonInfo->GetUnitSummoner(); + if (!summoner || !summoner->IsAlive()) + { + owner->DespawnOrUnsummon(); + return; + } + } + } + if (owner->IsStateRestoredOnEvade()) { owner->ClearUnitState(UNIT_STATE_EVADE);