diff --git a/src/common/Utilities/advstd.h b/src/common/Utilities/advstd.h index 810346ddee..ff2717c975 100644 --- a/src/common/Utilities/advstd.h +++ b/src/common/Utilities/advstd.h @@ -18,91 +18,9 @@ #ifndef TRINITY_ADVSTD_H #define TRINITY_ADVSTD_H -#include - -#ifdef __cpp_lib_bit_cast -#include -#else -#include // for memcpy -#endif -#include - // this namespace holds implementations of upcoming stdlib features that our c++ version doesn't have yet namespace advstd { -// libc++ is missing these two -[[nodiscard]] constexpr bool is_eq(std::partial_ordering cmp) noexcept { return cmp == 0; } -[[nodiscard]] constexpr bool is_neq(std::partial_ordering cmp) noexcept { return cmp != 0; } - -#ifdef __cpp_lib_bit_cast -using std::bit_cast; -#else -// libstdc++ v10 is missing this -template , - std::is_trivially_copyable, - std::is_trivially_copyable>, int> = 0> -[[nodiscard]] constexpr To bit_cast(From const& from) noexcept -{ - To to; - std::memcpy(&to, &from, sizeof(To)); - return to; -} -#endif -} - -// std::forward_like -#ifdef __cpp_lib_forward_like -#include -namespace advstd -{ -using std::forward_like; -} -#else -namespace advstd -{ -template -[[nodiscard]] constexpr decltype(auto) forward_like(U&& value) noexcept -{ - using ValueU = std::remove_reference_t; - using ValueConstU = std::conditional_t>, ValueU const, ValueU>; - return static_cast, ValueConstU&&, ValueConstU&>>(value); -} -} -#endif - -// std::ranges::contains -#include -#ifndef __cpp_lib_ranges_contains -#include // for std::ranges::equal_to, std::identity -#include // for std::input_iterator, std::sentinel_for, std::projected -#endif - -namespace advstd::ranges -{ -#ifndef __cpp_lib_ranges_contains -struct Contains -{ - template S, class T, class Proj = std::identity> - requires std::indirect_binary_predicate, T const*> - [[nodiscard]] inline constexpr bool operator()(I first, S last, T const& value, Proj proj = {}) const - { - return std::ranges::find(std::move(first), last, value, proj) != last; - } - - template - requires std::indirect_binary_predicate, Proj>, T const*> - [[nodiscard]] inline constexpr bool operator()(R&& r, T const& value, Proj proj = {}) const - { - auto first = std::ranges::begin(r); - auto last = std::ranges::end(r); - return std::ranges::find(std::move(first), last, value, proj) != last; - } -} inline constexpr contains; -#else -using std::ranges::contains; -#endif } #endif diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 3990ada129..d0e8ef223a 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -895,13 +895,4 @@ DEFINE_ENUM_FLAG(CurrencyTypeFlags); constexpr uint8 MAX_QUEST_DIFFICULTY = 10; -enum class TotemCategoryTypes : uint8 -{ - // Shaman Totems - EarthTotem = 2, - AirTotem = 3, - FireTotem = 4, - WaterTotem = 5, -}; - #endif diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index ab324a26db..fb11610b2a 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -637,6 +637,10 @@ void DBCManager::LoadStores(const std::string& dataPath, uint32 defaultLocale) if (!spellInfo) continue; + SpellLevelsEntry const* levels = sSpellLevelsStore.LookupEntry(spellInfo->LevelsID); + if (spellInfo->LevelsID && (!levels || levels->SpellLevel)) + continue; + if (spellInfo && spellInfo->Attributes & SPELL_ATTR0_PASSIVE) { for (CreatureFamilyEntry const* cFamily : sCreatureFamilyStore) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 4db91548ad..bac27977e0 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -51,9 +51,7 @@ #include "ScriptedGossip.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" -#include "SummonInfo.h" #include "TemporarySummon.h" -#include "TotemPackets.h" #include "Transport.h" #include "Util.h" #include "Vehicle.h" @@ -318,13 +316,6 @@ void Creature::AddToWorld() if (IsVehicle()) GetVehicleKit()->Install(); - if (SummonInfo* summonInfo = GetSummonInfo()) - { - // If the creature has been summoned, register it for the summoner - if (Unit* summoner = summonInfo->GetUnitSummoner()) - summoner->RegisterSummon(summonInfo); - } - if (GetZoneScript()) GetZoneScript()->OnCreatureCreate(this); } @@ -334,16 +325,6 @@ void Creature::RemoveFromWorld() { if (IsInWorld()) { - // If the creature about to despawn, unregister it for the summoner - if (SummonInfo* summonInfo = GetSummonInfo()) - { - // Perform cleanup actions if needed - summonInfo->HandlePreUnsummonActions(); - - if (Unit* summoner = summonInfo->GetUnitSummoner()) - summoner->UnregisterSummon(summonInfo); - } - if (GetZoneScript()) GetZoneScript()->OnCreatureRemove(this); @@ -604,10 +585,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (!GetCreatureAddon()) SetSheath(SHEATH_STATE_MELEE); - if (SummonInfo const* summonInfo = GetSummonInfo()) - SetFaction(summonInfo->GetFactionId().value_or(cInfo->faction)); - else - SetFaction(cInfo->faction); + SetFaction(cInfo->faction); uint32 npcflags = 0, unitFlags = 0, unitFlags2 = 0; ObjectMgr::ChooseCreatureFlags(cInfo, &npcflags, &unitFlags, &unitFlags2, _staticFlags, data); @@ -814,9 +792,6 @@ void Creature::Update(uint32 diff) { Unit::Update(diff); - if (SummonInfo* summonInfo = GetSummonInfo()) - summonInfo->UpdateRemainingDuration(Milliseconds(diff)); - // creature can be dead after Unit::Update call // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly) if (!IsAlive()) @@ -1428,22 +1403,12 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask) void Creature::SelectLevel() { - uint8 level = [&]() -> uint8 - { - // Some summons override their default level selection - if (SummonInfo const* summonInfo = GetSummonInfo()) - if (summonInfo->GetCreatureLevel().has_value()) - return *summonInfo->GetCreatureLevel(); - - CreatureTemplate const* cInfo = GetCreatureTemplate(); - - // level - uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel); - uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel); - - return minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); - }(); + CreatureTemplate const* cInfo = GetCreatureTemplate(); + // level + uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel); + uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel); + uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); SetLevel(level); } @@ -1454,17 +1419,10 @@ void Creature::UpdateLevelDependantStats() CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(getLevel(), cInfo->unit_class); // health - uint32 health = [&]() -> uint32 - { - // Some summons have their maximum health overridden by their summon spell effect values (e.g. player Shaman totems) - if (SummonInfo const* summonInfo = GetSummonInfo()) - if (summonInfo->GetMaxHealth().has_value()) - return *_summonInfo->GetMaxHealth(); + float healthmod = _GetHealthMod(rank); - float healthmod = _GetHealthMod(rank); - uint32 basehp = stats->GenerateHealth(cInfo); - return uint32(basehp * healthmod); - }(); + uint32 basehp = stats->GenerateHealth(cInfo); + uint32 health = uint32(basehp * healthmod); SetCreateHealth(health); SetMaxHealth(health); @@ -2803,22 +2761,6 @@ void Creature::InitializeMovementSpeeds() SetSpeed(MOVE_WALK, _creatureMovementInfo.WalkSpeed); } -void Creature::InitializeSummonInfo(SummonInfoArgs const& args) -{ - ASSERT(_summonInfo == nullptr, "SummonInfo has already been initialized and must not be allocated again!"); - _summonInfo = std::make_unique(this, args); -} - -SummonInfo* Creature::GetSummonInfo() const -{ - return _summonInfo.get(); -} - -bool Creature::IsSummon() const -{ - return _summonInfo != nullptr; -} - void Creature::AllLootRemovedFromCorpse() { if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient()) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 527bbcb3e1..7db13a4529 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -34,11 +34,8 @@ class Group; class Quest; class Player; class SpellInfo; -class SummonInfo; class WorldSession; -struct SummonInfoArgs; - enum MovementGeneratorType : uint8; struct VendorItemCount @@ -423,13 +420,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void ResetNoNpcDamageBelowPctHealthValue() { _noNpcDamageBelowPctHealth = 0.f; } float GetNoNpcDamageBelowPctHealthValue() const { return _noNpcDamageBelowPctHealth; } - // Initializes the summon API for this creature which marks it as summon and will be treated accordingly - void InitializeSummonInfo(SummonInfoArgs const& args); - // Returns a pointer to the SummonInfo API, nullptr if the creature is not a summon - SummonInfo* GetSummonInfo() const; - - bool IsSummon() const override; - protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); bool InitEntry(uint32 entry, CreatureData const* data = nullptr); @@ -520,9 +510,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma Optional _defaultMountDisplayIdOverride; float _noNpcDamageBelowPctHealth; - - // The Summon API which controls the behavior of summons - std::unique_ptr _summonInfo; }; class TC_GAME_API AssistDelayEvent : public BasicEvent diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp deleted file mode 100644 index 3d3c1d118d..0000000000 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "SummonInfo.h" - -#include "CharmInfo.h" -#include "Creature.h" -#include "CreatureAI.h" -#include "DBCEnums.h" -#include "DBCStores.h" -#include "Log.h" -#include "ObjectAccessor.h" -#include "Player.h" -#include "SpellMgr.h" -#include "SummonInfoArgs.h" -#include "TotemPackets.h" - -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), - _hasBeenSummonedByPlayer(args.Summoner ? args.Summoner->IsPlayer() : false) -{ - if (args.SummonPropertiesId.has_value()) - InitializeSummonProperties(*args.SummonPropertiesId, Object::ToUnit(args.Summoner)); -} - -void SummonInfo::InitializeSummonProperties(uint32 summonPropertiesId, Unit const* summoner) -{ - SummonPropertiesEntry const* summonProperties = sSummonPropertiesStore.LookupEntry(summonPropertiesId); - if (!summonProperties) - { - TC_LOG_ERROR("entities.unit", "Creature %s has been summoned with a non-existing SummonProperties.dbc entry (RecId: %u).", _summonedCreature->GetGUID().ToString().c_str(), summonPropertiesId); - return; - } - - if (summonProperties->Faction) - _factionId = summonProperties->Faction; - - _flags = summonProperties->GetFlags(); - _summonSlot = static_cast(summonProperties->Slot); - _control = static_cast(summonProperties->Control); - - if (summoner) - { - if (_flags.HasFlag(SummonPropertiesFlags::UseSummonerFaction)) - _factionId = summoner->GetFaction(); - - 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 - if (!_flags.HasFlag(SummonPropertiesFlags::UseCreatureLevel) && !_creatureLevel.has_value()) - _creatureLevel = summoner->getLevel(); - - // Controlled summons inherit their summoner's faction if not overridden by DBC data - if (!_factionId.has_value()) - _factionId = summoner->GetFaction(); - } - } -} - -Creature* SummonInfo::GetSummonedCreature() const -{ - return _summonedCreature; -} - -Unit* SummonInfo::GetUnitSummoner() const -{ - return ObjectAccessor::GetUnit(*_summonedCreature, _summonerGUID); -} - -GameObject* SummonInfo::GetGameObjectSummoner() const -{ - return ObjectAccessor::GetGameObject(*_summonedCreature, _summonerGUID); -} - -Optional SummonInfo::GetRemainingDuration() const -{ - return _remainingDuration; -} - -Optional SummonInfo::GetMaxHealth() const -{ - return _maxHealth; -} - -Optional SummonInfo::GetFactionId() const -{ - return _factionId; -} - -Optional SummonInfo::GetCreatureLevel() const -{ - return _creatureLevel; -} - -void SummonInfo::HandlePreSummonActions() -{ - // The summon is going to be treated as a pet. Prepare spell list. - if (_control == SummonPropertiesControl::Pet) - { - if (CharmInfo* charmInfo = _summonedCreature->InitCharmInfo()) - charmInfo->InitCharmCreateSpells(); - } - - if (Unit* summoner = GetUnitSummoner()) - { - SummonPropertiesSlot slot = GetSummonSlot(); - - // Controlled summons always set their creator guid, which is being used to display summoner names in their title - if (IsControlledBySummoner()) - _summonedCreature->SetCreatorGUID(summoner->GetGUID()); - - initializeReactState(); - - // Totem slot summons always send the TotemCreated packet. Some non-Shaman classes use this - // to display summon icons that can be canceled (Consecration, DK ghouls, Wild Mushrooms) - // This packet must be sent before the creature is being added to the world so that the client - // does send correct GUIDs in CMSG_TOTEM_DESTROYED - switch (slot) - { - case SummonPropertiesSlot::Totem1: - case SummonPropertiesSlot::Totem2: - case SummonPropertiesSlot::Totem3: - case SummonPropertiesSlot::Totem4: - { - if (Player* playerSummoner = Object::ToPlayer(summoner)) - { - WorldPackets::Totem::TotemCreated totemCreated; - totemCreated.Totem = _summonedCreature->GetGUID(); - totemCreated.SpellID = _summonedCreature->GetUInt32Value(UNIT_CREATED_BY_SPELL); - totemCreated.Duration = _remainingDuration.value_or(0ms).count(); - totemCreated.Slot = AsUnderlyingType(_summonSlot) - 1; - - playerSummoner->SendDirectMessage(totemCreated.Write()); - } - break; - } - default: - break; - } - } -} - -void SummonInfo::HandlePostSummonActions() -{ - - // If it's a summon with an expiration timer, mark it as active so its time won't stop ticking if no player is nearby - if (_remainingDuration.has_value()) - _summonedCreature->setActive(true); - - if (Unit* summoner = GetUnitSummoner()) - { - // Register Pet and enable its control - if (_control == SummonPropertiesControl::Pet) - { - _summonedCreature->SetOwnerGUID(summoner->GetGUID()); - - // Only set the Summon GUID when it's empty (prevent multiple applications on multi-summons like Feral Spirit) - if (summoner->GetMinionGUID() != _summonedCreature->GetGUID()) - { - summoner->SetMinionGUID(_summonedCreature->GetGUID()); - - // Enable player control over the summon - if (Player* playerSummoner = summoner->ToPlayer()) - { - _summonedCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - playerSummoner->SendPetSpells(this); - } - } - } - - // Set the critter guid if the summoned creature is a companion - if (_summonSlot == SummonPropertiesSlot::Critter) - summoner->SetCritterGUID(_summonedCreature->GetGUID()); - - // Infoke JustSummoned and IsSummonedBy AI hooks - if (summoner->IsCreature() && summoner->IsAIEnabled()) - summoner->ToCreature()->AI()->JustSummoned(_summonedCreature); - - if (_summonedCreature->IsAIEnabled()) - _summonedCreature->AI()->IsSummonedBy(summoner); - } - - // Casting passive spells - if (IsControlledBySummoner()) - castPassiveSpells(); -} - -void SummonInfo::HandlePreUnsummonActions() -{ - Unit* summoner = GetUnitSummoner(); - if (!summoner) - return; - - // Clear the current pet action bar and reset the Summon GUID - if (_control == SummonPropertiesControl::Pet && summoner->GetMinionGUID() == _summonedCreature->GetGUID()) - { - summoner->SetMinionGUID(ObjectGuid::Empty); - if (Player* player = summoner->ToPlayer()) - player->SendRemoveControlBar(); - } - - // Clear the critter guid if the summoned creature was a companion - if (_summonSlot == SummonPropertiesSlot::Critter && summoner->GetCritterGUID() == _summonedCreature->GetGUID()) - summoner->SetCritterGUID(ObjectGuid::Empty); - - if (summoner->IsCreature() && summoner->IsAIEnabled()) - summoner->ToCreature()->AI()->SummonedCreatureDespawn(_summonedCreature); -} - -void SummonInfo::UpdateRemainingDuration(Milliseconds deltaTime) -{ - if (!_remainingDuration.has_value() || _remainingDuration <= 0ms) - return; - - *_remainingDuration -= deltaTime; - if (_remainingDuration <= 0ms) - { - if (DespawnsWhenExpired()) - _summonedCreature->DespawnOrUnsummon(); - else - _summonedCreature->KillSelf(); - } -} - -bool SummonInfo::DespawnsOnSummonerLogout() const -{ - if (_flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerLogout)) - return true; - - if (IsControlledBySummoner()) - { - // Controlled creatures summoned by a creature will only despawn when not engaged - if (!_hasBeenSummonedByPlayer) - return !_summonedCreature->IsEngaged(); - - // Player controlled summons, however, always despawn - return true; - } - - return false; -} - -void SummonInfo::SetDespawnOnSummonerLogout(bool set) -{ - if (set) - _flags |= SummonPropertiesFlags::DespawnOnSummonerLogout; - else - _flags.RemoveFlag(SummonPropertiesFlags::DespawnOnSummonerLogout); -} - -bool SummonInfo::DespawnsOnSummonerDeath() const -{ - 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 (!_hasBeenSummonedByPlayer) - return !_summonedCreature->IsEngaged(); - - // Player controlled summons, however, always despawn - return true; - } - - return false; -} - -void SummonInfo::SetDespawnOnSummonerDeath(bool set) -{ - if (set) - _flags |= SummonPropertiesFlags::DespawnOnSummonerDeath; - else - _flags.RemoveFlag(SummonPropertiesFlags::DespawnOnSummonerDeath); -} - -bool SummonInfo::DespawnsWhenExpired() const -{ - return _flags.HasFlag(SummonPropertiesFlags::DespawnWhenExpired); -} - -void SummonInfo::SetDespawnWhenExpired(bool set) -{ - if (set) - _flags |= SummonPropertiesFlags::DespawnWhenExpired; - else - _flags.RemoveFlag(SummonPropertiesFlags::DespawnWhenExpired); -} - -bool SummonInfo::UsesSummonerFaction() const -{ - return _flags.HasFlag(SummonPropertiesFlags::UseSummonerFaction); -} - -void SummonInfo::SetUseSummonerFaction(bool set) -{ - if (set) - _flags |= SummonPropertiesFlags::UseSummonerFaction; - else - _flags.RemoveFlag(SummonPropertiesFlags::UseSummonerFaction); -} - -bool SummonInfo::IsControlledBySummoner() const -{ - return _control > SummonPropertiesControl::None && _control < SummonPropertiesControl::Vehicle; -} - -SummonPropertiesSlot SummonInfo::GetSummonSlot() const -{ - return _summonSlot; -} - -SummonPropertiesControl SummonInfo::GetControl() const -{ - return _control; -} - -bool SummonInfo::IsClassPet() const -{ - return _isClassPet; -} - -void SummonInfo::castPassiveSpells() -{ - CreatureTemplate const* creatureInfo = _summonedCreature->GetCreatureTemplate(); - if (!creatureInfo || !creatureInfo->family) - return; - - CreatureFamilyEntry const* creatureFamilyEntry = sCreatureFamilyStore.LookupEntry(creatureInfo->family); - if (!creatureFamilyEntry) - return; - - PetFamilySpellsStore::const_iterator itr = sPetFamilySpellsStore.find(creatureFamilyEntry->ID); - if (itr == sPetFamilySpellsStore.end()) - return; - - for (uint32 spellId : itr->second) - { - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) - { - if (spellInfo->IsPassive()) - { - _summonedCreature->CastSpell(nullptr, spellId); - //if (spellInfo->HasAttribute(SPELL_ATTR4_OWNER_POWER_SCALING)) - // _scalingAuras.push_back(spellId); - } - } - } -} -void SummonInfo::initializeReactState() -{ - switch (_control) - { - case SummonPropertiesControl::Guardian: - if (_flags.HasFlag(SummonPropertiesFlags::GuardianActsLikePet)) - _summonedCreature->SetReactState(REACT_ASSIST); - else - _summonedCreature->SetReactState(REACT_AGGRESSIVE); - break; - case SummonPropertiesControl::Pet: - // Player pets default to assist while creature summons retain their original state - if (_hasBeenSummonedByPlayer) - _summonedCreature->SetReactState(REACT_ASSIST); - else if (CharmInfo* charmInfo = _summonedCreature->GetCharmInfo()) - charmInfo->RestoreState(); - break; - case SummonPropertiesControl::Possessed: - _summonedCreature->SetReactState(REACT_PASSIVE); - break; - default: - if (CharmInfo* charmInfo = _summonedCreature->GetCharmInfo()) - charmInfo->RestoreState(); - break; - } -} diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h deleted file mode 100644 index 08a202f07d..0000000000 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef _SummonInfo_h__ -#define _SummonInfo_h__ - -#include "DBCEnums.h" -#include "Define.h" -#include "Duration.h" -#include "EnumFlag.h" -#include "ObjectGuid.h" -#include "Optional.h" - -class Creature; -class GameObject; -class Unit; - -struct SummonInfoArgs; - -/* -The API for summoned creatures. This class contains all the functionality and features to handle summons -in a safe manner. However, do not store pointers to SummonInfo instances in scripts as they may change during runtime. -Instead resort to the accessor methods provided in the Unit class which always return intact instances -and pointers to both ends of a summon relation: -- Unit::GetSummonInSlot -- Unit::GetSummonsByCreatureId -- Unit::GetSummonsBySpellId - */ -class TC_GAME_API SummonInfo -{ -public: - SummonInfo(SummonInfo const& right) = delete; - SummonInfo(SummonInfo&& right) = delete; - SummonInfo& operator=(SummonInfo const& right) = delete; - SummonInfo& operator=(SummonInfo&& right) = delete; - - explicit SummonInfo(Creature* summonedCreature, SummonInfoArgs const& args); - - // Initializes additional settings based on the provided SummonProperties ID. - void InitializeSummonProperties(uint32 summonPropertiesId, Unit const* summoner); - // Returns the creature that is tied to this SummonInfo instance. This pointer can never be null. - Creature* GetSummonedCreature() const; - // Returns the Unit summoner that has summoned the creature, or nullptr if no summoner has been provided or if the summoner is not an Unit. - Unit* GetUnitSummoner() const; - // Returns the GameObject summoner that has summoned the creature, or nullptr if no summoner has been provided or if the summoner is not a GameObject. - GameObject* GetGameObjectSummoner() const; - - // Returns the remaining time until the summon expires. Nullopt when no duration was set which implies that the summon is permanent. - Optional GetRemainingDuration() const; - // Returns the health amount that will override the default max health calculation. Nullopt when no amount is provided. - Optional GetMaxHealth() const; - // Returns the FactionTemplate ID of the summon that is overriding the default ID of the creature. Nullopt when the faction has not been overriden. - Optional GetFactionId() const; - // Returns the level of the creature that will override the default level calculation. Nullopt when the creature uses its default values. - Optional GetCreatureLevel() const; - - // Handles all summon actions which must be performed before the summoned creature is being added to the world - void HandlePreSummonActions(); - // Handles all summon actions which must be performed after the summoned creature has been added to the world - void HandlePostSummonActions(); - // Handles all summon actions which much be performend before the summoned creature is being removed from the world - void HandlePreUnsummonActions(); - - // Updates the remaining duration of a summon and triggers the expiration - void UpdateRemainingDuration(Milliseconds deltaTime); - - // Returns true when the summon will despawn when the summoner logs out. This also includes despawning and teleporting between map instances. - bool DespawnsOnSummonerLogout() const; - // Marks the summon to despawn when the summoner logs out. This also includes despawning and teleporting between map instaces. - void SetDespawnOnSummonerLogout(bool set); - // Returns true when the summon will despawn when its summoner has died. - bool DespawnsOnSummonerDeath() const; - // Marks the summon to despawn when the summoner has died. - void SetDespawnOnSummonerDeath(bool set); - // Returns true when the summon will despawn after its duration has expired. If not set, the summon will just die. - bool DespawnsWhenExpired() const; - // Marks the summon to despawn after its duration has expired. If disabled, the summon will just die. - void SetDespawnWhenExpired(bool set); - // Returns true when the summon will inherit its summoner's faction. - bool UsesSummonerFaction() const; - // Marks the summon to inherit its summoner's faction. - 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; - // Returns the summon control type which determines how the summon can be controlled by the summoner - SummonPropertiesControl GetControl() const; - // Returns true when the summon is a class pet (Hunter Pet, Warlock Minion or Death Knight Ghoul) - bool IsClassPet() const; - -private: - // Looks up and casts all passive spells of the creature summon. Often used for scaling auras. - void castPassiveSpells(); - // Initializes the react state of the summon - void initializeReactState(); - - Creature* _summonedCreature; - ObjectGuid _summonerGUID; - Optional _remainingDuration; // Implemented in SummonInfo::UpdateRemainingDuration - Optional _maxHealth; // Implemented in Creature::UpdateLevelDependantStats - Optional _factionId; // Implemented in Creature::UpdateEntry - Optional _creatureLevel; // Implemented in Creature::SelectLevel - EnumFlag _flags; - SummonPropertiesControl _control; - SummonPropertiesSlot _summonSlot; - bool _hasBeenSummonedByPlayer; - bool _isClassPet; // Placeholder for class pet interface -}; - -#endif // _SummonInfo_h__ diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfoArgs.h b/src/server/game/Entities/Creature/SummonInfo/SummonInfoArgs.h deleted file mode 100644 index 40f274094f..0000000000 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfoArgs.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef _SummonInfoArgs_h__ -#define _SummonInfoArgs_h__ - -#include "Define.h" -#include "Duration.h" -#include "Optional.h" - -class WorldObject; - -// Controls the behavior of a summoned creature and must be set with extreme care. If you want a blank summon that just exists as a permanent spawn, leave all fields untouched. -struct SummonInfoArgs -{ - WorldObject const* Summoner = nullptr; - Optional MaxHealth; - Optional Duration; - Optional SummonPropertiesId; - Optional CreatureLevel; -}; - -#endif // _SummonInfoArgs_h__ diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 63181a98d3..9a5233a9b1 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -24,7 +24,6 @@ #include "ObjectAccessor.h" #include "Pet.h" #include "Player.h" -#include "SummonInfo.h" TempSummon::TempSummon(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Creature(isWorldObject), m_Properties(properties), m_type(TEMPSUMMON_MANUAL_DESPAWN), @@ -50,7 +49,6 @@ void TempSummon::Update(uint32 diff) { Creature::Update(diff); - /* if (m_deathState == DEAD) { UnSummon(); @@ -60,8 +58,18 @@ void TempSummon::Update(uint32 diff) { case TEMPSUMMON_MANUAL_DESPAWN: case TEMPSUMMON_DEAD_DESPAWN: - case TEMPSUMMON_TIMED_DESPAWN: break; + case TEMPSUMMON_TIMED_DESPAWN: + { + if (m_timer <= diff) + { + UnSummon(); + return; + } + + m_timer -= diff; + break; + } case TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT: { if (!IsInCombat()) @@ -148,12 +156,10 @@ void TempSummon::Update(uint32 diff) TC_LOG_ERROR("entities.unit", "Temporary summoned creature (entry: %u) have unknown type %u of ", GetEntry(), m_type); break; } - */ } void TempSummon::InitStats(uint32 duration) { - /* ASSERT(!IsPet()); m_timer = duration; @@ -166,6 +172,8 @@ void TempSummon::InitStats(uint32 duration) if (owner && IsTrigger() && m_spells[0]) { + SetFaction(owner->GetFaction()); + SetLevel(owner->getLevel()); if (owner->GetTypeId() == TYPEID_PLAYER) m_ControlledByPlayer = true; } @@ -175,8 +183,26 @@ void TempSummon::InitStats(uint32 duration) if (owner) { + 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(); + } + if (m_Properties->Control != SUMMON_CATEGORY_WILD) { + if (!m_Properties->Faction) + SetFaction(owner->GetFaction()); + + // Creator guid is always set for allied summons + SetCreatorGUID(owner->GetGUID()); + // Summons inherit their player summoner's guild data if (owner && (owner->IsPlayer() || owner->IsTotem())) { @@ -188,13 +214,18 @@ void TempSummon::InitStats(uint32 duration) } } } + + if (owner->IsTotem()) + owner->m_Controlled.insert(this); } - */ + + // If property has a faction defined, use it. + if (m_Properties->Faction) + SetFaction(m_Properties->Faction); } void TempSummon::InitSummon() { - /* if (Unit* owner = GetSummoner()) { if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled()) @@ -202,7 +233,6 @@ void TempSummon::InitSummon() if (IsAIEnabled()) AI()->IsSummonedBy(owner); } - */ } void TempSummon::UpdateObjectVisibilityOnCreate() @@ -212,10 +242,7 @@ void TempSummon::UpdateObjectVisibilityOnCreate() void TempSummon::SetTempSummonType(TempSummonType type) { - /* m_type = type; - GetSummonInfo()->SetDespawnWhenExpired(true); // To maintain legacy behavior in which all summons just despawn when expired - */ } void TempSummon::UnSummon(uint32 msTime) @@ -228,7 +255,6 @@ void TempSummon::UnSummon(uint32 msTime) return; } - /* //ASSERT(!IsPet()); if (IsPet()) { @@ -243,7 +269,6 @@ void TempSummon::UnSummon(uint32 msTime) if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled()) owner->ToCreature()->AI()->SummonedCreatureDespawn(this); } - */ AddObjectToRemoveList(); } @@ -259,6 +284,15 @@ void TempSummon::RemoveFromWorld() if (!IsInWorld()) return; + if (m_Properties) + { + int32 slot = m_Properties->Slot; + if (slot > 0) + if (Unit* owner = GetSummoner()) + if (owner->m_SummonSlot[slot] == GetGUID()) + owner->m_SummonSlot[slot].Clear(); + } + //if (GetOwnerGUID()) // TC_LOG_ERROR("entities.unit", "Unit %u has owner guid when removed from world", GetEntry()); @@ -274,7 +308,6 @@ Minion::Minion(SummonPropertiesEntry const* properties, Unit* owner, bool isWorl void Minion::InitStats(uint32 duration) { - /* TempSummon::InitStats(duration); SetReactState(REACT_PASSIVE); @@ -297,7 +330,6 @@ void Minion::InitStats(uint32 duration) SetUInt32Value(UNIT_NPC_FLAGS, GetCreatureTemplate()->npcflag); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); } - */ } void Minion::RemoveFromWorld() @@ -305,7 +337,6 @@ void Minion::RemoveFromWorld() if (!IsInWorld()) return; - /* Unit* owner = GetOwner(); if ((IsMinion() || IsControlableGuardian()) && !IsTotem() && !IsVehicle()) @@ -322,7 +353,6 @@ void Minion::RemoveFromWorld() if (totemOwner->m_Controlled.find(this) != totemOwner->m_Controlled.end()) totemOwner->m_Controlled.erase(this); } - */ TempSummon::RemoveFromWorld(); } @@ -352,42 +382,35 @@ Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool is { memset(m_statFromOwner, 0, sizeof(float)*MAX_STATS); m_unitTypeMask |= UNIT_MASK_GUARDIAN; - - /* if (properties && SummonTitle(properties->Title) == SummonTitle::Pet) { m_unitTypeMask |= UNIT_MASK_CONTROLABLE_GUARDIAN; InitCharmInfo(); } - */ } void Guardian::InitStats(uint32 duration) { - /* Minion::InitStats(duration); - InitStatsForLevel(getLevel()); + InitStatsForLevel(GetOwner()->getLevel()); if (GetOwner()->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) m_charmInfo->InitCharmCreateSpells(); SetReactState(REACT_AGGRESSIVE); - */ } void Guardian::InitSummon() { TempSummon::InitSummon(); - /* if (GetOwner()->GetTypeId() == TYPEID_PLAYER && GetOwner()->GetMinionGUID() == GetGUID() && !GetOwner()->GetCharmedGUID()) { GetOwner()->ToPlayer()->CharmSpellInitialize(); } - */ } Puppet::Puppet(SummonPropertiesEntry const* properties, Unit* owner) @@ -400,26 +423,20 @@ Puppet::Puppet(SummonPropertiesEntry const* properties, Unit* owner) void Puppet::InitStats(uint32 duration) { Minion::InitStats(duration); - - /* + SetLevel(GetOwner()->getLevel()); SetReactState(REACT_PASSIVE); - */ } void Puppet::InitSummon() { - /* Minion::InitSummon(); if (!SetCharmedBy(GetOwner(), CHARM_TYPE_POSSESS)) ABORT(); - */ } void Puppet::Update(uint32 time) { Minion::Update(time); - - /* //check if caster is channelling? if (IsInWorld()) { @@ -429,7 +446,6 @@ void Puppet::Update(uint32 time) /// @todo why long distance .die does not remove it } } - */ } void Puppet::RemoveFromWorld() @@ -437,8 +453,6 @@ void Puppet::RemoveFromWorld() if (!IsInWorld()) return; - /* RemoveCharmedBy(nullptr); - */ Minion::RemoveFromWorld(); } diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index e679b19782..39de736d89 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -119,6 +119,15 @@ class TC_GAME_API Guardian : public Minion bool InitStatsForLevel(uint8 level); void InitSummon() override; + bool UpdateStats(Stats stat) override; + bool UpdateAllStats() override; + void UpdateResistances(uint32 school) override; + void UpdateArmor() override; + void UpdateMaxHealth() override; + void UpdateMaxPower(Powers power) override; + void UpdateAttackPowerAndDamage(bool ranged = false) override; + void UpdateDamagePhysical(WeaponAttackType attType) override; + int32 GetBonusDamage() const { return m_bonusSpellDamage; } float GetBonusStatFromOwner(Stats stat) const { return m_statFromOwner[stat]; } void SetBonusDamage(int32 damage); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index af02e159a0..3dd381ac6f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -38,16 +38,14 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "OutdoorPvPMgr.h" -#include "PathGenerator.h" #include "PhasingHandler.h" +#include "PathGenerator.h" #include "Player.h" #include "ReputationMgr.h" #include "SpellAuraEffects.h" #include "SpellDefines.h" #include "SpellMgr.h" #include "StringConvert.h" -#include "SummonInfo.h" -#include "SummonInfoArgs.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -56,9 +54,9 @@ #include "UpdateFieldFlags.h" #include "UpdateMask.h" #include "Util.h" +#include "Vehicle.h" #include "VMapFactory.h" #include "VMapManager2.h" -#include "Vehicle.h" #include "WaypointMovementGenerator.h" #include "World.h" #include "WorldPacket.h" @@ -2008,23 +2006,74 @@ void WorldObject::AddObjectToRemoveList() TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatureExtraArgs const& summonArgs /*= { }*/) { - TempSummon* summon = new TempSummon(summonArgs.SummonProperties, summonArgs.Summoner, false); + uint32 mask = UNIT_MASK_SUMMON; - // Store special settings which have been extracted from spell effects/scripts - SummonInfoArgs args; - if (summonArgs.Summoner) - args.Summoner = summonArgs.Summoner; if (summonArgs.SummonProperties) - args.SummonPropertiesId = summonArgs.SummonProperties->ID; - if (summonArgs.SummonDuration) - args.Duration = Milliseconds(summonArgs.SummonDuration); - if (summonArgs.CreatureLevel > 0) - args.CreatureLevel = summonArgs.CreatureLevel; - if (summonArgs.SummonHealth) - args.MaxHealth = summonArgs.SummonHealth; + { + switch (summonArgs.SummonProperties->Control) + { + case SUMMON_CATEGORY_PET: + mask = UNIT_MASK_GUARDIAN; + break; + case SUMMON_CATEGORY_PUPPET: + mask = UNIT_MASK_PUPPET; + break; + case SUMMON_CATEGORY_VEHICLE: + mask = UNIT_MASK_MINION; + break; + case SUMMON_CATEGORY_WILD: + case SUMMON_CATEGORY_ALLY: + case SUMMON_CATEGORY_UNK: + { + switch (SummonTitle(summonArgs.SummonProperties->Title)) + { + case SummonTitle::Minion: + case SummonTitle::Guardian: + case SummonTitle::Runeblade: + mask = UNIT_MASK_GUARDIAN; + break; + case SummonTitle::Totem: + case SummonTitle::Lightwell: + mask = UNIT_MASK_TOTEM; + break; + case SummonTitle::Vehicle: + case SummonTitle::Mount: + mask = UNIT_MASK_SUMMON; + break; + case SummonTitle::Companion: + mask = UNIT_MASK_MINION; + break; + default: + if (summonArgs.SummonProperties->Flags & 512) // Mirror Image, Summon Gargoyle + mask = UNIT_MASK_GUARDIAN; + break; + } + break; + } + default: + return nullptr; + } + } - // Initialize the SummonInfo API which marks the creature as Summon - summon->InitializeSummonInfo(args); + TempSummon* summon = nullptr; + switch (mask) + { + case UNIT_MASK_SUMMON: + summon = new TempSummon(summonArgs.SummonProperties, summonArgs.Summoner, false); + break; + case UNIT_MASK_GUARDIAN: + summon = new Guardian(summonArgs.SummonProperties, summonArgs.Summoner, false); + break; + case UNIT_MASK_PUPPET: + summon = new Puppet(summonArgs.SummonProperties, summonArgs.Summoner); + break; + case UNIT_MASK_TOTEM: + summon = new Totem(summonArgs.SummonProperties, summonArgs.Summoner); + break; + case UNIT_MASK_MINION: + summon = new Minion(summonArgs.SummonProperties, summonArgs.Summoner, false); + break; + } // Create creature entity if (!summon->Create(GenerateLowGuid(), this, entry, pos, nullptr, summonArgs.VehicleRecID, true)) @@ -2037,8 +2086,6 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur if (summonArgs.Summoner) PhasingHandler::InheritPhaseShift(summon, summonArgs.Summoner); - summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, summonArgs.SummonSpellId); - TransportBase* transport = summonArgs.Summoner ? summonArgs.Summoner->GetTransport() : nullptr; if (transport) { @@ -2051,13 +2098,23 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur transport->AddPassenger(summon); } - SummonInfo* summonInfo = ASSERT_NOTNULL(summon->GetSummonInfo()); - + // Initialize tempsummon fields + summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, summonArgs.SummonSpellId); summon->SetHomePosition(pos); - summonInfo->HandlePreSummonActions(); summon->InitStats(summonArgs.SummonDuration); summon->SetPrivateObjectOwner(summonArgs.PrivateObjectOwner); + // Handle health argument + if (summonArgs.SummonHealth) + { + summon->SetMaxHealth(summonArgs.SummonHealth); + summon->SetHealth(summonArgs.SummonHealth); + } + + // Handle creature level argument + if (summonArgs.CreatureLevel) + summon->SetLevel(summonArgs.CreatureLevel); + if (!AddToMap(summon->ToCreature())) { // Returning false will cause the object to be deleted - remove from transport @@ -2069,7 +2126,6 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur } summon->InitSummon(); - summonInfo->HandlePostSummonActions(); // call MoveInLineOfSight for nearby creatures Trinity::AIRelocationNotifier notifier(*summon); diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index d6a6906a55..3374d252e3 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -28,8 +28,6 @@ #include "SpellHistory.h" #include "SpellAuras.h" #include "SpellAuraEffects.h" -#include "SummonInfo.h" -#include "SummonInfoArgs.h" #include "Unit.h" #include "Util.h" #include "Group.h" @@ -41,16 +39,10 @@ constexpr float PET_XP_FACTOR = 0.05f; Pet::Pet(Player* owner, PetType type) : Guardian(nullptr, owner, true), m_usedTalentCount(0), m_removed(false), m_petType(type), m_duration(0), m_auraRaidUpdateMask(0), m_loading(false), - m_declinedname(nullptr), m_petSlot(0) + m_declinedname(nullptr) { ASSERT(GetOwner()); - InitializeSummonInfo( - { - .Summoner = GetOwner(), - .CreatureLevel = GetOwner()->getLevel() - }); - m_unitTypeMask |= UNIT_MASK_PET; if (type == HUNTER_PET) m_unitTypeMask |= UNIT_MASK_HUNTER_PET; @@ -77,11 +69,6 @@ void Pet::AddToWorld() ///- Register the pet for guid lookup GetMap()->GetObjectsStore().Insert(GetGUID(), this); Unit::AddToWorld(); - - // SummonInfo should always be initialized for pets. Make sure that we will not go any further if this is no longer the case. - if (Unit* summoner = ASSERT_NOTNULL(GetSummonInfo())->GetUnitSummoner()) - summoner->RegisterSummon(GetSummonInfo()); - AIM_Initialize(); } @@ -102,10 +89,6 @@ void Pet::RemoveFromWorld() ///- Remove the pet from the accessor if (IsInWorld()) { - // SummonInfo should always be initialized for pets. Make sure that we will not go any further if this is no longer the case. - if (Unit* summoner = ASSERT_NOTNULL(GetSummonInfo())->GetUnitSummoner()) - summoner->UnregisterSummon(GetSummonInfo()); - ///- Don't call the function for Creature, normal mobs + totems go in a different storage Unit::RemoveFromWorld(); GetMap()->GetObjectsStore().Remove(GetGUID()); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a117efd088..f816a7f45c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16,11 +16,6 @@ */ #include "Player.h" - -#include - -#include - #include "AccountMgr.h" #include "AchievementMgr.h" #include "Archaeology.h" @@ -41,13 +36,13 @@ #include "CharmInfo.h" #include "Chat.h" #include "CinematicMgr.h" -#include "CombatLogPackets.h" #include "CombatPackets.h" +#include "CombatLogPackets.h" #include "Common.h" #include "ConditionMgr.h" #include "CreatureAI.h" -#include "DB2Stores.h" #include "DatabaseEnv.h" +#include "DB2Stores.h" #include "DisableMgr.h" #include "Formulas.h" #include "GameClient.h" @@ -62,13 +57,13 @@ #include "GroupMgr.h" #include "Guild.h" #include "GuildMgr.h" -#include "InstancePackets.h" #include "InstanceSaveMgr.h" #include "InstanceScript.h" +#include "InstancePackets.h" #include "ItemPackets.h" #include "KillRewarder.h" -#include "LFGMgr.h" #include "Language.h" +#include "LFGMgr.h" #include "Log.h" #include "LootItemStorage.h" #include "LootMgr.h" @@ -77,9 +72,9 @@ #include "MapManager.h" #include "MiscPackets.h" #include "MotionMaster.h" -#include "MovementPacketSender.h" -#include "MovementPackets.h" #include "MovementStructures.h" +#include "MovementPackets.h" +#include "MovementPacketSender.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -107,7 +102,6 @@ #include "SpellMgr.h" #include "SpellPackets.h" #include "StringConvert.h" -#include "SummonInfo.h" #include "TerrainMgr.h" #include "TicketMgr.h" #include "TradeData.h" @@ -125,6 +119,8 @@ #include "WorldSession.h" #include "WorldStateMgr.h" #include "WorldStatePackets.h" +#include +#include // corpse reclaim times static constexpr uint32 DEATH_EXPIRE_STEP = 5 * MINUTE; @@ -20767,49 +20763,6 @@ void Player::VehicleSpellInitialize() SendDirectMessage(&data); } -void Player::SendPetSpells(SummonInfo* summonInfo) -{ - if (!summonInfo) - return; - - Creature* summon = summonInfo->GetSummonedCreature(); - CharmInfo* charmInfo = summon->GetCharmInfo(); - if (!charmInfo) - return; - - uint8 maxCharmInfoSpells = 0; - for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i) - if (charmInfo->GetCharmSpell(i)->GetAction()) - ++maxCharmInfoSpells; - - WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 4 * maxCharmInfoSpells + 1); - data << summon->GetGUID(); - data << uint16(summon->GetCreatureTemplate()->family); - data << uint32(summonInfo->GetRemainingDuration().value_or(0ms).count()); - data << uint8(summon->GetReactState()); - data << uint8(charmInfo->GetCommandState()); - data << uint16(0); - - // action bar loop - charmInfo->BuildActionBar(&data); - - data << uint8(maxCharmInfoSpells); - - if (maxCharmInfoSpells) - { - for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i) - { - CharmSpellInfo* cspell = charmInfo->GetCharmSpell(i); - if (cspell->GetAction()) - data << uint32(cspell->packedData); - } - } - - data << uint8(0); // cooldowns count - - SendDirectMessage(&data); -} - void Player::CharmSpellInitialize() { Unit* charm = GetFirstControlled(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3ae5783617..9900d366eb 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1533,7 +1533,6 @@ class TC_GAME_API Player : public Unit, public GridObject void CharmSpellInitialize(); void PossessSpellInitialize(); void VehicleSpellInitialize(); - void SendPetSpells(SummonInfo* summonInfo); void SendRemoveControlBar() const; bool HasSpell(uint32 spell) const override; bool HasActiveSpell(uint32 spell) const; // show in spellbook diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 48f7b618b6..0913356517 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -53,8 +53,17 @@ 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))); } @@ -95,6 +104,16 @@ void Totem::UnSummon(uint32 msTime) CombatStop(); RemoveAurasDueToSpell(GetSpell(), GetGUID()); + // clear owner's totem slot + for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) + { + if (GetOwner()->m_SummonSlot[i] == GetGUID()) + { + GetOwner()->m_SummonSlot[i].Clear(); + break; + } + } + GetOwner()->RemoveAurasDueToSpell(GetSpell(), GetGUID()); // remove aura all party members too diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 43193985d6..a982ba99b4 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -1096,6 +1096,214 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, ######## ######## #######################################*/ +bool Guardian::UpdateStats(Stats stat) +{ + if (stat >= MAX_STATS) + return false; + + // value = ((base_value * base_pct) + total_value) * total_pct + float value = GetTotalStatValue(stat); + //ApplyStatBuffMod(stat, m_statFromOwner[stat], false); + float ownersBonus = 0.0f; + + SetStat(stat, int32(value)); + m_statFromOwner[stat] = ownersBonus; + UpdateStatBuffMod(stat); + + switch (stat) + { + case STAT_STRENGTH: + UpdateAttackPowerAndDamage(); + break; + case STAT_AGILITY: + UpdateArmor(); + break; + case STAT_STAMINA: + UpdateMaxHealth(); + break; + case STAT_INTELLECT: + UpdateMaxPower(POWER_MANA); + break; + case STAT_SPIRIT: + break; + default: + break; + } + + return true; +} + +bool Guardian::UpdateAllStats() +{ + UpdateMaxHealth(); + + for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) + UpdateStats(Stats(i)); + + for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i) + UpdateMaxPower(Powers(i)); + + UpdateAllResistances(); + + return true; +} + +void Guardian::UpdateResistances(uint32 school) +{ + if (school > SPELL_SCHOOL_NORMAL) + { + float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)); + SetResistance(SpellSchools(school), int32(value)); + } + else + UpdateArmor(); +} + +void Guardian::UpdateArmor() +{ + float value = 0.0f; + UnitMods unitMod = UNIT_MOD_ARMOR; + + value = GetFlatModifierValue(unitMod, BASE_VALUE); + value *= GetPctModifierValue(unitMod, BASE_PCT); + //value += GetStat(STAT_AGILITY) * 2.0f; (deprecated since 4.x) + value += GetFlatModifierValue(unitMod, TOTAL_VALUE); + value *= GetPctModifierValue(unitMod, TOTAL_PCT); + + + SetArmor(int32(value)); +} + +void Guardian::UpdateMaxHealth() +{ + UnitMods unitMod = UNIT_MOD_HEALTH; + float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA); + float multiplicator = 10.0f; + + float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); + + SetMaxHealth((uint32)value); +} + +void Guardian::UpdateMaxPower(Powers power) +{ + if (GetPowerIndex(power) == MAX_POWERS) + return; + + UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power)); + + float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f; + float multiplicator = 15.0f; + + float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); + + SetMaxPower(power, int32(value)); +} + +void Guardian::UpdateAttackPowerAndDamage(bool ranged) +{ + if (ranged) + return; + + float ap_per_strength = 2.0f; + float val = GetStat(STAT_STRENGTH) - 20.0f; + + val *= ap_per_strength; + + UnitMods unitMod = UNIT_MOD_ATTACK_POWER; + + SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val); + + // in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB + float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f; + + // UNIT_FIELD_(RANGED)_ATTACK_POWER field + SetInt32Value(UNIT_FIELD_ATTACK_POWER, int32(base_attPower)); + // UNIT_FIELD_(RANGED)_ATTACK_POWER_MOD_POS field + SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS, int32(attPowerMod > 0.f ? attPowerMod : 0.f)); + // UNIT_FIELD_(RANGED)_ATTACK_POWER_MOD_NEG field + SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_NEG, int32(attPowerMod < 0.f ? -attPowerMod : 0.f)); + // UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field + SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier); + + // automatically update weapon damage after attack power modification + UpdateDamagePhysical(BASE_ATTACK); + + // update pet spell power + int32 spellDamage = 0; + for (auto itr : GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE)) + spellDamage += itr->GetAmount(); + + SetBonusDamage(spellDamage); +} + +void Guardian::UpdateDamagePhysical(WeaponAttackType attType) +{ + if (attType > BASE_ATTACK) + return; + + float bonusDamage = 0.0f; + if (m_owner->GetTypeId() == TYPEID_PLAYER) + { + //force of nature + if (GetEntry() == ENTRY_TREANT) + { + int32 spellDmg = int32(m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + AsUnderlyingType(SPELL_SCHOOL_NATURE))) + m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + AsUnderlyingType(SPELL_SCHOOL_NATURE)); + if (spellDmg > 0) + bonusDamage = spellDmg * 0.09f; + } + //greater fire elemental + else if (GetEntry() == ENTRY_FIRE_ELEMENTAL) + { + int32 spellDmg = int32(m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + AsUnderlyingType(SPELL_SCHOOL_FIRE))) + m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + AsUnderlyingType(SPELL_SCHOOL_FIRE)); + if (spellDmg > 0) + bonusDamage = spellDmg * 0.4f; + } + } + + UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND; + + float att_speed = float(GetBaseAttackTime(BASE_ATTACK))/1000.0f; + + float base_value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed + bonusDamage; + float base_pct = GetPctModifierValue(unitMod, BASE_PCT); + float total_value = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float total_pct = GetPctModifierValue(unitMod, TOTAL_PCT); + + float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE); + float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE); + + float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct; + float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct; + + /// @todo: remove this + Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_MOD_ATTACKSPEED); + for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + { + switch ((*itr)->GetSpellInfo()->Id) + { + case 61682: + case 61683: + AddPct(mindamage, -(*itr)->GetAmount()); + AddPct(maxdamage, -(*itr)->GetAmount()); + break; + default: + break; + } + } + + SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage); + SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage); +} + void Guardian::SetBonusDamage(int32 damage) { //m_bonusSpellDamage = damage; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e5483af5d6..22634ee9f4 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -79,7 +79,6 @@ #include "SpellInfo.h" #include "SpellMgr.h" #include "SpellPackets.h" -#include "SummonInfo.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -379,8 +378,6 @@ Unit::Unit(bool isWorldObject) : _oldFactionId = 0; _isWalkingBeforeCharm = false; - - _slottedSummons.resize(AsUnderlyingType(SummonPropertiesSlot::Max), nullptr); } //////////////////////////////////////////////////////////// @@ -5726,16 +5723,6 @@ void Unit::SetOwnerGUID(ObjectGuid owner) RemoveFieldNotifyFlag(UF_FLAG_OWNER); } -Unit* Unit::GetCreator() const -{ - return ObjectAccessor::GetUnit(*this, GetCreatorGUID()); -} - -Creature* Unit::GetCritter() const -{ - return ObjectAccessor::GetCreature(*this, GetCritterGUID()); -} - Player* Unit::GetControllingPlayer() const { if (ObjectGuid guid = GetCharmerOrOwnerGUID()) @@ -6117,10 +6104,10 @@ void Unit::RemoveAllControlled() m_Controlled.erase(m_Controlled.begin()); if (target->GetCharmerGUID() == GetGUID()) target->RemoveCharmAuras(); - else if (target->GetOwnerGUID() == GetGUID() && target->IsSummon()) + else if (target->GetOwnerOrCreatorGUID() == GetGUID() && target->IsSummon()) target->ToTempSummon()->UnSummon(); - //else - // TC_LOG_ERROR("entities.unit", "Unit %u is trying to release unit %u which is neither charmed nor owned by it", GetEntry(), target->GetEntry()); + else + TC_LOG_ERROR("entities.unit", "Unit %u is trying to release unit %u which is neither charmed nor owned by it", GetEntry(), target->GetEntry()); } if (GetPetGUID()) TC_LOG_FATAL("entities.unit", "Unit %u is not able to release its pet %s", GetEntry(), GetPetGUID().ToString().c_str()); @@ -8606,8 +8593,7 @@ 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 - - DespawnSummonsOnSummonerDeath(); + UnsummonAllTotems(); RemoveAllControlled(); RemoveAllAurasOnDeath(); } @@ -8832,153 +8818,6 @@ uint32 Unit::GetCreatureTypeMask() const return (creatureType >= 1) ? (1 << (creatureType - 1)) : 0; } -void Unit::RegisterSummon(SummonInfo* summon) -{ - if (!summon) - return; - - 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(_unslottedSummons, summon)) - return; - - _unslottedSummons.push_back(summon); - return; - } - - uint8 targetSlot = AsUnderlyingType(slot); - - // Another summon is occupying the slot. Unsummon that one first - if (SummonInfo* activeSummon = _slottedSummons[targetSlot]) - activeSummon->GetSummonedCreature()->DespawnOrUnsummon(); - - _slottedSummons[targetSlot] = summon; -} - -void Unit::UnregisterSummon(SummonInfo* summon) -{ - if (!summon) - return; - - SummonPropertiesSlot slot = summon->GetSummonSlot(); - if (slot == SummonPropertiesSlot::None) - { - std::erase(_unslottedSummons, summon); - return; - } - - uint8 targetSlot = AsUnderlyingType(slot); - _slottedSummons[targetSlot] = nullptr; -} - -void Unit::DespawnSummonsOnSummonerLogout() -{ - std::vector unslottedSummons = _unslottedSummons; - for (SummonInfo* summon : unslottedSummons) - if (summon->DespawnsOnSummonerLogout()) - summon->GetSummonedCreature()->DespawnOrUnsummon(); - - std::vector slottedSummons = _slottedSummons; - for (SummonInfo* summon : slottedSummons) - if (summon && summon->DespawnsOnSummonerLogout()) - summon->GetSummonedCreature()->DespawnOrUnsummon(); -} - -void Unit::DespawnSummonsOnSummonerDeath() -{ - std::vector unslottedSummons = _unslottedSummons; - for (SummonInfo* summon : unslottedSummons) - if (summon->DespawnsOnSummonerDeath()) - summon->GetSummonedCreature()->DespawnOrUnsummon(); - - std::vector slottedSummons = _slottedSummons; - for (SummonInfo* summon : slottedSummons) - if (summon && summon->DespawnsOnSummonerDeath()) - summon->GetSummonedCreature()->DespawnOrUnsummon(); - -} - -void Unit::DismissPet() -{ - if (!GetMinionGUID()) - return; - - for (SummonInfo* summonInfo : GetSummonsByControlType(SummonPropertiesControl::Pet)) - summonInfo->GetSummonedCreature()->DespawnOrUnsummon(); -} - -SummonInfo* Unit::GetSummonInSlot(SummonPropertiesSlot slot) const -{ - if (slot == SummonPropertiesSlot::None || - slot == SummonPropertiesSlot::Max || - slot == SummonPropertiesSlot::AnyAvailableTotem) - return nullptr; - - return _slottedSummons[AsUnderlyingType(slot)]; -} - -std::vector Unit::GetSummonsByCreatureId(uint32 creatureId) -{ - if (creatureId == 0) - return {}; - - std::vector summons; - - - std::ranges::copy_if(_unslottedSummons, std::back_inserter(summons), [creatureId](SummonInfo const* summon) - { - return summon->GetSummonedCreature()->GetEntry() == creatureId; - }); - - std::ranges::copy_if(_slottedSummons, std::back_inserter(summons), [creatureId](SummonInfo const* summon) - { - return summon && summon->GetSummonedCreature()->GetEntry() == creatureId; - }); - - return summons; -} - -std::vector Unit::GetSummonsBySpellId(uint32 spellId) -{ - if (spellId == 0) - return {}; - - std::vector summons; - - std::ranges::copy_if(_unslottedSummons, std::back_inserter(summons), [spellId](SummonInfo const* summon) - { - return summon->GetSummonedCreature()->GetUInt32Value(UNIT_CREATED_BY_SPELL) == spellId; - }); - - std::ranges::copy_if(_slottedSummons, std::back_inserter(summons), [spellId](SummonInfo const* summon) - { - return summon && summon->GetSummonedCreature()->GetUInt32Value(UNIT_CREATED_BY_SPELL) == spellId; - }); - - return summons; -} - -std::vector Unit::GetSummonsByControlType(SummonPropertiesControl control) -{ - std::vector summons; - - std::ranges::copy_if(_unslottedSummons, std::back_inserter(summons), [control](SummonInfo const* summon) - { - return summon->GetControl() == control; - }); - - std::ranges::copy_if(_slottedSummons, std::back_inserter(summons), [control](SummonInfo const* summon) - { - return summon && summon->GetControl() == control; - }); - - return summons; -} - void Unit::SetShapeshiftForm(ShapeshiftForm form) { SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM, form); @@ -9744,7 +9583,7 @@ void Unit::RemoveFromWorld() RemoveAllDynObjects(); ExitVehicle(); // Remove applied auras with SPELL_AURA_CONTROL_VEHICLE - DespawnSummonsOnSummonerLogout(); + UnsummonAllTotems(); RemoveAllControlled(); RemoveAreaAurasDueToLeaveWorld(); @@ -9768,10 +9607,6 @@ 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 - _unslottedSummons.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 70cd079c7e..b0aa46ee44 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -87,7 +87,6 @@ class Spell; class SpellCastTargets; class SpellHistory; class SpellInfo; -class SummonInfo; class Totem; class Transport; class TransportBase; @@ -103,9 +102,6 @@ enum ProcFlagsSpellType : uint32; enum ZLiquidStatus : uint32; enum CharmType : uint8; -enum class SummonPropertiesControl : uint8; -enum class SummonPropertiesSlot: int8; - namespace Movement { class ExtraMovementStatusElement; @@ -760,7 +756,7 @@ class TC_GAME_API Unit : public WorldObject uint32 HasUnitTypeMask(uint32 mask) const { return mask & m_unitTypeMask; } void AddUnitTypeMask(uint32 mask) { m_unitTypeMask |= mask; } - virtual bool IsSummon() const { return false; } + bool IsSummon() const { return (m_unitTypeMask & UNIT_MASK_SUMMON) != 0; } bool IsMinion() const { return (m_unitTypeMask & UNIT_MASK_MINION) != 0; } bool IsGuardian() const { return (m_unitTypeMask & UNIT_MASK_GUARDIAN) != 0; } bool IsPet() const { return (m_unitTypeMask & UNIT_MASK_PET) != 0; } @@ -1081,21 +1077,14 @@ class TC_GAME_API Unit : public WorldObject ObjectGuid GetOwnerGUID() const override { return GetGuidValue(UNIT_FIELD_SUMMONEDBY); } void SetOwnerGUID(ObjectGuid owner); - ObjectGuid GetCreatorGUID() const { return GetGuidValue(UNIT_FIELD_CREATEDBY); } void SetCreatorGUID(ObjectGuid creator) { SetGuidValue(UNIT_FIELD_CREATEDBY, creator); } - Unit* GetCreator() const; - ObjectGuid GetMinionGUID() const { return GetGuidValue(UNIT_FIELD_SUMMON); } void SetMinionGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_SUMMON, guid); } - - ObjectGuid GetPetGUID() const { return m_SummonSlot[SUMMON_SLOT_PET]; } void SetPetGUID(ObjectGuid guid) { m_SummonSlot[SUMMON_SLOT_PET] = guid; } - - ObjectGuid GetCritterGUID() const { return GetGuidValue(UNIT_FIELD_CRITTER); } + ObjectGuid GetPetGUID() const { return m_SummonSlot[SUMMON_SLOT_PET]; } void SetCritterGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CRITTER, guid); } - Creature* GetCritter() const; - + ObjectGuid GetCritterGUID() const { return GetGuidValue(UNIT_FIELD_CRITTER); } ObjectGuid GetOwnerOrCreatorGUID() const { return GetOwnerGUID() ? GetOwnerGUID() : GetCreatorGUID(); } ObjectGuid GetCharmerGUID() const { return GetGuidValue(UNIT_FIELD_CHARMEDBY); } @@ -1334,26 +1323,6 @@ class TC_GAME_API Unit : public WorldObject std::array m_SummonSlot; std::array m_ObjectSlot; - // Registers the SummonInfo API of a summoned creature to allow to safely access it - void RegisterSummon(SummonInfo* summon); - // Unregisters the SummonInfo API of a summoned creature so it can no longer be accessed - 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(); - // Dismisses the currently active pet summon(s) (class pets and summons which are considered pets) - void DismissPet(); - - // Returns the currently active summon that is the summoner's specified summon slot - SummonInfo* GetSummonInSlot(SummonPropertiesSlot slot) const; - // Returns a vector with all currently active summons with the specified creature Id - std::vector GetSummonsByCreatureId(uint32 creatureId); - // Returns a vector with all currently active summons which have been created by the specified spell Id - std::vector GetSummonsBySpellId(uint32 spellId); - // Returns a vector with all currently active summons which are being controlled in a specific way - std::vector GetSummonsByControlType(SummonPropertiesControl control); - ShapeshiftForm GetShapeshiftForm() const { return ShapeshiftForm(GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM)); } void SetShapeshiftForm(ShapeshiftForm form); @@ -1850,10 +1819,6 @@ class TC_GAME_API Unit : public WorldObject std::unordered_map m_pendingMovementChanges; /* Player Movement fields END*/ - - // SummonInfo slot handling - std::vector _unslottedSummons; - std::vector _slottedSummons; }; namespace Trinity diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index cef6336780..974c089fe3 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "WorldSession.h" #include "CharmInfo.h" #include "Common.h" #include "CreatureAI.h" @@ -26,37 +27,141 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Pet.h" -#include "PetAI.h" #include "PetPackets.h" +#include "PetAI.h" #include "Player.h" #include "Spell.h" #include "SpellHistory.h" #include "SpellInfo.h" #include "SpellMgr.h" -#include "SummonInfo.h" #include "Util.h" #include "WorldPacket.h" -#include "WorldSession.h" -void WorldSession::HandleDismissCritter(WorldPackets::Pet::DismissCritter& dismissCritter) +void WorldSession::HandleDismissCritter(WorldPacket& recvData) { - if (dismissCritter.CritterGUID.IsEmpty() || _player->GetCritterGUID() != dismissCritter.CritterGUID) - return; + ObjectGuid guid; + recvData >> guid; - if (SummonInfo* summonInfo = _player->GetSummonInSlot(SummonPropertiesSlot::Critter)) - summonInfo->GetSummonedCreature()->DespawnOrUnsummon(); + TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_DISMISS_CRITTER for %s", guid.ToString().c_str()); + + Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + + if (!pet) + { + TC_LOG_DEBUG("entities.pet", "Vanitypet (%s) does not exist - player '%s' (guid: %u / account: %u) attempted to dismiss it (possibly lagged out)", + guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().GetCounter(), GetAccountId()); + return; + } + + if (_player->GetCritterGUID() == pet->GetGUID()) + { + if (pet->GetTypeId() == TYPEID_UNIT && pet->IsSummon()) + pet->ToTempSummon()->UnSummon(); + } } -void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 actionType, uint32 actionValue, ObjectGuid targetGuid, float x, float y, float z) +void WorldSession::HandlePetAction(WorldPacket& recvData) +{ + ObjectGuid guid1; + uint32 data; + ObjectGuid guid2; + float x, y, z; + recvData >> guid1; //pet guid + recvData >> data; + recvData >> guid2; //tag guid + // Position + recvData >> x; + recvData >> y; + recvData >> z; + + uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); + uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1 + + // used also for charmed creature + Unit* pet = ObjectAccessor::GetUnit(*_player, guid1); + TC_LOG_DEBUG("entities.pet", "HandlePetAction: %s - flag: %u, spellid: %u, target: %s.", guid1.ToString().c_str(), uint32(flag), spellid, guid2.ToString().c_str()); + + if (!pet) + { + TC_LOG_DEBUG("entities.pet", "HandlePetAction: %s doesn't exist for %s %s", guid1.ToString().c_str(), GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str()); + return; + } + + if (pet != GetPlayer()->GetFirstControlled()) + { + TC_LOG_DEBUG("entities.pet", "HandlePetAction: %s does not belong to %s %s", guid1.ToString().c_str(), GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str()); + return; + } + + if (!pet->IsAlive()) + { + SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : nullptr; + if (!spell) + return; + if (!spell->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD)) + return; + } + + /// @todo allow control charmed player? + if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) + return; + + if (GetPlayer()->m_Controlled.size() == 1) + HandlePetActionHelper(pet, guid1, spellid, flag, guid2, x, y, z); + else + { + //If a pet is dismissed, m_Controlled will change + std::vector controlled; + for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr) + if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->IsAlive()) + controlled.push_back(*itr); + for (std::vector::iterator itr = controlled.begin(); itr != controlled.end(); ++itr) + HandlePetActionHelper(*itr, guid1, spellid, flag, guid2, x, y, z); + } +} + +void WorldSession::HandlePetStopAttack(WorldPacket &recvData) +{ + ObjectGuid guid; + recvData >> guid; + + TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_STOP_ATTACK for %s", guid.ToString().c_str()); + + Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + + if (!pet) + { + TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s does not exist", guid.ToString().c_str()); + return; + } + + if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharmed()) + { + TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s isn't a pet or charmed creature of player %s", + guid.ToString().c_str(), GetPlayer()->GetName().c_str()); + return; + } + + if (!pet->IsAlive()) + return; + + pet->AttackStop(); +} + +void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, float x, float y, float z) { CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) + { + TC_LOG_DEBUG("entities.pet", "WorldSession::HandlePetAction(petGuid: %s, tagGuid: %s, spellId: %u, flag: %u): object (GUID: %u Entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", + guid1.ToString().c_str(), guid2.ToString().c_str(), spellid, flag, pet->GetGUID().GetCounter(), pet->GetEntry(), pet->GetTypeId()); return; + } - switch (actionType) + switch (flag) { case ACT_COMMAND: //0x07 - switch (actionValue) + switch (spellid) { case COMMAND_STAY: //flat=1792 //STAY if (pet->GetMotionMaster()->GetCurrentSlot() != MOTION_SLOT_CONTROLLED) @@ -77,7 +182,7 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->InterruptNonMeleeSpells(false); - pet->FollowTarget(player); + pet->FollowTarget(_player); charmInfo->SetCommandState(COMMAND_FOLLOW); charmInfo->SetIsCommandAttack(false); @@ -89,7 +194,7 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 case COMMAND_ATTACK: //spellid=1792 //ATTACK { // Can't attack if owner is pacified - if (player->HasAuraType(SPELL_AURA_MOD_PACIFY)) + if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY)) { //pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED); /// @todo Send proper error message to client @@ -97,7 +202,7 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 } // only place where pet can be player - Unit* TargetUnit = ObjectAccessor::GetUnit(*player, targetGuid); + Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2); if (!TargetUnit) return; @@ -132,7 +237,7 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 else { // 90% chance for pet and 100% chance for charmed creature - pet->SendPetAIReaction(petGuid); + pet->SendPetAIReaction(guid1); } } else // charmed player @@ -144,40 +249,28 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 charmInfo->SetIsReturning(false); pet->Attack(TargetUnit, true); - pet->SendPetAIReaction(petGuid); + pet->SendPetAIReaction(guid1); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) - { - // Releasing a currently charmed unit - if (pet->GetCharmerGUID() == player->GetGUID()) + if (pet->GetCharmerGUID() == GetPlayer()->GetGUID()) + _player->StopCastingCharm(); + else if (pet->GetOwnerOrCreatorGUID() == GetPlayer()->GetGUID()) { - player->StopCastingCharm(); - return; + ASSERT(pet->GetTypeId() == TYPEID_UNIT); + if (pet->IsPet()) + { + if (((Pet*)pet)->IsHunterPet()) + GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); + else + GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_DISMISS); + } + else if (pet->HasUnitTypeMask(UNIT_MASK_MINION)) + ((Minion*)pet)->UnSummon(); } - - if (player->GetMinionGUID().IsEmpty() || !pet->IsCreature()) - return; - - // Abandoning a hunter pet - Creature* creaturePet = pet->ToCreature(); - SummonInfo* summonInfo = creaturePet->GetSummonInfo(); - - // this should never be possible since we got here from a SummonInfo vector, but for good manners... - if (!summonInfo) - return; - - // Hunter pets use this command to abandon their class pets instead of dismissing them. - if (player->getClass() == CLASS_HUNTER && summonInfo->IsClassPet()) - { - // @todo: abandoning pets - } - else // otherwise this is just a dismiss command - creaturePet->DespawnOrUnsummon(); break; - } case COMMAND_MOVE_TO: pet->GetMotionMaster()->Clear(MOTION_SLOT_IDLE); pet->GetMotionMaster()->MoveIdle(); @@ -191,11 +284,11 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 charmInfo->SaveStayPosition(); break; default: - break; + TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } break; case ACT_REACTION: // 0x6 - switch (actionValue) + switch (spellid) { case REACT_PASSIVE: //passive pet->AttackStop(); @@ -204,7 +297,7 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 case REACT_AGGRESSIVE: //activete case REACT_ASSIST: if (pet->GetTypeId() == TYPEID_UNIT) - pet->ToCreature()->SetReactState(static_cast(actionValue)); + pet->ToCreature()->SetReactState(ReactStates(spellid)); break; } break; @@ -212,15 +305,18 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { - Unit* unitTarget = nullptr; + Unit* unit_target = nullptr; - if (!targetGuid.IsEmpty()) - unitTarget = ObjectAccessor::GetUnit(*player, targetGuid); + if (guid2) + unit_target = ObjectAccessor::GetUnit(*_player, guid2); // do not cast unknown spells - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(actionValue); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) + { + TC_LOG_ERROR("spells.pet", "WORLD: unknown PET spell id %i", spellid); return; + } for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { @@ -229,7 +325,7 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 } // do not cast not learned spells - if (!pet->HasSpell(actionValue) || spellInfo->IsPassive()) + if (!pet->HasSpell(spellid) || spellInfo->IsPassive()) return; // Clear the flags as if owner clicked 'attack'. AI will reset them @@ -244,15 +340,15 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE); - SpellCastResult result = spell->CheckPetCast(unitTarget); + SpellCastResult result = spell->CheckPetCast(unit_target); //auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle()) { - if (unitTarget) + if (unit_target) { - pet->SetOrientationTowards(unitTarget); - if (Player* player = unitTarget->ToPlayer()) + pet->SetOrientationTowards(unit_target); + if (Player* player = unit_target->ToPlayer()) pet->SendUpdateToPlayer(player); } else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget()) @@ -271,29 +367,29 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 if (result == SPELL_CAST_OK) { - unitTarget = spell->m_targets.GetUnitTarget(); + unit_target = spell->m_targets.GetUnitTarget(); //10% chance to play special pet attack talk, else growl //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell - if (pet->IsPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unitTarget) && (urand(0, 100) < 10)) + if (pet->IsPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) pet->SendPetActionSound(PET_TALK_SPECIAL_SPELL); else { - pet->SendPetAIReaction(petGuid); + pet->SendPetAIReaction(guid1); } - if (unitTarget && !player->IsFriendlyTo(unitTarget) && !pet->isPossessed() && !pet->IsVehicle()) + if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle()) { // This is true if pet has no target or has target but targets differs. - if (pet->GetVictim() != unitTarget) + if (pet->GetVictim() != unit_target) { pet->GetMotionMaster()->Clear(); if (CreatureAI* AI = pet->ToCreature()->AI()) { if (PetAI* petAI = dynamic_cast(AI)) - petAI->_AttackStart(unitTarget); // force victim switch + petAI->_AttackStart(unit_target); // force victim switch else - AI->AttackStart(unitTarget); + AI->AttackStart(unit_target); } } } @@ -303,12 +399,12 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 else { if (pet->isPossessed() || pet->IsVehicle()) /// @todo: confirm this check - Spell::SendCastResult(player, spellInfo, 0, result); + Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else spell->SendPetCastResult(result); - if (!pet->GetSpellHistory()->HasCooldown(actionValue)) - pet->GetSpellHistory()->ResetCooldown(actionValue, true); + if (!pet->GetSpellHistory()->HasCooldown(spellid)) + pet->GetSpellHistory()->ResetCooldown(spellid, true); spell->finish(false); delete spell; @@ -320,84 +416,10 @@ void HandlePetActionHelper(Player* player, Unit* pet, ObjectGuid petGuid, uint32 break; } default: - break; + TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } } -void WorldSession::HandlePetAction(WorldPackets::Pet::PetAction& petAction) -{ - // No pet specified, block action - if (petAction.PetGUID.IsEmpty()) - return; - - // Make sure that we either have a controlled pet or charmed unit - if (_player->GetMinionGUID() != petAction.PetGUID && _player->GetCharmedGUID() != petAction.PetGUID) - return; - - // Extract spell Id and action type from packet - uint32 actionValue = UNIT_ACTION_BUTTON_ACTION(petAction.Action); - uint32 actionType = UNIT_ACTION_BUTTON_TYPE(petAction.Action); //delete = 0x07 CastSpell = C1 - - float x, y, z; - petAction.ActionPosition.Pos.GetPosition(x, y, z); - - // Check if the player is currently controlling the pet - if (!_player->GetMinionGUID().IsEmpty()) - { - // We might attempt to control multiple pets at once (e.g. Force of Nature treants or Feral Spirit wolves) - std::vector petSummons = _player->GetSummonsByControlType(SummonPropertiesControl::Pet); - - // One final check to make sure that we actually are controlling a pet with the provided GUID - bool hasPet = std::ranges::any_of(petSummons, [&](SummonInfo const* summonInfo) - { - return summonInfo->GetSummonedCreature()->GetGUID() == petAction.PetGUID; - }); - - if (hasPet) - { - for (SummonInfo* summonInfo : petSummons) - HandlePetActionHelper(_player, summonInfo->GetSummonedCreature(), petAction.PetGUID, actionType, actionValue, petAction.TargetGUID, x, y, z); - - return; - } - } - - // Check if the player is currently charming a unit instead - if (!_player->GetCharmedGUID().IsEmpty()) - { - if (Unit* charmedUnit = ObjectAccessor::GetUnit(*_player, petAction.PetGUID)) - HandlePetActionHelper(_player, charmedUnit, petAction.PetGUID, actionType, actionValue, petAction.TargetGUID, x, y, z); - } -} - -void WorldSession::HandlePetStopAttack(WorldPacket &recvData) -{ - ObjectGuid guid; - recvData >> guid; - - TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_STOP_ATTACK for %s", guid.ToString().c_str()); - - Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); - - if (!pet) - { - TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s does not exist", guid.ToString().c_str()); - return; - } - - if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharmed()) - { - TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s isn't a pet or charmed creature of player %s", - guid.ToString().c_str(), GetPlayer()->GetName().c_str()); - return; - } - - if (!pet->IsAlive()) - return; - - pet->AttackStop(); -} - void WorldSession::HandlePetNameQuery(WorldPacket& recvData) { TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_NAME_QUERY"); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index fae51a2375..f92da7b84f 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -15,11 +15,12 @@ * with this program. If not, see . */ +#include "WorldSession.h" #include "Archaeology.h" #include "Common.h" #include "Config.h" -#include "DBCStores.h" #include "DatabaseEnv.h" +#include "DBCStores.h" #include "GameClient.h" #include "GameObject.h" #include "GameObjectAI.h" @@ -38,12 +39,10 @@ #include "SpellCastRequest.h" #include "SpellMgr.h" #include "SpellPackets.h" -#include "SummonInfo.h" #include "Totem.h" #include "TotemPackets.h" #include "World.h" #include "WorldPacket.h" -#include "WorldSession.h" void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlags, SpellCastTargets& targets) { @@ -424,15 +423,15 @@ void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& pac if (_player->IsCharming()) return; - SummonPropertiesSlot slot = static_cast(packet.Slot + 1); - if (slot < SummonPropertiesSlot::Totem1 || slot > SummonPropertiesSlot::Totem4) + if (packet.Slot +1 >= MAX_TOTEM_SLOT) return; - SummonInfo* summonInfo = _player->GetSummonInSlot(slot); - if (!summonInfo || summonInfo->GetSummonedCreature()->GetGUID() != packet.TotemGUID) + if (!_player->m_SummonSlot[packet.Slot + 1]) return; - summonInfo->GetSummonedCreature()->DespawnOrUnsummon(); + Creature* totem = ObjectAccessor::GetCreature(*GetPlayer(), _player->m_SummonSlot[packet.Slot + 1]); + if (totem && totem->IsTotem() && totem->GetGUID() == packet.TotemGUID) + totem->ToTotem()->UnSummon(); } void WorldSession::HandleSelfResOpcode(WorldPacket& /*recvData*/) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 7e0b73e7a8..0edaf0cd33 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -144,7 +144,7 @@ inline bool CompareRespawnInfo::operator()(RespawnInfo const* a, RespawnInfo con struct TC_GAME_API SummonCreatureExtraArgs { public: - SummonCreatureExtraArgs() = default; + SummonCreatureExtraArgs() { } SummonCreatureExtraArgs& SetSummonDuration(uint32 duration) { SummonDuration = duration; return *this; } diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index 64302c16a8..7ca8596e6a 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -21,7 +21,6 @@ #include "MoveSpline.h" #include "MoveSplineInit.h" #include "PathGenerator.h" -#include "SummonInfo.h" #include "Vehicle.h" template @@ -82,21 +81,6 @@ 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); diff --git a/src/server/game/Server/Packets/PetPackets.cpp b/src/server/game/Server/Packets/PetPackets.cpp index 2ad3e03698..3469659fcb 100644 --- a/src/server/game/Server/Packets/PetPackets.cpp +++ b/src/server/game/Server/Packets/PetPackets.cpp @@ -16,9 +16,7 @@ */ #include "PetPackets.h" - #include "PetDefines.h" -#include "Spell.h" WorldPacket const* WorldPackets::Pet::PetDismissSound::Write() { @@ -82,45 +80,3 @@ WorldPacket const* WorldPackets::Pet::PetAdded::Write() return &_worldPacket; } - -WorldPacket const* WorldPackets::Pet::PetSpells::Write() -{ - _worldPacket << PetGUID; - if (PetGUID.IsEmpty()) - return &_worldPacket; - - _worldPacket << uint16(_CreatureFamily); - _worldPacket << uint32(TimeLimit); - _worldPacket << uint8(ReactState); - _worldPacket << uint8(CommandState); - _worldPacket << uint16(Flag); - - _worldPacket.append(ActionButtons.data(), ActionButtons.size()); - - _worldPacket << uint8(Actions.size()); - _worldPacket.append(Actions.data(), Actions.size()); - - _worldPacket << uint8(Cooldowns.size()); - for (PetSpellCooldown const& cooldown : Cooldowns) - { - _worldPacket << int32(cooldown.SpellID); - _worldPacket << uint16(cooldown.Category); - _worldPacket << int32(cooldown.Duration); - _worldPacket << int32(cooldown.CategoryDuration); - } - - return &_worldPacket; -} - -void WorldPackets::Pet::DismissCritter::Read() -{ - _worldPacket >> CritterGUID; -} - -void WorldPackets::Pet::PetAction::Read() -{ - _worldPacket >> PetGUID; - _worldPacket >> Action; - _worldPacket >> TargetGUID; - _worldPacket >> ActionPosition; -} diff --git a/src/server/game/Server/Packets/PetPackets.h b/src/server/game/Server/Packets/PetPackets.h index 0cb066b2e1..bd9f9989ef 100644 --- a/src/server/game/Server/Packets/PetPackets.h +++ b/src/server/game/Server/Packets/PetPackets.h @@ -94,57 +94,6 @@ namespace WorldPackets uint32 PetNumber = 0; uint8 Flags = 0; }; - - struct PetSpellCooldown - { - int32 SpellID = 0; - int32 Duration = 0; - int32 CategoryDuration = 0; - uint16 Category = 0; - }; - - class PetSpells final : public ServerPacket - { - public: - PetSpells() : ServerPacket(SMSG_PET_SPELLS, 100) { } - - WorldPacket const* Write() override; - - ObjectGuid PetGUID; - uint16 _CreatureFamily = 0; ///< @see enum CreatureFamily - uint32 TimeLimit = 0; - uint8 ReactState = 0; - uint8 CommandState = 0; - uint16 Flag = 0; - - std::array ActionButtons = { }; - - std::vector Actions; - std::vector Cooldowns; - }; - - class DismissCritter final : public ClientPacket - { - public: - DismissCritter(WorldPacket&& packet) : ClientPacket(CMSG_DISMISS_CRITTER, std::move(packet)) { } - - void Read() override; - - ObjectGuid CritterGUID; - }; - - class PetAction final : public ClientPacket - { - public: - PetAction(WorldPacket&& packet) : ClientPacket(CMSG_PET_ACTION, std::move(packet)) { } - - void Read() override; - - ObjectGuid PetGUID; - ObjectGuid TargetGUID; - TaggedPosition ActionPosition; - uint32 Action = 0; - }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 7dd491c37d..88cd2c82e6 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -497,7 +497,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_PETITION_SHOW_SIGNATURES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionShowSignOpcode ); DEFINE_HANDLER(CMSG_PETITION_SIGN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionSignOpcode ); DEFINE_HANDLER(CMSG_PET_ABANDON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetAbandon ); - DEFINE_HANDLER(CMSG_PET_ACTION, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandlePetAction ); + DEFINE_HANDLER(CMSG_PET_ACTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetAction ); DEFINE_HANDLER(CMSG_PET_CANCEL_AURA, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePetCancelAuraOpcode ); DEFINE_HANDLER(CMSG_PET_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePetCastSpellOpcode ); DEFINE_HANDLER(CMSG_PET_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePetLearnTalent ); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 2d5150aff7..60bff9c95f 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -193,12 +193,6 @@ namespace WorldPackets class TrainerBuySpell; } - namespace Pet - { - class DismissCritter; - class PetAction; - } - namespace Party { class SetEveryoneIsAssistant; @@ -1065,8 +1059,9 @@ class TC_GAME_API WorldSession void HandleTutorialReset(WorldPacket& recvData); //Pet - void HandlePetAction(WorldPackets::Pet::PetAction& packet); + void HandlePetAction(WorldPacket& recvData); void HandlePetStopAttack(WorldPacket& recvData); + void HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, float x, float y, float z); void HandlePetNameQuery(WorldPacket& recvData); void HandlePetSetAction(WorldPacket& recvData); void HandlePetAbandon(WorldPacket& recvData); @@ -1080,7 +1075,7 @@ class TC_GAME_API WorldSession void HandleSetActionBarToggles(WorldPacket& recvData); void HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& packet); - void HandleDismissCritter(WorldPackets::Pet::DismissCritter& packet); + void HandleDismissCritter(WorldPacket& recvData); //Battleground void HandleBattlemasterHelloOpcode(WorldPacket& recvData); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a50004bd9b..5276b9b5a5 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3710,9 +3710,10 @@ void Spell::_cast(bool skipCheck) return; } - if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST)) - if (Unit* unitCaster = m_caster->ToUnit()) - unitCaster->DismissPet(); + if (Unit* unitCaster = m_caster->ToUnit()) + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST)) + if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, unitCaster->GetPetGUID())) + pet->DespawnOrUnsummon(); PrepareTriggersExecutedOnHit(); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 9b14d194cd..d95854faef 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -15,22 +15,23 @@ * with this program. If not, see . */ +#include "Spell.h" #include "AccountMgr.h" #include "AreaTrigger.h" #include "Battleground.h" #include "CellImpl.h" #include "CharmInfo.h" -#include "CombatLogPackets.h" #include "Common.h" #include "Creature.h" #include "CreatureAI.h" +#include "CombatLogPackets.h" #include "DatabaseEnv.h" #include "DynamicObject.h" #include "Formulas.h" -#include "GameClient.h" #include "GameEventSender.h" #include "GameObject.h" #include "GameObjectAI.h" +#include "GameClient.h" #include "GossipDef.h" #include "GridNotifiers.h" #include "Group.h" @@ -59,12 +60,10 @@ #include "SharedDefines.h" #include "SkillExtraItems.h" #include "SocialMgr.h" -#include "Spell.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" #include "SpellHistory.h" #include "SpellMgr.h" -#include "SummonInfo.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -2011,7 +2010,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) SummonCreatureExtraArgs extraArgs; extraArgs.SummonProperties = properties; extraArgs.SummonDuration = duration; - extraArgs.Summoner = Object::ToUnit(caster); + extraArgs.Summoner = caster->IsUnit() ? caster->ToUnit() : nullptr; extraArgs.SummonSpellId = m_spellInfo->Id; extraArgs.PrivateObjectOwner = privateObjectOwner; @@ -2078,6 +2077,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) caster->CastSpell(summon, VEHICLE_SPELL_RIDE_HARDCODED, CastSpellExtraArgs(true).AddSpellBP0(extraArgs.SeatNumber)); else if (extraArgs.RideSpell) caster->CastSpell(summon, extraArgs.RideSpell, true); + } } } @@ -4755,8 +4755,6 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/) pet->SavePetToDB(PET_SAVE_CURRENT_STATE); } -static constexpr uint32 SPELL_SHAMAN_TOTEMIC_RECALL_ENERGIZE = 39104; - void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) @@ -4765,30 +4763,31 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) if (!unitCaster) return; - int32 manaToRefund = 0; - for (SummonPropertiesSlot slot : { SummonPropertiesSlot::Totem1, SummonPropertiesSlot::Totem2, - SummonPropertiesSlot::Totem3, SummonPropertiesSlot::Totem4}) + int32 mana = 0; + for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot) { - SummonInfo* summonInfo = unitCaster->GetSummonInSlot(slot); - if (!summonInfo) + if (!unitCaster->m_SummonSlot[slot]) continue; - uint32 summonSpellId = summonInfo->GetSummonedCreature()->GetUInt32Value(UNIT_CREATED_BY_SPELL); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summonSpellId); - if (!spellInfo->IsShamanTotemSpell()) - continue; - - manaToRefund += spellInfo->CalcPowerCost(unitCaster, spellInfo->GetSchoolMask()); - - summonInfo->GetSummonedCreature()->DespawnOrUnsummon(); + Creature* totem = m_caster->GetMap()->GetCreature(unitCaster->m_SummonSlot[slot]); + if (totem && totem->IsTotem()) + { + uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id); + if (spellInfo) + { + mana += spellInfo->ManaCost; + mana += int32(CalculatePct(unitCaster->GetCreateMana(), spellInfo->ManaCostPercentage)); + } + totem->ToTotem()->UnSummon(); + } } - - ApplyPct(manaToRefund, damage); - if (manaToRefund) + ApplyPct(mana, damage); + if (mana) { CastSpellExtraArgs args(TRIGGERED_FULL_MASK); - args.AddSpellMod(SPELLVALUE_BASE_POINT0, manaToRefund); - unitCaster->CastSpell(unitCaster, SPELL_SHAMAN_TOTEMIC_RECALL_ENERGIZE, args); + args.AddSpellMod(SPELLVALUE_BASE_POINT0, mana); + unitCaster->CastSpell(unitCaster, 39104, args); } } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 77ae986c93..20b1488949 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -3416,25 +3416,6 @@ float SpellInfo::CalculateScaledCoefficient(Unit const* caster, float coefficien return coefficient *= GetSpellScalingMultiplier(caster->getLevel()); } -bool SpellInfo::IsShamanTotemSpell() const -{ - for (uint8 i = 0; i < MAX_SPELL_TOTEMS; ++i) - { - TotemCategoryTypes type = static_cast(TotemCategory[i]); - switch (type) - { - case TotemCategoryTypes::EarthTotem: - case TotemCategoryTypes::AirTotem: - case TotemCategoryTypes::FireTotem: - case TotemCategoryTypes::WaterTotem: - return true; - default: - return false; - } - } - - return false; -} float SpellInfo::GetMinRange(bool positive) const { diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 2cca499418..65a660678c 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -595,7 +595,6 @@ class TC_GAME_API SpellInfo float CalculateScaledCoefficient(Unit const* caster, float coefficient) const; - bool IsShamanTotemSpell() const; private: // loading helpers void _InitializeExplicitTargetMask(); diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp index 959819a36a..11f28b4400 100644 --- a/src/server/scripts/Commands/cs_pet.cpp +++ b/src/server/scripts/Commands/cs_pet.cpp @@ -103,7 +103,7 @@ public: creatureTarget->DespawnOrUnsummon(); creatureTarget->SetHealth(0); // just for nice GM-mode view - pet->SetCreatorGUID(player->GetGUID()); + pet->SetGuidValue(UNIT_FIELD_CREATEDBY, player->GetGUID()); pet->SetFaction(player->GetFaction()); if (!pet->InitStatsForLevel(creatureTarget->getLevel())) diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index e96001d3a2..9e14c942b8 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -2476,7 +2476,7 @@ public: if (Creature* injuredStormwindInfantry = caster->FindNearestCreature(NPC_INJURED_STORMWIND_INFANTRY, 5.0f, true)) { injuredStormwindInfantry->SetCreatorGUID(caster->GetGUID()); - injuredStormwindInfantry->CastSpell(injuredStormwindInfantry, SPELL_RENEWED_LIFE); + injuredStormwindInfantry->CastSpell(injuredStormwindInfantry, SPELL_RENEWED_LIFE, true); } }