Core/Creatures: temporarily revert all summon changes in order to keep the master branch functional until the implementation is done

This commit is contained in:
Ovahlord committed 2026-02-28 11:06:35 +01:00
1 parent 3f65afb962
commit 266d1d7717
32 files changed
+598 -1384

No files matched your search

-82
View File
@@ -18,91 +18,9 @@
#ifndef TRINITY_ADVSTD_H
#define TRINITY_ADVSTD_H
#include <version>
#ifdef __cpp_lib_bit_cast
#include <bit>
#else
#include <cstring> // for memcpy
#endif
#include <compare>
// 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 <typename To, typename From,
std::enable_if_t<std::conjunction_v<
std::bool_constant<sizeof(To) == sizeof(From)>,
std::is_trivially_copyable<To>,
std::is_trivially_copyable<From>>, 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 <utility>
namespace advstd
{
using std::forward_like;
}
#else
namespace advstd
{
template <class T, class U>
[[nodiscard]] constexpr decltype(auto) forward_like(U&& value) noexcept
{
using ValueU = std::remove_reference_t<U>;
using ValueConstU = std::conditional_t<std::is_const_v<std::remove_reference_t<T>>, ValueU const, ValueU>;
return static_cast<std::conditional_t<std::is_rvalue_reference_v<T&&>, ValueConstU&&, ValueConstU&>>(value);
}
}
#endif
// std::ranges::contains
#include <algorithm>
#ifndef __cpp_lib_ranges_contains
#include <functional> // for std::ranges::equal_to, std::identity
#include <iterator> // for std::input_iterator, std::sentinel_for, std::projected
#endif
namespace advstd::ranges
{
#ifndef __cpp_lib_ranges_contains
struct Contains
{
template<std::input_iterator I, std::sentinel_for<I> S, class T, class Proj = std::identity>
requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<I, Proj>, 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<std::ranges::input_range R, class T, class Proj = std::identity>
requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<std::ranges::iterator_t<R>, 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
-9
View File
@@ -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
+4
View File
@@ -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)
+9 -67
View File
@@ -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<SummonInfo>(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())
@@ -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<Creature>, 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<Creature>, public Ma
Optional<uint32> _defaultMountDisplayIdOverride;
float _noNpcDamageBelowPctHealth;
// The Summon API which controls the behavior of summons
std::unique_ptr<SummonInfo> _summonInfo;
};
class TC_GAME_API AssistDelayEvent : public BasicEvent
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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<SummonPropertiesSlot>(summonProperties->Slot);
_control = static_cast<SummonPropertiesControl>(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<Milliseconds> SummonInfo::GetRemainingDuration() const
{
return _remainingDuration;
}
Optional<uint64> SummonInfo::GetMaxHealth() const
{
return _maxHealth;
}
Optional<uint32> SummonInfo::GetFactionId() const
{
return _factionId;
}
Optional<uint8> 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;
}
}
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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<Milliseconds> GetRemainingDuration() const;
// Returns the health amount that will override the default max health calculation. Nullopt when no amount is provided.
Optional<uint64> 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<uint32> GetFactionId() const;
// Returns the level of the creature that will override the default level calculation. Nullopt when the creature uses its default values.
Optional<uint8> 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<Milliseconds> _remainingDuration; // Implemented in SummonInfo::UpdateRemainingDuration
Optional<uint64> _maxHealth; // Implemented in Creature::UpdateLevelDependantStats
Optional<uint32> _factionId; // Implemented in Creature::UpdateEntry
Optional<uint8> _creatureLevel; // Implemented in Creature::SelectLevel
EnumFlag<SummonPropertiesFlags> _flags;
SummonPropertiesControl _control;
SummonPropertiesSlot _summonSlot;
bool _hasBeenSummonedByPlayer;
bool _isClassPet; // Placeholder for class pet interface
};
#endif // _SummonInfo_h__
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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<uint64> MaxHealth;
Optional<Milliseconds> Duration;
Optional<uint32> SummonPropertiesId;
Optional<uint8> CreatureLevel;
};
#endif // _SummonInfoArgs_h__
@@ -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();
}
@@ -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);
+80 -24
View File
@@ -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<HighGuid::Unit>(), 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);
+1 -18
View File
@@ -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<Pet>(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<Pet>(GetGUID());
+8 -55
View File
@@ -16,11 +16,6 @@
*/
#include "Player.h"
#include <G3D/g3dmath.h>
#include <boost/dynamic_bitset.hpp>
#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 <boost/dynamic_bitset.hpp>
#include <G3D/g3dmath.h>
// 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();
-1
View File
@@ -1533,7 +1533,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
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
+20 -1
View File
@@ -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
@@ -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;
+5 -170
View File
@@ -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<SummonInfo*> unslottedSummons = _unslottedSummons;
for (SummonInfo* summon : unslottedSummons)
if (summon->DespawnsOnSummonerLogout())
summon->GetSummonedCreature()->DespawnOrUnsummon();
std::vector<SummonInfo*> slottedSummons = _slottedSummons;
for (SummonInfo* summon : slottedSummons)
if (summon && summon->DespawnsOnSummonerLogout())
summon->GetSummonedCreature()->DespawnOrUnsummon();
}
void Unit::DespawnSummonsOnSummonerDeath()
{
std::vector<SummonInfo*> unslottedSummons = _unslottedSummons;
for (SummonInfo* summon : unslottedSummons)
if (summon->DespawnsOnSummonerDeath())
summon->GetSummonedCreature()->DespawnOrUnsummon();
std::vector<SummonInfo*> 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<SummonInfo*> Unit::GetSummonsByCreatureId(uint32 creatureId)
{
if (creatureId == 0)
return {};
std::vector<SummonInfo*> 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<SummonInfo*> Unit::GetSummonsBySpellId(uint32 spellId)
{
if (spellId == 0)
return {};
std::vector<SummonInfo*> 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<SummonInfo*> Unit::GetSummonsByControlType(SummonPropertiesControl control)
{
std::vector<SummonInfo*> 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;
}
+3 -38
View File
@@ -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<ObjectGuid, MAX_SUMMON_SLOT> m_SummonSlot;
std::array<ObjectGuid, MAX_GAMEOBJECT_SLOT> 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<SummonInfo*> GetSummonsByCreatureId(uint32 creatureId);
// Returns a vector with all currently active summons which have been created by the specified spell Id
std::vector<SummonInfo*> GetSummonsBySpellId(uint32 spellId);
// Returns a vector with all currently active summons which are being controlled in a specific way
std::vector<SummonInfo*> 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<MovementChangeType, PlayerMovementPendingChange> m_pendingMovementChanges;
/* Player Movement fields END*/
// SummonInfo slot handling
std::vector<SummonInfo*> _unslottedSummons;
std::vector<SummonInfo*> _slottedSummons;
};
namespace Trinity
+160 -138
View File
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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<Unit*> 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<Unit*>::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<ReactStates>(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<PetAI*>(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<SummonInfo*> 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");
+7 -8
View File
@@ -15,11 +15,12 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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<SummonPropertiesSlot>(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*/)
+1 -1
View File
@@ -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; }
@@ -21,7 +21,6 @@
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "PathGenerator.h"
#include "SummonInfo.h"
#include "Vehicle.h"
template<class T>
@@ -82,21 +81,6 @@ void HomeMovementGenerator<Creature>::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);
@@ -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;
}
@@ -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<int, 10> ActionButtons = { };
std::vector<uint32> Actions;
std::vector<PetSpellCooldown> 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<Position::XYZ> ActionPosition;
uint32 Action = 0;
};
}
}
+1 -1
View File
@@ -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 );
+3 -8
View File
@@ -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);
+4 -3
View File
@@ -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();
+24 -25
View File
@@ -15,22 +15,23 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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);
}
}
-19
View File
@@ -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<TotemCategoryTypes>(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
{
-1
View File
@@ -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();
+1 -1
View File
@@ -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()))
+1 -1
View File
@@ -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);
}
}