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:
1 parent
3f65afb962
commit
266d1d7717
32 files changed
+598
-1384
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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*/)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user