aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities
diff options
context:
space:
mode:
authorariel- <ariel-@users.noreply.github.com>2018-02-10 16:43:01 -0300
committerShauren <shauren.trinity@gmail.com>2021-08-28 15:59:11 +0200
commit962f6d7988b9003e550f6745be7cff812e9d8efa (patch)
treeee6ab5872b947afb00f4ca99e87c7dddea35bdb3 /src/server/game/Entities
parent65dca120d34febdaa84a63e17f638ab0fa59b3df (diff)
Core/Spells: rework part 5: GameObject casting
Closes #21330 Closes #18885 Ref #18752 (cherry picked from commit 45c5e1b9d63796d168339a44f63418f220cf2403)
Diffstat (limited to 'src/server/game/Entities')
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.cpp8
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.h4
-rw-r--r--src/server/game/Entities/Conversation/Conversation.h2
-rw-r--r--src/server/game/Entities/Corpse/Corpse.h4
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp4
-rw-r--r--src/server/game/Entities/Creature/Creature.h4
-rw-r--r--src/server/game/Entities/DynamicObject/DynamicObject.cpp6
-rw-r--r--src/server/game/Entities/DynamicObject/DynamicObject.h2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp101
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h13
-rw-r--r--src/server/game/Entities/Object/Object.cpp990
-rw-r--r--src/server/game/Entities/Object/Object.h78
-rw-r--r--src/server/game/Entities/Player/Player.cpp53
-rw-r--r--src/server/game/Entities/Player/Player.h11
-rw-r--r--src/server/game/Entities/Totem/Totem.cpp2
-rw-r--r--src/server/game/Entities/Totem/Totem.h2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp1120
-rw-r--r--src/server/game/Entities/Unit/Unit.h95
18 files changed, 1272 insertions, 1227 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
index 773226e43fe..046d194efb0 100644
--- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
+++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
@@ -500,6 +500,14 @@ Unit* AreaTrigger::GetTarget() const
return ObjectAccessor::GetUnit(*this, _targetGuid);
}
+uint32 AreaTrigger::GetFaction() const
+{
+ if (Unit const* caster = GetCaster())
+ return caster->GetFaction();
+
+ return 0;
+}
+
void AreaTrigger::UpdatePolygonOrientation()
{
float newOrientation = GetOrientation();
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h
index f0f41a44f09..e00f8842ea6 100644
--- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h
+++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h
@@ -92,10 +92,13 @@ class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigge
AreaTriggerTemplate const* GetTemplate() const;
uint32 GetScriptId() const;
+ ObjectGuid GetOwnerGUID() const override { return GetCasterGuid(); }
ObjectGuid const& GetCasterGuid() const { return m_areaTriggerData->Caster; }
Unit* GetCaster() const;
Unit* GetTarget() const;
+ uint32 GetFaction() const override;
+
Position const& GetRollPitchYaw() const { return _rollPitchYaw; }
Position const& GetTargetRollPitchYaw() const { return _targetRollPitchYaw; }
void InitSplineOffsets(std::vector<Position> const& offsets, uint32 timeToTarget);
@@ -112,7 +115,6 @@ class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigge
UF::UpdateField<UF::AreaTriggerData, 0, TYPEID_AREATRIGGER> m_areaTriggerData;
-
protected:
void _UpdateDuration(int32 newDuration);
float GetProgress() const;
diff --git a/src/server/game/Entities/Conversation/Conversation.h b/src/server/game/Entities/Conversation/Conversation.h
index 821bfcaa30f..aa180000016 100644
--- a/src/server/game/Entities/Conversation/Conversation.h
+++ b/src/server/game/Entities/Conversation/Conversation.h
@@ -56,6 +56,8 @@ class TC_GAME_API Conversation : public WorldObject, public GridObject<Conversat
void AddParticipant(ObjectGuid const& participantGuid);
ObjectGuid const& GetCreatorGuid() const { return _creatorGuid; }
+ ObjectGuid GetOwnerGUID() const override { return GetCreatorGuid(); }
+ uint32 GetFaction() const override { return 0; }
float GetStationaryX() const override { return _stationaryPosition.GetPositionX(); }
float GetStationaryY() const override { return _stationaryPosition.GetPositionY(); }
diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h
index 2135fb3358d..22636982bcf 100644
--- a/src/server/game/Entities/Corpse/Corpse.h
+++ b/src/server/game/Entities/Corpse/Corpse.h
@@ -77,7 +77,7 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse>
void AddCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::DynamicFlags), dynamicFlags); }
void RemoveCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::DynamicFlags), dynamicFlags); }
void SetCorpseDynamicFlags(CorpseDynFlags dynamicFlags) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::DynamicFlags), dynamicFlags); }
- ObjectGuid GetOwnerGUID() const { return m_corpseData->Owner; }
+ ObjectGuid GetOwnerGUID() const override { return m_corpseData->Owner; }
void SetOwnerGUID(ObjectGuid owner) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Owner), owner); }
void SetPartyGUID(ObjectGuid partyGuid) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::PartyGUID), partyGuid); }
void SetGuildGUID(ObjectGuid guildGuid) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::GuildGUID), guildGuid); }
@@ -87,6 +87,8 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse>
void SetSex(uint8 sex) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Sex), sex); }
void SetFlags(uint32 flags) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Flags), flags); }
void SetFactionTemplate(int32 factionTemplate) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::FactionTemplate), factionTemplate); }
+ uint32 GetFaction() const override { return m_corpseData->FactionTemplate; }
+ void SetFaction(uint32 faction) override { SetFactionTemplate(faction); }
void SetItem(uint32 slot, uint32 item) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Items, slot), item); }
template<typename Iter>
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 1e5a42060cb..f59f4e48b6a 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -2355,7 +2355,7 @@ void Creature::LoadTemplateImmunities()
}
}
-bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const
+bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const
{
if (!spellInfo)
return false;
@@ -2379,7 +2379,7 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const
return Unit::IsImmunedToSpell(spellInfo, caster);
}
-bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const
+bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const
{
SpellEffectInfo const* effect = spellInfo->GetEffect(index);
if (!effect)
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 138a061eec3..1463e1b6975 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -127,8 +127,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool CanResetTalents(Player* player) const;
bool CanCreatureAttack(Unit const* victim, bool force = true) const;
void LoadTemplateImmunities();
- bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const override;
- bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override;
+ bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const override;
+ bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override;
bool isElite() const;
bool isWorldBoss() const;
diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp
index 550ca700d90..4a9cceedb0a 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp
@@ -226,6 +226,12 @@ void DynamicObject::RemoveCasterViewpoint()
}
}
+uint32 DynamicObject::GetFaction() const
+{
+ ASSERT(_caster);
+ return _caster->GetFaction();
+}
+
void DynamicObject::BindToCaster()
{
ASSERT(!_caster);
diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h
index f2de544ed53..fd3d6a8aa0d 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.h
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.h
@@ -61,11 +61,13 @@ class TC_GAME_API DynamicObject : public WorldObject, public GridObject<DynamicO
void SetCasterViewpoint();
void RemoveCasterViewpoint();
Unit* GetCaster() const { return _caster; }
+ uint32 GetFaction() const override;
void BindToCaster();
void UnbindFromCaster();
uint32 GetSpellId() const { return m_dynamicObjectData->SpellID; }
SpellInfo const* GetSpellInfo() const;
ObjectGuid GetCasterGUID() const { return m_dynamicObjectData->Caster; }
+ ObjectGuid GetOwnerGUID() const override { return GetCasterGUID(); }
float GetRadius() const { return m_dynamicObjectData->Radius; }
UF::UpdateField<UF::DynamicObjectData, 0, TYPEID_DYNAMICOBJECT> m_dynamicObjectData;
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 821dc454323..6a13100d63d 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -517,6 +517,8 @@ GameObject* GameObject::CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map*
void GameObject::Update(uint32 diff)
{
+ m_Events.Update(diff);
+
if (AI())
AI()->UpdateAI(diff);
else if (!AIM_Initialize())
@@ -825,8 +827,10 @@ void GameObject::Update(uint32 diff)
else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID))
{
// Some traps do not have a spell but should be triggered
+ CastSpellExtraArgs args;
+ args.SetOriginalCaster(GetOwnerGUID());
if (goInfo->trap.spell)
- CastSpell(target, goInfo->trap.spell);
+ CastSpell(target, goInfo->trap.spell, args);
// Template value or 4 seconds
m_cooldownTime = GameTime::GetGameTimeMS() + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)) * IN_MILLISECONDS;
@@ -1307,11 +1311,6 @@ bool GameObject::IsDestructibleBuilding() const
return gInfo->type == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING;
}
-Unit* GameObject::GetOwner() const
-{
- return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
-}
-
void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb)
{
if (m_goData && (forceDelay || m_respawnTime > GameTime::GetGameTime()) && m_spawnedByDefault)
@@ -2211,68 +2210,6 @@ void GameObject::Use(Unit* user)
CastSpell(user, spellId);
}
-void GameObject::CastSpell(Unit* target, uint32 spellId, bool triggered /* = true*/)
-{
- CastSpell(target, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
-}
-
-void GameObject::CastSpell(Unit* target, uint32 spellId, TriggerCastFlags triggered)
-{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, GetMap()->GetDifficultyID());
- if (!spellInfo)
- return;
-
- bool self = false;
- for (SpellEffectInfo const* effect : spellInfo->GetEffects())
- {
- if (effect && effect->TargetA.GetTarget() == TARGET_UNIT_CASTER)
- {
- self = true;
- break;
- }
- }
-
- if (self)
- {
- if (target)
- target->CastSpell(target, spellInfo->Id, triggered);
- return;
- }
-
- //summon world trigger
- Creature* trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, spellInfo->CalcCastTime() + 100);
- if (!trigger)
- return;
-
- // remove immunity flags, to allow spell to target anything
- trigger->SetImmuneToAll(false);
- PhasingHandler::InheritPhaseShift(trigger, this);
-
- CastSpellExtraArgs args;
- args.TriggerFlags = triggered;
- if (Unit* owner = GetOwner())
- {
- trigger->SetFaction(owner->GetFaction());
- if (owner->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- trigger->AddUnitFlag(UNIT_FLAG_PVP_ATTACKABLE);
- // copy pvp state flags from owner
- trigger->SetPvpFlags(owner->GetPvpFlags());
- // needed for GO casts for proper target validation checks
- trigger->SetOwnerGUID(owner->GetGUID());
-
- args.OriginalCaster = owner->GetGUID();
- trigger->CastSpell(target ? target : trigger, spellInfo->Id, args);
- }
- else
- {
- trigger->SetFaction(spellInfo->IsPositive() ? FACTION_FRIENDLY : FACTION_MONSTER);
- // Set owner guid for target if no owner available - needed by trigger auras
- // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell())
- args.OriginalCaster = target ? target->GetGUID() : ObjectGuid::Empty;
- trigger->CastSpell(target ? target : trigger, spellInfo->Id, args);
- }
-}
-
void GameObject::SendCustomAnim(uint32 anim)
{
WorldPackets::GameObject::GameObjectCustomAnim customAnim;
@@ -2380,7 +2317,7 @@ void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot)
SetWorldRotation(quat.x, quat.y, quat.z, quat.w);
}
-void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/)
+void GameObject::ModifyHealth(int32 change, WorldObject* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/)
{
if (!m_goValue.Building.MaxHealth || !change)
return;
@@ -2399,10 +2336,8 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/
// Set the health bar, value = 255 * healthPct;
SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth);
- Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr;
-
// dealing damage, send packet
- if (player)
+ if (Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr)
{
WorldPackets::GameObject::DestructibleBuildingDamage packet;
packet.Caster = attackerOrHealer->GetGUID(); // todo: this can be a GameObject
@@ -2425,11 +2360,10 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/
if (newState == GetDestructibleState())
return;
- /// @todo: pass attackerOrHealer instead of player
- SetDestructibleState(newState, player, false);
+ SetDestructibleState(newState, attackerOrHealer, false);
}
-void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= nullptr*/, bool setHealth /*= false*/)
+void GameObject::SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer /*= nullptr*/, bool setHealth /*= false*/)
{
// the user calling this must know he is already operating on destructible gameobject
ASSERT(GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING);
@@ -2448,8 +2382,8 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player*
break;
case GO_DESTRUCTIBLE_DAMAGED:
{
- EventInform(m_goInfo->destructibleBuilding.DamagedEvent, eventInvoker);
- AI()->Damaged(eventInvoker, m_goInfo->destructibleBuilding.DamagedEvent);
+ EventInform(m_goInfo->destructibleBuilding.DamagedEvent, attackerOrHealer);
+ AI()->Damaged(attackerOrHealer, m_goInfo->destructibleBuilding.DamagedEvent);
RemoveFlag(GO_FLAG_DESTROYED);
AddFlag(GO_FLAG_DAMAGED);
@@ -2473,11 +2407,12 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player*
}
case GO_DESTRUCTIBLE_DESTROYED:
{
- EventInform(m_goInfo->destructibleBuilding.DestroyedEvent, eventInvoker);
- AI()->Destroyed(eventInvoker, m_goInfo->destructibleBuilding.DestroyedEvent);
- if (eventInvoker)
- if (Battleground* bg = eventInvoker->GetBattleground())
- bg->DestroyGate(eventInvoker, this);
+ EventInform(m_goInfo->destructibleBuilding.DestroyedEvent, attackerOrHealer);
+ AI()->Destroyed(attackerOrHealer, m_goInfo->destructibleBuilding.DestroyedEvent);
+
+ if (attackerOrHealer && attackerOrHealer->GetTypeId() == TYPEID_PLAYER)
+ if (Battleground* bg = attackerOrHealer->ToPlayer()->GetBattleground())
+ bg->DestroyGate(attackerOrHealer->ToPlayer(), this);
RemoveFlag(GO_FLAG_DAMAGED);
AddFlag(GO_FLAG_DESTROYED);
@@ -2498,7 +2433,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player*
}
case GO_DESTRUCTIBLE_REBUILDING:
{
- EventInform(m_goInfo->destructibleBuilding.RebuildingEvent, eventInvoker);
+ EventInform(m_goInfo->destructibleBuilding.RebuildingEvent, attackerOrHealer);
RemoveFlag(GameObjectFlags(GO_FLAG_DAMAGED | GO_FLAG_DESTROYED));
uint32 modelId = m_goInfo->displayId;
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index bb1efe37da0..d16120e0edc 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -140,8 +140,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
m_spawnedByDefault = false; // all object with owner is despawned after delay
SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::CreatedBy), owner);
}
- ObjectGuid GetOwnerGUID() const { return m_gameObjectData->CreatedBy; }
- Unit* GetOwner() const;
+ ObjectGuid GetOwnerGUID() const override { return m_gameObjectData->CreatedBy; }
void SetSpellId(uint32 id)
{
@@ -249,14 +248,12 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
GameObject* LookupFishingHoleAround(float range);
- void CastSpell(Unit* target, uint32 spell, bool triggered = true);
- void CastSpell(Unit* target, uint32 spell, TriggerCastFlags triggered);
void SendCustomAnim(uint32 anim);
bool IsInRange(float x, float y, float z, float radius) const;
- void ModifyHealth(int32 change, Unit* attackerOrHealer = nullptr, uint32 spellId = 0);
+ void ModifyHealth(int32 change, WorldObject* attackerOrHealer = nullptr, uint32 spellId = 0);
// sets GameObject type 33 destruction flags and optionally default health for that state
- void SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker = nullptr, bool setHealth = false);
+ void SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer = nullptr, bool setHealth = false);
GameObjectDestructibleState GetDestructibleState() const
{
if ((*m_gameObjectData->Flags & GO_FLAG_DESTROYED))
@@ -280,8 +277,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 GetDisplayId() const { return m_gameObjectData->DisplayID; }
uint8 GetNameSetId() const;
- uint32 GetFaction() const { return m_gameObjectData->FactionTemplate; }
- void SetFaction(uint32 faction) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::FactionTemplate), faction); }
+ uint32 GetFaction() const override { return m_gameObjectData->FactionTemplate; }
+ void SetFaction(uint32 faction) override { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::FactionTemplate), faction); }
GameObjectModel* m_model;
void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr) const;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 985d079826c..a2b4067291d 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -21,6 +21,7 @@
#include "BattlefieldMgr.h"
#include "CellImpl.h"
#include "CinematicMgr.h"
+#include "CombatLogPacketsCommon.h"
#include "Common.h"
#include "Creature.h"
#include "GameTime.h"
@@ -36,8 +37,9 @@
#include "OutdoorPvPMgr.h"
#include "PhasingHandler.h"
#include "Player.h"
-#include "SharedDefines.h"
+#include "ReputationMgr.h"
#include "SpellAuraEffects.h"
+#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Totem.h"
#include "Transport.h"
@@ -848,8 +850,8 @@ void MovementInfo::OutDebug()
TC_LOG_DEBUG("misc", "splineElevation: %f", splineElevation);
}
-WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0),
-m_name(""), m_isActive(false), m_isFarVisible(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr),
+WorldObject::WorldObject(bool isWorldObject) : Object(), WorldLocation(), LastUsedScriptID(0),
+m_movementInfo(), m_name(), m_isActive(false), m_isFarVisible(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr),
m_transport(nullptr), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_currMap(nullptr), m_InstanceId(0),
_dbPhase(0), m_notifyflags(0)
{
@@ -1658,6 +1660,36 @@ void WorldObject::SendMessageToSet(WorldPacket const* data, Player const* skippe
Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
}
+struct CombatLogSender
+{
+ WorldPackets::CombatLog::CombatLogServerPacket const* i_message;
+
+ explicit CombatLogSender(WorldPackets::CombatLog::CombatLogServerPacket* msg)
+ : i_message(msg)
+ {
+ msg->Write();
+ }
+
+ void operator()(Player const* player) const
+ {
+ if (player->IsAdvancedCombatLoggingEnabled())
+ player->SendDirectMessage(i_message->GetFullLogPacket());
+ else
+ player->SendDirectMessage(i_message->GetBasicLogPacket());
+ }
+};
+
+void WorldObject::SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const
+{
+ CombatLogSender combatLogSender(combatLog);
+
+ if (Player const* self = ToPlayer())
+ combatLogSender(self);
+
+ Trinity::MessageDistDeliverer<CombatLogSender> notifier(this, combatLogSender, GetVisibilityRange());
+ Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
+}
+
void WorldObject::SetMap(Map* map)
{
ASSERT(map);
@@ -1986,6 +2018,958 @@ Player* WorldObject::SelectNearestPlayer(float distance) const
return target;
}
+ObjectGuid WorldObject::GetCharmerOrOwnerOrOwnGUID() const
+{
+ ObjectGuid guid = GetCharmerOrOwnerGUID();
+ if (!guid.IsEmpty())
+ return guid;
+ return GetGUID();
+}
+
+Unit* WorldObject::GetOwner() const
+{
+ return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
+}
+
+Unit* WorldObject::GetCharmerOrOwner() const
+{
+ if (Unit const* unit = ToUnit())
+ return unit->GetCharmerOrOwner();
+ else if (GameObject const* go = ToGameObject())
+ return go->GetOwner();
+
+ return nullptr;
+}
+
+Unit* WorldObject::GetCharmerOrOwnerOrSelf() const
+{
+ if (Unit* u = GetCharmerOrOwner())
+ return u;
+
+ return const_cast<WorldObject*>(this)->ToUnit();
+}
+
+Player* WorldObject::GetCharmerOrOwnerPlayerOrPlayerItself() const
+{
+ ObjectGuid guid = GetCharmerOrOwnerGUID();
+ if (guid.IsPlayer())
+ return ObjectAccessor::GetPlayer(*this, guid);
+
+ return const_cast<WorldObject*>(this)->ToPlayer();
+}
+
+Player* WorldObject::GetAffectingPlayer() const
+{
+ if (!GetCharmerOrOwnerGUID())
+ return const_cast<WorldObject*>(this)->ToPlayer();
+
+ if (Unit* owner = GetCharmerOrOwner())
+ return owner->GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ return nullptr;
+}
+
+Player* WorldObject::GetSpellModOwner() const
+{
+ if (Player* player = const_cast<WorldObject*>(this)->ToPlayer())
+ return player;
+
+ if (GetTypeId() == TYPEID_UNIT)
+ {
+ Creature const* creature = ToCreature();
+ if (creature->IsPet() || creature->IsTotem())
+ {
+ if (Unit* owner = creature->GetOwner())
+ return owner->ToPlayer();
+ }
+ }
+ else if (GetTypeId() == TYPEID_GAMEOBJECT)
+ {
+ GameObject const* go = ToGameObject();
+ if (Unit* owner = go->GetOwner())
+ return owner->ToPlayer();
+ }
+
+ return nullptr;
+}
+
+// function uses real base points (typically value - 1)
+int32 WorldObject::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effIndex, int32 const* basePoints /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const
+{
+ SpellEffectInfo const* effect = spellProto->GetEffect(effIndex);
+ if (variance)
+ *variance = 0.0f;
+
+ return effect ? effect->CalcValue(this, basePoints, target, variance, castItemId, itemLevel) : 0;
+}
+
+float WorldObject::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
+{
+ if (!spellInfo->RangeEntry)
+ return 0.f;
+
+ if (spellInfo->RangeEntry->RangeMax[0] == spellInfo->RangeEntry->RangeMax[1])
+ return spellInfo->GetMaxRange();
+
+ if (!target)
+ return spellInfo->GetMaxRange(true);
+
+ return spellInfo->GetMaxRange(!IsHostileTo(target));
+}
+
+float WorldObject::GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
+{
+ if (!spellInfo->RangeEntry)
+ return 0.f;
+
+ if (spellInfo->RangeEntry->RangeMin[0] == spellInfo->RangeEntry->RangeMin[1])
+ return spellInfo->GetMinRange();
+
+ if (!target)
+ return spellInfo->GetMinRange(true);
+
+ return spellInfo->GetMinRange(!IsHostileTo(target));
+}
+
+float WorldObject::ApplyEffectModifiers(SpellInfo const* spellInfo, uint8 effIndex, float value) const
+{
+ if (Player* modOwner = GetSpellModOwner())
+ {
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::Points, value);
+ switch (effIndex)
+ {
+ case EFFECT_0:
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex0, value);
+ break;
+ case EFFECT_1:
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex1, value);
+ break;
+ case EFFECT_2:
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex2, value);
+ break;
+ case EFFECT_3:
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex3, value);
+ break;
+ case EFFECT_4:
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex4, value);
+ break;
+ }
+ }
+ return value;
+}
+
+int32 WorldObject::CalcSpellDuration(SpellInfo const* spellInfo) const
+{
+ uint8 comboPoints = 0;
+ if (Unit const* unit = ToUnit())
+ comboPoints = unit->GetPower(POWER_COMBO_POINTS);
+
+ int32 minduration = spellInfo->GetDuration();
+ int32 maxduration = spellInfo->GetMaxDuration();
+
+ int32 duration;
+ if (comboPoints && minduration != -1 && minduration != maxduration)
+ duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
+ else
+ duration = minduration;
+
+ return duration;
+}
+
+int32 WorldObject::ModSpellDuration(SpellInfo const* spellInfo, WorldObject const* target, int32 duration, bool positive, uint32 effectMask) const
+{
+ // don't mod permanent auras duration
+ if (duration < 0)
+ return duration;
+
+ // some auras are not affected by duration modifiers
+ if (spellInfo->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS))
+ return duration;
+
+ // cut duration only of negative effects
+ Unit const* unitTarget = target->ToUnit();
+ if (!unitTarget)
+ return duration;
+
+ if (!positive)
+ {
+ int32 mechanicMask = spellInfo->GetSpellMechanicMaskByEffectMask(effectMask);
+ auto mechanicCheck = [mechanicMask](AuraEffect const* aurEff) -> bool
+ {
+ if (mechanicMask & (1 << aurEff->GetMiscValue()))
+ return true;
+ return false;
+ };
+
+ // Find total mod value (negative bonus)
+ int32 durationMod_always = unitTarget->GetTotalAuraModifier(SPELL_AURA_MECHANIC_DURATION_MOD, mechanicCheck);
+ // Find max mod (negative bonus)
+ int32 durationMod_not_stack = unitTarget->GetMaxNegativeAuraModifier(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanicCheck);
+
+ // Select strongest negative mod
+ int32 durationMod = std::min(durationMod_always, durationMod_not_stack);
+ if (durationMod != 0)
+ AddPct(duration, durationMod);
+
+ // there are only negative mods currently
+ durationMod_always = unitTarget->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellInfo->Dispel);
+ durationMod_not_stack = unitTarget->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK, spellInfo->Dispel);
+
+ durationMod = std::min(durationMod_always, durationMod_not_stack);
+ if (durationMod != 0)
+ AddPct(duration, durationMod);
+ }
+ else
+ {
+ // else positive mods here, there are no currently
+ // when there will be, change GetTotalAuraModifierByMiscValue to GetMaxPositiveAuraModifierByMiscValue
+
+ // Mixology - duration boost
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ {
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_POTION && (
+ sSpellMgr->IsSpellMemberOfSpellGroup(spellInfo->Id, SPELL_GROUP_ELIXIR_BATTLE) ||
+ sSpellMgr->IsSpellMemberOfSpellGroup(spellInfo->Id, SPELL_GROUP_ELIXIR_GUARDIAN)))
+ {
+ SpellEffectInfo const* effect = spellInfo->GetEffect(EFFECT_0);
+ if (unitTarget->HasAura(53042) && effect && unitTarget->HasSpell(effect->TriggerSpell))
+ duration *= 2;
+ }
+ }
+ }
+
+ return std::max(duration, 0);
+}
+
+void WorldObject::ModSpellCastTime(SpellInfo const* spellInfo, int32& castTime, Spell* spell /*= nullptr*/) const
+{
+ if (!spellInfo || castTime < 0)
+ return;
+
+ // called from caster
+ if (Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, castTime, spell);
+
+ Unit const* unitCaster = ToUnit();
+ if (!unitCaster)
+ return;
+
+ if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) &&
+ ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT))
+ castTime = unitCaster->CanInstantCast() ? 0 : int32(float(castTime) * unitCaster->m_unitData->ModCastingSpeed);
+ else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG))
+ castTime = int32(float(castTime) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]);
+ else if (IsPartOfSkillLine(SKILL_COOKING, spellInfo->Id) && unitCaster->HasAura(67556)) // cooking with Chef Hat.
+ castTime = 500;
+}
+
+void WorldObject::ModSpellDurationTime(SpellInfo const* spellInfo, int32& duration, Spell* spell /*= nullptr*/) const
+{
+ if (!spellInfo || duration < 0)
+ return;
+
+ if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
+ return;
+
+ // called from caster
+ if (Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, duration, spell);
+
+ Unit const* unitCaster = ToUnit();
+ if (!unitCaster)
+ return;
+
+ if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) &&
+ ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT))
+ duration = int32(float(duration) * unitCaster->m_unitData->ModCastingSpeed);
+ else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG))
+ duration = int32(float(duration) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]);
+}
+
+float WorldObject::MeleeSpellMissChance(Unit const* /*victim*/, WeaponAttackType /*attType*/, SpellInfo const* /*spellInfo*/) const
+{
+ return 0.0f;
+}
+
+SpellMissInfo WorldObject::MeleeSpellHitResult(Unit* /*victim*/, SpellInfo const* /*spellInfo*/) const
+{
+ return SPELL_MISS_NONE;
+}
+
+SpellMissInfo WorldObject::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const
+{
+ // Can`t miss on dead target (on skinning for example)
+ if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_MISS_NONE;
+
+ SpellSchoolMask schoolMask = spellInfo->GetSchoolMask();
+ // PvP - PvE spell misschances per leveldif > 2
+ int32 lchance = victim->GetTypeId() == TYPEID_PLAYER ? 7 : 11;
+ int32 thisLevel = GetLevelForTarget(victim);
+ if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger())
+ thisLevel = std::max<int32>(thisLevel, spellInfo->SpellLevel);
+ int32 leveldif = int32(victim->GetLevelForTarget(this)) - thisLevel;
+ int32 levelBasedHitDiff = leveldif;
+
+ // Base hit chance from attacker and victim levels
+ int32 modHitChance = 100;
+ if (levelBasedHitDiff >= 0)
+ {
+ if (victim->GetTypeId() != TYPEID_PLAYER)
+ {
+ modHitChance = 94 - 3 * std::min(levelBasedHitDiff, 3);
+ levelBasedHitDiff -= 3;
+ }
+ else
+ {
+ modHitChance = 96 - std::min(levelBasedHitDiff, 2);
+ levelBasedHitDiff -= 2;
+ }
+ if (levelBasedHitDiff > 0)
+ modHitChance -= lchance * std::min(levelBasedHitDiff, 7);
+ }
+ else
+ modHitChance = 97 - levelBasedHitDiff;
+
+ // Spellmod from SpellModOp::HitChance
+ if (Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo, SpellModOp::HitChance, modHitChance);
+
+ // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects
+ if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
+ {
+ // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
+ modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask);
+ }
+
+ int32 HitChance = modHitChance * 100;
+ // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
+ if (Unit const* unit = ToUnit())
+ HitChance += int32(unit->m_modSpellHitChance * 100.0f);
+
+ RoundToInterval(HitChance, 0, 10000);
+
+ int32 tmp = 10000 - HitChance;
+
+ int32 rand = irand(0, 9999);
+ if (tmp > 0 && rand < tmp)
+ return SPELL_MISS_MISS;
+
+ // Chance resist mechanic (select max value from every mechanic spell effect)
+ int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100;
+
+ // Roll chance
+ if (resist_chance > 0 && rand < (tmp += resist_chance))
+ return SPELL_MISS_RESIST;
+
+ // cast by caster in front of victim
+ if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)))
+ {
+ int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100;
+ if (deflect_chance > 0 && rand < (tmp += deflect_chance))
+ return SPELL_MISS_DEFLECT;
+ }
+
+ return SPELL_MISS_NONE;
+}
+
+// Calculate spell hit result can be:
+// Every spell can: Evade/Immune/Reflect/Sucesful hit
+// For melee based spells:
+// Miss
+// Dodge
+// Parry
+// For spells
+// Resist
+SpellMissInfo WorldObject::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/) const
+{
+ // All positive spells can`t miss
+ /// @todo client not show miss log for this spells - so need find info for this in dbc and use it!
+ if (spellInfo->IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell
+ return SPELL_MISS_NONE;
+
+ if (this == victim)
+ return SPELL_MISS_NONE;
+
+ // Return evade for units in evade mode
+ if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())
+ return SPELL_MISS_EVADE;
+
+ // Try victim reflect spell
+ if (canReflect)
+ {
+ int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS);
+ reflectchance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_REFLECT_SPELLS_SCHOOL, spellInfo->GetSchoolMask());
+
+ if (reflectchance > 0 && roll_chance_i(reflectchance))
+ return SPELL_MISS_REFLECT;
+ }
+
+ if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
+ return SPELL_MISS_NONE;
+
+ // Check for immune
+ if (victim->IsImmunedToSpell(spellInfo, this))
+ return SPELL_MISS_IMMUNE;
+
+ // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
+ // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit
+ if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo))
+ return SPELL_MISS_IMMUNE;
+
+ switch (spellInfo->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_RANGED:
+ case SPELL_DAMAGE_CLASS_MELEE:
+ return MeleeSpellHitResult(victim, spellInfo);
+ case SPELL_DAMAGE_CLASS_NONE:
+ return SPELL_MISS_NONE;
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ return MagicSpellHitResult(victim, spellInfo);
+ }
+ return SPELL_MISS_NONE;
+}
+
+FactionTemplateEntry const* WorldObject::GetFactionTemplateEntry() const
+{
+ FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(GetFaction());
+ if (!entry)
+ {
+ if (Player const* player = ToPlayer())
+ TC_LOG_ERROR("entities", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), GetFaction());
+ else if (Creature const* creature = ToCreature())
+ TC_LOG_ERROR("entities", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, GetFaction());
+ else if (GameObject const* go = ToGameObject())
+ TC_LOG_ERROR("entities", "GameObject (template id: %u) has invalid faction (faction template id) #%u", go->GetGOInfo()->entry, GetFaction());
+ else
+ TC_LOG_ERROR("entities", "WorldObject (name: %s, type: %u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), GetFaction());
+ }
+
+ return entry;
+}
+
+// function based on function Unit::UnitReaction from 13850 client
+ReputationRank WorldObject::GetReactionTo(WorldObject const* target) const
+{
+ // always friendly to self
+ if (this == target)
+ return REP_FRIENDLY;
+
+ // always friendly to charmer or owner
+ if (GetCharmerOrOwnerOrSelf() == target->GetCharmerOrOwnerOrSelf())
+ return REP_FRIENDLY;
+
+ Player const* selfPlayerOwner = GetAffectingPlayer();
+ Player const* targetPlayerOwner = target->GetAffectingPlayer();
+
+ // check forced reputation to support SPELL_AURA_FORCE_REACTION
+ if (selfPlayerOwner)
+ {
+ if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry())
+ if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry))
+ return *repRank;
+ }
+ else if (targetPlayerOwner)
+ {
+ if (FactionTemplateEntry const* selfFactionTemplateEntry = GetFactionTemplateEntry())
+ if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry))
+ return *repRank;
+ }
+
+ Unit const* unit = ToUnit();
+ Unit const* targetUnit = target->ToUnit();
+ if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ {
+ if (targetUnit && targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ {
+ if (selfPlayerOwner && targetPlayerOwner)
+ {
+ // always friendly to other unit controlled by player, or to the player himself
+ if (selfPlayerOwner == targetPlayerOwner)
+ return REP_FRIENDLY;
+
+ // duel - always hostile to opponent
+ if (selfPlayerOwner->duel && selfPlayerOwner->duel->opponent == targetPlayerOwner && selfPlayerOwner->duel->startTime != 0)
+ return REP_HOSTILE;
+
+ // same group - checks dependant only on our faction - skip FFA_PVP for example
+ if (selfPlayerOwner->IsInRaidWith(targetPlayerOwner))
+ return REP_FRIENDLY; // return true to allow config option AllowTwoSide.Interaction.Group to work
+ // however client seems to allow mixed group parties, because in 13850 client it works like:
+ // return GetFactionReactionTo(GetFactionTemplateEntry(), target);
+ }
+
+ // check FFA_PVP
+ if (unit->IsFFAPvP() && targetUnit->IsFFAPvP())
+ return REP_HOSTILE;
+
+ if (selfPlayerOwner)
+ {
+ if (FactionTemplateEntry const* targetFactionTemplateEntry = targetUnit->GetFactionTemplateEntry())
+ {
+ if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry))
+ return *repRank;
+ if (!selfPlayerOwner->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION))
+ {
+ if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->Faction))
+ {
+ if (targetFactionEntry->CanHaveReputation())
+ {
+ // check contested flags
+ if (targetFactionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD
+ && selfPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
+ return REP_HOSTILE;
+
+ // if faction has reputation, hostile state depends only from AtWar state
+ if (selfPlayerOwner->GetReputationMgr().IsAtWar(targetFactionEntry))
+ return REP_HOSTILE;
+ return REP_FRIENDLY;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // do checks dependant only on our faction
+ return WorldObject::GetFactionReactionTo(GetFactionTemplateEntry(), target);
+}
+
+/*static*/ ReputationRank WorldObject::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, WorldObject const* target)
+{
+ // always neutral when no template entry found
+ if (!factionTemplateEntry)
+ return REP_NEUTRAL;
+
+ FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry();
+ if (!targetFactionTemplateEntry)
+ return REP_NEUTRAL;
+
+ if (Player const* targetPlayerOwner = target->GetAffectingPlayer())
+ {
+ // check contested flags
+ if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD
+ && targetPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
+ return REP_HOSTILE;
+ if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry))
+ return *repRank;
+ if (target->IsUnit() && !target->ToUnit()->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION))
+ {
+ if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->Faction))
+ {
+ if (factionEntry->CanHaveReputation())
+ {
+ // CvP case - check reputation, don't allow state higher than neutral when at war
+ ReputationRank repRank = targetPlayerOwner->GetReputationMgr().GetRank(factionEntry);
+ if (targetPlayerOwner->GetReputationMgr().IsAtWar(factionEntry))
+ repRank = std::min(REP_NEUTRAL, repRank);
+ return repRank;
+ }
+ }
+ }
+ }
+
+ // common faction based check
+ if (factionTemplateEntry->IsHostileTo(targetFactionTemplateEntry))
+ return REP_HOSTILE;
+ if (factionTemplateEntry->IsFriendlyTo(targetFactionTemplateEntry))
+ return REP_FRIENDLY;
+ if (targetFactionTemplateEntry->IsFriendlyTo(factionTemplateEntry))
+ return REP_FRIENDLY;
+ if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT)
+ return REP_HOSTILE;
+ // neutral by default
+ return REP_NEUTRAL;
+}
+
+bool WorldObject::IsHostileTo(WorldObject const* target) const
+{
+ return GetReactionTo(target) <= REP_HOSTILE;
+}
+
+bool WorldObject::IsFriendlyTo(WorldObject const* target) const
+{
+ return GetReactionTo(target) >= REP_FRIENDLY;
+}
+
+bool WorldObject::IsHostileToPlayers() const
+{
+ FactionTemplateEntry const* my_faction = GetFactionTemplateEntry();
+ if (!my_faction->Faction)
+ return false;
+
+ FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction);
+ if (raw_faction && raw_faction->ReputationIndex >= 0)
+ return false;
+
+ return my_faction->IsHostileToPlayers();
+}
+
+bool WorldObject::IsNeutralToAll() const
+{
+ FactionTemplateEntry const* my_faction = GetFactionTemplateEntry();
+ if (!my_faction->Faction)
+ return true;
+
+ FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction);
+ if (raw_faction && raw_faction->ReputationIndex >= 0)
+ return false;
+
+ return my_faction->IsNeutralToAll();
+}
+
+void WorldObject::CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/)
+{
+ SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId, args.CastDifficulty != DIFFICULTY_NONE ? args.CastDifficulty : GetMap()->GetDifficultyID());
+ if (!info)
+ {
+ TC_LOG_ERROR("entities.unit", "CastSpell: unknown spell %u by caster %s", spellId, GetGUID().ToString().c_str());
+ return;
+ }
+
+ Spell* spell = new Spell(this, info, args.TriggerFlags, args.OriginalCaster);
+ for (auto const& pair : args.SpellValueOverrides)
+ spell->SetSpellValue(pair.first, pair.second);
+
+ spell->m_CastItem = args.CastItem;
+ spell->prepare(targets, args.TriggeringAura);
+}
+
+void WorldObject::CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/)
+{
+ SpellCastTargets targets;
+ if (target)
+ {
+ if (Unit* unitTarget = target->ToUnit())
+ targets.SetUnitTarget(unitTarget);
+ else if (GameObject* goTarget = target->ToGameObject())
+ targets.SetGOTarget(goTarget);
+ else
+ {
+ TC_LOG_ERROR("entities.unit", "CastSpell: Invalid target %s passed to spell cast by %s", target->GetGUID().ToString().c_str(), GetGUID().ToString().c_str());
+ return;
+ }
+ }
+ CastSpell(targets, spellId, args);
+}
+
+void WorldObject::CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/)
+{
+ SpellCastTargets targets;
+ targets.SetDst(dest);
+ CastSpell(targets, spellId, args);
+}
+
+// function based on function Unit::CanAttack from 13850 client
+bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const
+{
+ ASSERT(target);
+
+ // can't attack self
+ if (this == target)
+ return false;
+
+ // can't attack GMs
+ if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster())
+ return false;
+
+ Unit const* unit = ToUnit();
+ Unit const* targetUnit = target->ToUnit();
+
+ // CvC case - can attack each other only when one of them is hostile
+ if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && targetUnit && !targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ return IsHostileTo(target) || target->IsHostileTo(this);
+
+ // PvP, PvC, CvP case
+ // can't attack friendly targets
+ if (IsFriendlyTo(target) || target->IsFriendlyTo(this))
+ return false;
+
+ Player const* playerAffectingAttacker = unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
+ Player const* playerAffectingTarget = targetUnit && targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
+
+ // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
+ if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget))
+ {
+ Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
+
+ if (Unit const* creature = playerAffectingAttacker ? targetUnit : unit)
+ {
+ if (creature->IsContestedGuard() && player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
+ return true;
+
+ if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
+ {
+ if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
+ if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction))
+ if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
+ if (!repState->Flags.HasFlag(ReputationFlags::AtWar))
+ return false;
+
+ }
+ }
+ }
+
+ Creature const* creatureAttacker = ToCreature();
+ if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))
+ return false;
+
+ if (!bySpell)
+ spellCheck = false;
+
+ if (spellCheck && !IsValidSpellAttackTarget(target, bySpell))
+ return false;
+
+ return true;
+}
+
+bool WorldObject::IsValidSpellAttackTarget(WorldObject const* target, SpellInfo const* bySpell) const
+{
+ ASSERT(target);
+ ASSERT(bySpell);
+
+ // can't attack unattackable units
+ Unit const* unitTarget = target->ToUnit();
+ if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE))
+ return false;
+
+ Unit const* unit = ToUnit();
+ // visibility checks (only units)
+ if (unit)
+ {
+ // can't attack invisible
+ if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE))
+ {
+ if (!unit->CanSeeOrDetect(target, bySpell->IsAffectingArea()))
+ return false;
+
+ /*
+ else if (!obj)
+ {
+ // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player
+ bool const ignoreStealthCheck = (bySpell && bySpell->IsAffectingArea()) ||
+ (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && IsInCombatWith(target));
+
+ if (!CanSeeOrDetect(target, ignoreStealthCheck))
+ return false;
+ }
+ */
+ }
+ }
+
+ // can't attack dead
+ if (!bySpell->IsAllowingDeadTarget() && unitTarget && !unitTarget->IsAlive())
+ return false;
+
+ // can't attack untargetable
+ if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
+ return false;
+
+ if (Player const* playerAttacker = ToPlayer())
+ {
+ if (playerAttacker->HasPlayerFlag(PLAYER_FLAGS_UBER))
+ return false;
+ }
+
+ // check flags
+ if (unitTarget && unitTarget->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)))
+ return false;
+
+ if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC())
+ return false;
+
+ if (unitTarget && !unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC())
+ return false;
+
+ if (!bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
+ {
+ if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC())
+ return false;
+
+ if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC())
+ return false;
+ }
+
+ // check duel - before sanctuary checks
+ Player const* playerAffectingAttacker = unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
+ Player const* playerAffectingTarget = unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
+ if (playerAffectingAttacker && playerAffectingTarget)
+ if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
+ return true;
+
+ // PvP case - can't attack when attacker or target are in sanctuary
+ // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
+ if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && (unitTarget->IsInSanctuary() || unit->IsInSanctuary()))
+ return false;
+
+ // additional checks - only PvP case
+ if (playerAffectingAttacker && playerAffectingTarget)
+ {
+ if (unitTarget->IsPvP())
+ return true;
+
+ if (unit->IsFFAPvP() && unitTarget->IsFFAPvP())
+ return true;
+
+ return unit->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1) ||
+ unitTarget->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1);
+ }
+
+ return true;
+}
+
+// function based on function Unit::CanAssist from 13850 client
+bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const
+{
+ ASSERT(target);
+
+ // can assist to self
+ if (this == target)
+ return true;
+
+ // can't assist GMs
+ if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster())
+ return false;
+
+ // can't assist non-friendly targets
+ if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)))
+ return false;
+
+ if (!bySpell)
+ spellCheck = false;
+
+ if (spellCheck && !IsValidSpellAssistTarget(target, bySpell))
+ return false;
+
+ return true;
+}
+
+bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo const* bySpell) const
+{
+ ASSERT(target);
+ ASSERT(bySpell);
+
+ // can't assist unattackable units
+ Unit const* unitTarget = target->ToUnit();
+ if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE))
+ return false;
+
+ // can't assist own vehicle or passenger
+ Unit const* unit = ToUnit();
+ if (unit && unitTarget && unit->GetVehicle())
+ {
+ if (unit->IsOnVehicle(unitTarget))
+ return false;
+
+ if (unit->GetVehicleBase()->IsOnVehicle(unitTarget))
+ return false;
+ }
+
+ // can't assist invisible
+ if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !CanSeeOrDetect(target, bySpell->IsAffectingArea()))
+ return false;
+
+ // can't assist dead
+ if (!bySpell->IsAllowingDeadTarget() && unitTarget && !unitTarget->IsAlive())
+ return false;
+
+ // can't assist untargetable
+ if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
+ return false;
+
+ if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
+ {
+ if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ {
+ if (unitTarget && unitTarget->IsImmuneToPC())
+ return false;
+ }
+ else
+ {
+ if (unitTarget && unitTarget->IsImmuneToNPC())
+ return false;
+ }
+ }
+
+ // PvP case
+ if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ {
+ Player const* targetPlayerOwner = target->GetAffectingPlayer();
+ if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ {
+ Player const* selfPlayerOwner = GetAffectingPlayer();
+ if (selfPlayerOwner && targetPlayerOwner)
+ {
+ // can't assist player which is dueling someone
+ if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner->duel)
+ return false;
+ }
+ // can't assist player in ffa_pvp zone from outside
+ if (unitTarget->IsFFAPvP() && unit && !unit->IsFFAPvP())
+ return false;
+
+ // can't assist player out of sanctuary from sanctuary if has pvp enabled
+ if (unitTarget->IsPvP())
+ if (unit && unit->IsInSanctuary() && !unitTarget->IsInSanctuary())
+ return false;
+ }
+ }
+ // PvC case - player can assist creature only if has specific type flags
+ // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) &&
+ else if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
+ {
+ if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
+ if (unitTarget && !unitTarget->IsPvP())
+ if (Creature const* creatureTarget = target->ToCreature())
+ return ((creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST));
+ }
+
+ return true;
+}
+
+Unit* WorldObject::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
+{
+ // Patch 1.2 notes: Spell Reflection no longer reflects abilities
+ if (spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY))
+ return victim;
+
+ Unit::AuraEffectList const& magnetAuras = victim->GetAuraEffectsByType(SPELL_AURA_SPELL_MAGNET);
+ for (AuraEffect const* aurEff : magnetAuras)
+ {
+ if (Unit* magnet = aurEff->GetBase()->GetCaster())
+ {
+ if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK && IsValidAttackTarget(magnet, spellInfo))
+ {
+ /// @todo handle this charge drop by proc in cast phase on explicit target
+ if (spellInfo->HasHitDelay())
+ {
+ // Set up missile speed based delay
+ float hitDelay = spellInfo->LaunchDelay;
+ if (spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
+ hitDelay += spellInfo->Speed;
+ else if (spellInfo->Speed > 0.0f)
+ hitDelay += std::max(victim->GetDistance(this), 5.0f) / spellInfo->Speed;
+
+ uint32 delay = uint32(std::floor(hitDelay * 1000.0f));
+ // Schedule charge drop
+ aurEff->GetBase()->DropChargeDelayed(delay, AURA_REMOVE_BY_EXPIRE);
+ }
+ else
+ aurEff->GetBase()->DropCharge(AURA_REMOVE_BY_EXPIRE);
+
+ return magnet;
+ }
+ }
+ }
+ return victim;
+}
+
+uint32 WorldObject::GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const
+{
+ return spellInfo->GetSpellXSpellVisualId(this);
+}
+
template <typename Container>
void WorldObject::GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange /*= 250.0f*/) const
{
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 5f46af426d8..4e0d9005b07 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -20,6 +20,7 @@
#include "Common.h"
#include "Duration.h"
+#include "EventProcessor.h"
#include "GridReference.h"
#include "GridRefManager.h"
#include "ModelIgnoreFlags.h"
@@ -47,6 +48,9 @@ class Map;
class Object;
class Player;
class Scenario;
+class Spell;
+class SpellCastTargets;
+class SpellInfo;
class TempSummon;
class Transport;
class Unit;
@@ -54,9 +58,18 @@ class UpdateData;
class WorldObject;
class WorldPacket;
class ZoneScript;
+struct FactionTemplateEntry;
struct PositionFullTerrainStatus;
struct QuaternionData;
+namespace WorldPackets
+{
+ namespace CombatLog
+ {
+ class CombatLogServerPacket;
+ }
+}
+
typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType;
struct CreateObjectBits
@@ -400,7 +413,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
public:
virtual ~WorldObject();
- virtual void Update (uint32 /*time_diff*/) { }
+ virtual void Update(uint32 /*time_diff*/) { }
void AddToWorld() override;
void RemoveFromWorld() override;
@@ -491,6 +504,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
virtual void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const;
virtual void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const;
+ void SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const;
+
virtual uint8 GetLevelForTarget(WorldObject const* /*target*/) const { return 1; }
void PlayDistanceSound(uint32 soundId, Player* target = nullptr);
@@ -538,6 +553,61 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const;
Player* SelectNearestPlayer(float distance) const;
+ virtual ObjectGuid GetOwnerGUID() const = 0;
+ virtual ObjectGuid GetCharmerOrOwnerGUID() const { return GetOwnerGUID(); }
+ ObjectGuid GetCharmerOrOwnerOrOwnGUID() const;
+
+ Unit* GetOwner() const;
+ Unit* GetCharmerOrOwner() const;
+ Unit* GetCharmerOrOwnerOrSelf() const;
+ Player* GetCharmerOrOwnerPlayerOrPlayerItself() const;
+ Player* GetAffectingPlayer() const;
+
+ Player* GetSpellModOwner() const;
+ int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellInfo, uint8 effIndex, int32 const* basePoints = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const;
+
+ // target dependent range checks
+ float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
+ float GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
+
+ float ApplyEffectModifiers(SpellInfo const* spellInfo, uint8 effIndex, float value) const;
+ int32 CalcSpellDuration(SpellInfo const* spellInfo) const;
+ int32 ModSpellDuration(SpellInfo const* spellInfo, WorldObject const* target, int32 duration, bool positive, uint32 effectMask) const;
+ void ModSpellCastTime(SpellInfo const* spellInfo, int32& castTime, Spell* spell = nullptr) const;
+ void ModSpellDurationTime(SpellInfo const* spellInfo, int32& durationTime, Spell* spell = nullptr) const;
+
+ virtual float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const;
+ virtual SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const;
+ SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const;
+ SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false) const;
+
+ virtual uint32 GetFaction() const = 0;
+ virtual void SetFaction(uint32 /*faction*/) { }
+ FactionTemplateEntry const* GetFactionTemplateEntry() const;
+
+ ReputationRank GetReactionTo(WorldObject const* target) const;
+ static ReputationRank GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, WorldObject const* target);
+
+ bool IsHostileTo(WorldObject const* target) const;
+ bool IsHostileToPlayers() const;
+ bool IsFriendlyTo(WorldObject const* target) const;
+ bool IsNeutralToAll() const;
+
+ // CastSpell's third arg can be a variety of things - check out CastSpellExtraArgs' constructors!
+ void CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args = { });
+ void CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args = { });
+ void CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args = { });
+
+ bool IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const;
+ bool IsValidSpellAttackTarget(WorldObject const* target, SpellInfo const* bySpell) const;
+
+ bool IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const;
+ bool IsValidSpellAssistTarget(WorldObject const* target, SpellInfo const* bySpell) const;
+
+ Unit* GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo);
+
+ virtual uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const;
+
template <typename Container>
void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const;
@@ -599,6 +669,9 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
float GetMapWaterOrGroundLevel(float x, float y, float z, float* ground = nullptr) const;
float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h
+ // Event handler
+ EventProcessor m_Events;
+
virtual uint16 GetAIAnimKitId() const { return 0; }
virtual uint16 GetMovementAnimKitId() const { return 0; }
virtual uint16 GetMeleeAnimKitId() const { return 0; }
@@ -614,7 +687,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
bool m_isActive;
bool m_isFarVisible;
Optional<float> m_visibilityDistanceOverride;
- const bool m_isWorldObject;
+ bool const m_isWorldObject;
ZoneScript* m_zoneScript;
// transports
@@ -640,7 +713,6 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
private:
Map* m_currMap; // current object's Map location
- //uint32 m_mapId; // object at map with map_id
uint32 m_InstanceId; // in map copy with instance id
PhaseShift _phaseShift;
PhaseShift _suppressedPhaseShift; // contains phases for current area but not applied due to conditions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 62bcd92b3d3..166b650f7e9 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1663,7 +1663,7 @@ void Player::SetObjectScale(float scale)
SendMovementSetCollisionHeight(scale * GetCollisionHeight(), WorldPackets::Movement::UpdateCollisionHeightReason::Scale);
}
-bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const
+bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const
{
SpellEffectInfo const* effect = spellInfo->GetEffect(index);
if (!effect || !effect->IsEffect())
@@ -23424,7 +23424,7 @@ void Player::UpdatePotionCooldown(Spell* spell)
m_lastPotionId = 0;
}
-void Player::SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura)
+void Player::SetResurrectRequestData(WorldObject const* caster, uint32 health, uint32 mana, uint32 appliedAura)
{
ASSERT(!IsResurrectRequested());
_resurrectionData.reset(new ResurrectionData());
@@ -24082,45 +24082,6 @@ Player* Player::GetSelectedPlayer() const
return nullptr;
}
-void Player::AddComboPoints(int8 count, Spell* spell)
-{
- if (!count)
- return;
-
- int8 comboPoints = spell ? spell->m_comboPointGain : GetPower(POWER_COMBO_POINTS);
-
- comboPoints += count;
-
- if (comboPoints > 5)
- comboPoints = 5;
- else if (comboPoints < 0)
- comboPoints = 0;
-
- if (!spell)
- SetPower(POWER_COMBO_POINTS, comboPoints);
- else
- spell->m_comboPointGain = comboPoints;
-}
-
-void Player::GainSpellComboPoints(int8 count)
-{
- if (!count)
- return;
-
- int8 cp = GetPower(POWER_COMBO_POINTS);
-
- cp += count;
- if (cp > 5) cp = 5;
- else if (cp < 0) cp = 0;
-
- SetPower(POWER_COMBO_POINTS, cp);
-}
-
-void Player::ClearComboPoints()
-{
- SetPower(POWER_COMBO_POINTS, 0);
-}
-
bool Player::IsInGroup(ObjectGuid groupGuid) const
{
if (Group const* group = GetGroup())
@@ -26626,10 +26587,10 @@ void Player::ResetCriteria(CriteriaFailEvent condition, int32 failAsset, bool ev
m_questObjectiveCriteriaMgr->ResetCriteria(condition, failAsset, evenIfCriteriaComplete);
}
-void Player::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit* unit /*= nullptr*/)
+void Player::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, WorldObject* ref /*= nullptr*/)
{
- m_achievementMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
- m_questObjectiveCriteriaMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
+ m_achievementMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this);
+ m_questObjectiveCriteriaMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this);
// Update only individual achievement criteria here, otherwise we may get multiple updates
// from a single boss kill
@@ -26637,10 +26598,10 @@ void Player::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint6
return;
if (Scenario* scenario = GetScenario())
- scenario->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
+ scenario->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this);
if (Guild* guild = sGuildMgr->GetGuildById(GetGuildId()))
- guild->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
+ guild->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this);
}
void Player::CompletedAchievement(AchievementEntry const* entry)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index ed747c885ab..0fd50b7b5f9 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1085,7 +1085,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void Update(uint32 time) override;
- bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override;
+ bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override;
void SetInWater(bool inWater) override;
@@ -1664,11 +1664,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SetTarget(ObjectGuid const& /*guid*/) override { } /// Used for serverside target changes, does not apply to players
void SetSelection(ObjectGuid const& guid) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Target), guid); }
- uint32 GetComboPoints() const { return uint32(GetPower(POWER_COMBO_POINTS)); }
- void AddComboPoints(int8 count, Spell* spell = nullptr);
- void GainSpellComboPoints(int8 count);
- void ClearComboPoints();
-
void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError = 0, ObjectGuid::LowType item_guid = UI64LIT(0), uint32 item_count = 0) const;
void SendNewMail() const;
void UpdateNextMailTimeAndUnreads();
@@ -1816,7 +1811,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
void UpdatePotionCooldown(Spell* spell = nullptr);
- void SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura);
+ void SetResurrectRequestData(WorldObject const* caster, uint32 health, uint32 mana, uint32 appliedAura);
void ClearResurrectRequestData()
{
_resurrectionData.reset();
@@ -2499,7 +2494,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool HasAchieved(uint32 achievementId) const;
void ResetAchievements();
void ResetCriteria(CriteriaFailEvent condition, int32 failAsset, bool evenIfCriteriaComplete = false);
- void UpdateCriteria(CriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, Unit* unit = nullptr);
+ void UpdateCriteria(CriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject* ref = nullptr);
void StartCriteriaTimer(CriteriaStartEvent startEvent, uint32 entry, uint32 timeLost = 0);
void RemoveCriteriaTimer(CriteriaStartEvent startEvent, uint32 entry);
void CompletedAchievement(AchievementEntry const* entry);
diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp
index b5822d11f8a..38f30551a63 100644
--- a/src/server/game/Entities/Totem/Totem.cpp
+++ b/src/server/game/Entities/Totem/Totem.cpp
@@ -137,7 +137,7 @@ void Totem::UnSummon(uint32 msTime)
AddObjectToRemoveList();
}
-bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const
+bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const
{
/// @todo possibly all negative auras immune?
if (GetEntry() == 5925)
diff --git a/src/server/game/Entities/Totem/Totem.h b/src/server/game/Entities/Totem/Totem.h
index 92f29789384..54fc53a09d0 100644
--- a/src/server/game/Entities/Totem/Totem.h
+++ b/src/server/game/Entities/Totem/Totem.h
@@ -50,7 +50,7 @@ class TC_GAME_API Totem : public Minion
void UpdateAttackPowerAndDamage(bool /*ranged*/) override { }
void UpdateDamagePhysical(WeaponAttackType /*attType*/) override { }
- bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override;
+ bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override;
protected:
TotemType m_type;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index b75fc274072..6fa98f4bb55 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -940,50 +940,46 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons
}
}
- if (damagetype != NODAMAGE)
+ if (damagetype != NODAMAGE && damagetype != DOT)
{
- if (victim != attacker
- && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC))))
+ if (victim != attacker && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC))))
{
- if (damagetype != DOT)
+ if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
{
- if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
+ if (spell->getState() == SPELL_STATE_PREPARING)
{
- if (spell->getState() == SPELL_STATE_PREPARING)
+ auto isCastInterrupted = [&]()
{
- auto isCastInterrupted = [&]()
- {
- if (!damage)
- return spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::ZeroDamageCancels);
+ if (!damage)
+ return spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::ZeroDamageCancels);
- if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly)))
- return true;
+ if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly)))
+ return true;
- if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels))
- return true;
+ if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels))
+ return true;
- return false;
- };
+ return false;
+ };
- auto isCastDelayed = [&]()
- {
- if (!damage)
- return false;
+ auto isCastDelayed = [&]()
+ {
+ if (!damage)
+ return false;
- if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly)))
- return true;
+ if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly)))
+ return true;
- if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushback))
- return true;
+ if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushback))
+ return true;
- return false;
- };
+ return false;
+ };
- if (isCastInterrupted())
- victim->InterruptNonMeleeSpells(false);
- else if (isCastDelayed())
- spell->Delayed();
- }
+ if (isCastInterrupted())
+ victim->InterruptNonMeleeSpells(false);
+ else if (isCastDelayed())
+ spell->Delayed();
}
if (damage && victim->IsPlayer())
@@ -1024,48 +1020,6 @@ void Unit::CastStop(uint32 except_spellid)
InterruptSpell(CurrentSpellTypes(i), false);
}
-void Unit::CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args)
-{
- SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId, args.CastDifficulty != DIFFICULTY_NONE ? args.CastDifficulty : GetMap()->GetDifficultyID());
- if (!info)
- {
- TC_LOG_ERROR("entities.unit", "CastSpell: unknown spell %u by caster %s", spellId, GetGUID().ToString().c_str());
- return;
- }
-
- Spell* spell = new Spell(this, info, args.TriggerFlags, args.OriginalCaster);
- for (auto const& pair : args.SpellValueOverrides)
- spell->SetSpellValue(pair.first, pair.second);
-
- spell->m_CastItem = args.CastItem;
- spell->prepare(targets, args.TriggeringAura);
-}
-
-void Unit::CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args)
-{
- SpellCastTargets targets;
- if (target)
- {
- if (Unit* unitTarget = target->ToUnit())
- targets.SetUnitTarget(unitTarget);
- else if (GameObject* goTarget = target->ToGameObject())
- targets.SetGOTarget(goTarget);
- else
- {
- TC_LOG_ERROR("entities.unit", "CastSpell: Invalid target %s passed to spell cast by %s", target->GetGUID().ToString().c_str(), GetGUID().ToString().c_str());
- return;
- }
- }
- CastSpell(targets, spellId, args);
-}
-
-void Unit::CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args)
-{
- SpellCastTargets targets;
- targets.SetDst(dest);
- CastSpell(targets, spellId, args);
-}
-
void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType, bool crit)
{
if (damage < 0)
@@ -1720,21 +1674,19 @@ void Unit::HandleEmoteCommand(uint32 anim_id, Trinity::IteratorPair<int32 const*
return uint32(damageResisted);
}
-/*static*/ float Unit::CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo /*= nullptr*/)
+/*static*/ float Unit::CalculateAverageResistReduction(WorldObject const* caster, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo /*= nullptr*/)
{
float victimResistance = float(victim->GetResistance(schoolMask));
- if (attacker)
+ if (caster)
{
// pets inherit 100% of masters penetration
- // excluding traps
- Player const* player = attacker->GetSpellModOwner();
- if (player && attacker->GetEntry() != WORLD_TRIGGER)
+ if (Player const* player = caster->GetSpellModOwner())
{
victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
victimResistance -= float(player->GetSpellPenetrationItemMod());
}
- else
- victimResistance += float(attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
+ else if (Unit const* unitCaster = caster->ToUnit())
+ victimResistance += float(unitCaster->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
}
// holy resistance exists in pve and comes from level difference, ignore template values
@@ -1748,12 +1700,13 @@ void Unit::HandleEmoteCommand(uint32 anim_id, Trinity::IteratorPair<int32 const*
victimResistance = std::max(victimResistance, 0.0f);
// level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration
- if (attacker && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL)))
- victimResistance += std::max((float(victim->GetLevelForTarget(attacker)) - float(attacker->GetLevelForTarget(victim))) * 5.0f, 0.0f);
+ // gameobject caster -- should it have level based resistance?
+ if (caster && caster->GetTypeId() != TYPEID_GAMEOBJECT && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL)))
+ victimResistance += std::max((float(victim->GetLevelForTarget(caster)) - float(caster->GetLevelForTarget(victim))) * 5.0f, 0.0f);
static uint32 const bossLevel = 83;
static float const bossResistanceConstant = 510.0f;
- uint32 level = attacker ? victim->GetLevelForTarget(attacker) : victim->getLevel();
+ uint32 level = caster ? victim->GetLevelForTarget(caster) : victim->getLevel();
float resistanceConstant = 0.0f;
if (level == bossLevel)
@@ -2555,139 +2508,6 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
return SPELL_MISS_NONE;
}
-SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const
-{
- // Can`t miss on dead target (on skinning for example)
- if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER)
- return SPELL_MISS_NONE;
-
- SpellSchoolMask schoolMask = spellInfo->GetSchoolMask();
- // PvP - PvE spell misschances per leveldif > 2
- int32 lchance = victim->GetTypeId() == TYPEID_PLAYER ? 7 : 11;
- int32 thisLevel = GetLevelForTarget(victim);
- if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger())
- thisLevel = std::max<int32>(thisLevel, spellInfo->SpellLevel);
- int32 leveldif = int32(victim->GetLevelForTarget(this)) - thisLevel;
- int32 levelBasedHitDiff = leveldif;
-
- // Base hit chance from attacker and victim levels
- int32 modHitChance = 100;
- if (levelBasedHitDiff >= 0)
- {
- if (victim->GetTypeId() != TYPEID_PLAYER)
- {
- modHitChance = 94 - 3 * std::min(levelBasedHitDiff, 3);
- levelBasedHitDiff -= 3;
- }
- else
- {
- modHitChance = 96 - std::min(levelBasedHitDiff, 2);
- levelBasedHitDiff -= 2;
- }
- if (levelBasedHitDiff > 0)
- modHitChance -= lchance * std::min(levelBasedHitDiff, 7);
- }
- else
- modHitChance = 97 - levelBasedHitDiff;
-
- // Spellmod from SpellModOp::HitChance
- if (Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo, SpellModOp::HitChance, modHitChance);
-
- // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects
- if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
- {
- // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
- modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask);
- }
-
- int32 HitChance = modHitChance * 100;
- // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
- HitChance += int32(m_modSpellHitChance * 100.0f);
-
- RoundToInterval(HitChance, 0, 10000);
-
- int32 tmp = 10000 - HitChance;
-
- int32 rand = irand(0, 9999);
- if (tmp > 0 && rand < tmp)
- return SPELL_MISS_MISS;
-
- // Chance resist mechanic (select max value from every mechanic spell effect)
- int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100;
-
- // Roll chance
- if (resist_chance > 0 && rand < (tmp += resist_chance))
- return SPELL_MISS_RESIST;
-
- // cast by caster in front of victim
- if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)))
- {
- int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100;
- if (deflect_chance > 0 && rand < (tmp += deflect_chance))
- return SPELL_MISS_DEFLECT;
- }
-
- return SPELL_MISS_NONE;
-}
-
-// Calculate spell hit result can be:
-// Every spell can: Evade/Immune/Reflect/Sucesful hit
-// For melee based spells:
-// Miss
-// Dodge
-// Parry
-// For spells
-// Resist
-SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/)
-{
- // All positive spells can`t miss
- /// @todo client not show miss log for this spells - so need find info for this in dbc and use it!
- if (spellInfo->IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell
- return SPELL_MISS_NONE;
-
- if (this == victim)
- return SPELL_MISS_NONE;
-
- // Return evade for units in evade mode
- if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())
- return SPELL_MISS_EVADE;
-
- // Try victim reflect spell
- if (canReflect)
- {
- int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS);
- reflectchance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_REFLECT_SPELLS_SCHOOL, spellInfo->GetSchoolMask());
-
- if (reflectchance > 0 && roll_chance_i(reflectchance))
- return SPELL_MISS_REFLECT;
- }
-
- if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
- return SPELL_MISS_NONE;
-
- // Check for immune
- if (victim->IsImmunedToSpell(spellInfo, this))
- return SPELL_MISS_IMMUNE;
-
- // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
- // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit
- if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo))
- return SPELL_MISS_IMMUNE;
-
- switch (spellInfo->DmgClass)
- {
- case SPELL_DAMAGE_CLASS_RANGED:
- case SPELL_DAMAGE_CLASS_MELEE:
- return MeleeSpellHitResult(victim, spellInfo);
- case SPELL_DAMAGE_CLASS_NONE:
- return SPELL_MISS_NONE;
- case SPELL_DAMAGE_CLASS_MAGIC:
- return MagicSpellHitResult(victim, spellInfo);
- }
- return SPELL_MISS_NONE;
-}
-
float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const
{
int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim);
@@ -3263,6 +3083,14 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(AuraCreateInfo& createInfo)
if (!createInfo.CasterGUID && !createInfo.GetSpellInfo()->IsStackableOnOneSlotWithDifferentCasters())
createInfo.CasterGUID = createInfo.Caster->GetGUID();
+ // world gameobjects can't own auras and they send empty casterguid
+ // checked on sniffs with spell 22247
+ if (createInfo.CasterGUID.IsGameObject())
+ {
+ createInfo.Caster = nullptr;
+ createInfo.CasterGUID.Clear();
+ }
+
// passive and Incanter's Absorption and auras with different type can stack with themselves any number of times
if (!createInfo.GetSpellInfo()->IsMultiSlotAura())
{
@@ -3863,7 +3691,7 @@ void Unit::RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID, AuraRemove
}
}
-void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, Unit* dispeller, uint8 chargesRemoved/*= 1*/)
+void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, WorldObject* dispeller, uint8 chargesRemoved /*= 1*/)
{
AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
for (AuraMap::iterator iter = range.first; iter != range.second;)
@@ -3891,7 +3719,7 @@ void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId
}
}
-void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, Unit* stealer)
+void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, WorldObject* stealer)
{
AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
for (AuraMap::iterator iter = range.first; iter != range.second;)
@@ -3925,38 +3753,41 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U
// Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster
uint32 dur = std::min(2u * MINUTE * IN_MILLISECONDS, uint32(aura->GetDuration()));
- if (Aura* oldAura = stealer->GetAura(aura->GetId(), aura->GetCasterGUID()))
+ if (Unit* unitStealer = stealer->ToUnit())
{
- if (stealCharge)
- oldAura->ModCharges(1);
+ if (Aura* oldAura = unitStealer->GetAura(aura->GetId(), aura->GetCasterGUID()))
+ {
+ if (stealCharge)
+ oldAura->ModCharges(1);
+ else
+ oldAura->ModStackAmount(1);
+ oldAura->SetDuration(int32(dur));
+ }
else
- oldAura->ModStackAmount(1);
- oldAura->SetDuration(int32(dur));
- }
- else
- {
- // single target state must be removed before aura creation to preserve existing single target aura
- if (aura->IsSingleTarget())
- aura->UnregisterSingleTarget();
+ {
+ // single target state must be removed before aura creation to preserve existing single target aura
+ if (aura->IsSingleTarget())
+ aura->UnregisterSingleTarget();
- AuraCreateInfo createInfo(aura->GetCastGUID(), aura->GetSpellInfo(), aura->GetCastDifficulty(), effMask, stealer);
- createInfo
- .SetCasterGUID(aura->GetCasterGUID())
- .SetBaseAmount(baseDamage);
+ AuraCreateInfo createInfo(aura->GetCastGUID(), aura->GetSpellInfo(), aura->GetCastDifficulty(), effMask, unitStealer);
+ createInfo
+ .SetCasterGUID(aura->GetCasterGUID())
+ .SetBaseAmount(baseDamage);
- if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo))
- {
- // created aura must not be single target aura,, so stealer won't loose it on recast
- if (newAura->IsSingleTarget())
+ if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo))
{
- newAura->UnregisterSingleTarget();
- // bring back single target aura status to the old aura
- aura->SetIsSingleTarget(true);
- caster->GetSingleCastAuras().push_back(aura);
+ // created aura must not be single target aura,, so stealer won't loose it on recast
+ if (newAura->IsSingleTarget())
+ {
+ newAura->UnregisterSingleTarget();
+ // bring back single target aura status to the old aura
+ aura->SetIsSingleTarget(true);
+ caster->GetSingleCastAuras().push_back(aura);
+ }
+ // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
+ newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]);
+ newAura->ApplyForTargets();
}
- // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
- newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]);
- newAura->ApplyForTargets();
}
}
@@ -4380,7 +4211,6 @@ void Unit::DelayOwnedAuras(uint32 spellId, ObjectGuid caster, int32 delaytime)
// update for out of range group members (on 1 slot use)
aura->SetNeedClientUpdateForTargets();
- TC_LOG_DEBUG("spells", "Aura %u partially interrupted on %s, new duration: %u ms", aura->GetId(), GetGUID().ToString().c_str(), aura->GetDuration());
}
}
}
@@ -4529,7 +4359,7 @@ Aura* Unit::GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGu
return aurApp ? aurApp->GetBase() : nullptr;
}
-void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect /*= false*/) const
+void Unit::GetDispellableAuraList(WorldObject const* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect /*= false*/) const
{
AuraMap const& auras = GetOwnedAuras();
for (auto itr = auras.begin(); itr != auras.end(); ++itr)
@@ -5555,187 +5385,6 @@ void Unit::SetSheath(SheathState sheathed)
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Sheathing);
}
-FactionTemplateEntry const* Unit::GetFactionTemplateEntry() const
-{
- FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(GetFaction());
- if (!entry)
- {
- if (Player const* player = ToPlayer())
- TC_LOG_ERROR("entities.unit", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), GetFaction());
- else if (Creature const* creature = ToCreature())
- TC_LOG_ERROR("entities.unit", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, GetFaction());
- else
- TC_LOG_ERROR("entities.unit", "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), GetFaction());
-
- ABORT();
- }
- return entry;
-}
-
-// function based on function Unit::UnitReaction from 13850 client
-ReputationRank Unit::GetReactionTo(Unit const* target) const
-{
- // always friendly to self
- if (this == target)
- return REP_FRIENDLY;
-
- // always friendly to charmer or owner
- if (GetCharmerOrOwnerOrSelf() == target->GetCharmerOrOwnerOrSelf())
- return REP_FRIENDLY;
-
- Player const* selfPlayerOwner = GetAffectingPlayer();
- Player const* targetPlayerOwner = target->GetAffectingPlayer();
-
- // check forced reputation to support SPELL_AURA_FORCE_REACTION
- if (selfPlayerOwner)
- {
- if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry())
- if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry))
- return *repRank;
- }
- else if (targetPlayerOwner)
- {
- if (FactionTemplateEntry const* selfFactionTemplateEntry = GetFactionTemplateEntry())
- if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry))
- return *repRank;
- }
-
- if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- {
- if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- {
- if (selfPlayerOwner && targetPlayerOwner)
- {
- // always friendly to other unit controlled by player, or to the player himself
- if (selfPlayerOwner == targetPlayerOwner)
- return REP_FRIENDLY;
-
- // duel - always hostile to opponent
- if (selfPlayerOwner->duel && selfPlayerOwner->duel->opponent == targetPlayerOwner && selfPlayerOwner->duel->startTime != 0)
- return REP_HOSTILE;
-
- // same group - checks dependant only on our faction - skip FFA_PVP for example
- if (selfPlayerOwner->IsInRaidWith(targetPlayerOwner))
- return REP_FRIENDLY; // return true to allow config option AllowTwoSide.Interaction.Group to work
- // however client seems to allow mixed group parties, because in 13850 client it works like:
- // return GetFactionReactionTo(GetFactionTemplateEntry(), target);
- }
-
- // check FFA_PVP
- if (IsFFAPvP() && target->IsFFAPvP())
- return REP_HOSTILE;
-
- if (selfPlayerOwner)
- {
- if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry())
- {
- if (!selfPlayerOwner->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION))
- {
- if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->Faction))
- {
- if (targetFactionEntry->CanHaveReputation())
- {
- // check contested flags
- if (targetFactionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD
- && selfPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
- return REP_HOSTILE;
-
- // if faction has reputation, hostile state depends only from AtWar state
- if (selfPlayerOwner->GetReputationMgr().IsAtWar(targetFactionEntry))
- return REP_HOSTILE;
- return REP_FRIENDLY;
- }
- }
- }
- }
- }
- }
- }
- // do checks dependant only on our faction
- return GetFactionReactionTo(GetFactionTemplateEntry(), target);
-}
-
-ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target)
-{
- // always neutral when no template entry found
- if (!factionTemplateEntry)
- return REP_NEUTRAL;
-
- FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry();
-
- if (Player const* targetPlayerOwner = target->GetAffectingPlayer())
- {
- // check contested flags
- if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD
- && targetPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
- return REP_HOSTILE;
- if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry))
- return *repRank;
- if (!target->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION))
- {
- if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->Faction))
- {
- if (factionEntry->CanHaveReputation())
- {
- // CvP case - check reputation, don't allow state higher than neutral when at war
- ReputationRank repRank = targetPlayerOwner->GetReputationMgr().GetRank(factionEntry);
- if (targetPlayerOwner->GetReputationMgr().IsAtWar(factionEntry))
- repRank = std::min(REP_NEUTRAL, repRank);
- return repRank;
- }
- }
- }
- }
-
- // common faction based check
- if (factionTemplateEntry->IsHostileTo(targetFactionTemplateEntry))
- return REP_HOSTILE;
- if (factionTemplateEntry->IsFriendlyTo(targetFactionTemplateEntry))
- return REP_FRIENDLY;
- if (targetFactionTemplateEntry->IsFriendlyTo(factionTemplateEntry))
- return REP_FRIENDLY;
- if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT)
- return REP_HOSTILE;
- // neutral by default
- return REP_NEUTRAL;
-}
-
-bool Unit::IsHostileTo(Unit const* unit) const
-{
- return GetReactionTo(unit) <= REP_HOSTILE;
-}
-
-bool Unit::IsFriendlyTo(Unit const* unit) const
-{
- return GetReactionTo(unit) >= REP_FRIENDLY;
-}
-
-bool Unit::IsHostileToPlayers() const
-{
- FactionTemplateEntry const* my_faction = GetFactionTemplateEntry();
- if (!my_faction->Faction)
- return false;
-
- FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction);
- if (raw_faction && raw_faction->ReputationIndex >= 0)
- return false;
-
- return my_faction->IsHostileToPlayers();
-}
-
-bool Unit::IsNeutralToAll() const
-{
- FactionTemplateEntry const* my_faction = GetFactionTemplateEntry();
- if (!my_faction->Faction)
- return true;
-
- FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction);
- if (raw_faction && raw_faction->ReputationIndex >= 0)
- return false;
-
- return my_faction->IsNeutralToAll();
-}
-
void Unit::_addAttacker(Unit* pAttacker)
{
m_attackers.insert(pAttacker);
@@ -6108,15 +5757,6 @@ void Unit::SetOwnerGUID(ObjectGuid owner)
player->SendDirectMessage(&packet);
}
-Unit* Unit::GetOwner() const
-{
- ObjectGuid ownerGUID = GetOwnerGUID();
- if (!ownerGUID.IsEmpty())
- return ObjectAccessor::GetUnit(*this, ownerGUID);
-
- return nullptr;
-}
-
Unit* Unit::GetCharmer() const
{
ObjectGuid charmerGUID = GetCharmerGUID();
@@ -6126,26 +5766,6 @@ Unit* Unit::GetCharmer() const
return nullptr;
}
-Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const
-{
- ObjectGuid guid = GetCharmerOrOwnerGUID();
- if (guid.IsPlayer())
- return ObjectAccessor::GetPlayer(*this, guid);
-
- return const_cast<Unit*>(this)->ToPlayer();
-}
-
-Player* Unit::GetAffectingPlayer() const
-{
- if (!GetCharmerOrOwnerGUID())
- return const_cast<Unit*>(this)->ToPlayer();
-
- if (Unit* owner = GetCharmerOrOwner())
- return owner->GetCharmerOrOwnerPlayerOrPlayerItself();
-
- return nullptr;
-}
-
Minion* Unit::GetFirstMinion() const
{
ObjectGuid pet_guid = GetMinionGUID();
@@ -6198,14 +5818,6 @@ Unit* Unit::GetCharmerOrOwner() const
return !GetCharmerGUID().IsEmpty() ? GetCharmer() : GetOwner();
}
-Unit* Unit::GetCharmerOrOwnerOrSelf() const
-{
- if (Unit* u = GetCharmerOrOwner())
- return u;
-
- return (Unit*)this;
-}
-
void Unit::SetMinion(Minion *minion, bool apply)
{
TC_LOG_DEBUG("entities.unit", "SetMinion %u for %u, apply %u", minion->GetEntry(), GetEntry(), apply);
@@ -6521,42 +6133,6 @@ bool Unit::IsMagnet() const
return false;
}
-Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
-{
- // Patch 1.2 notes: Spell Reflection no longer reflects abilities
- if (spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY))
- return victim;
-
- Unit::AuraEffectList const& magnetAuras = victim->GetAuraEffectsByType(SPELL_AURA_SPELL_MAGNET);
- for (Unit::AuraEffectList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
- {
- if (Unit* magnet = (*itr)->GetBase()->GetCaster())
- if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK
- && IsValidAttackTarget(magnet, spellInfo))
- {
- /// @todo handle this charge drop by proc in cast phase on explicit target
- if (spellInfo->HasHitDelay())
- {
- // Set up missile speed based delay
- float hitDelay = spellInfo->LaunchDelay;
- if (spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
- hitDelay += spellInfo->Speed;
- else if (spellInfo->Speed > 0.0f)
- hitDelay += std::max(victim->GetDistance(this), 5.0f) / spellInfo->Speed;
-
- uint32 delay = uint32(std::floor(hitDelay * 1000.0f));
- // Schedule charge drop
- (*itr)->GetBase()->DropChargeDelayed(delay, AURA_REMOVE_BY_EXPIRE);
- }
- else
- (*itr)->GetBase()->DropCharge(AURA_REMOVE_BY_EXPIRE);
-
- return magnet;
- }
- }
- return victim;
-}
-
Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo /*= nullptr*/)
{
AuraEffectList const& interceptAuras = victim->GetAuraEffectsByType(SPELL_AURA_INTERCEPT_MELEE_RANGED_ATTACKS);
@@ -7559,7 +7135,7 @@ bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const
return false;
}
-bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const
+bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const
{
if (!spellInfo)
return false;
@@ -7655,7 +7231,7 @@ uint32 Unit::GetMechanicImmunityMask() const
return mask;
}
-bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const
+bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const
{
if (!spellInfo)
return false;
@@ -8262,252 +7838,6 @@ bool Unit::isTargetableForAttack(bool checkFakeDeath) const
return !HasUnitState(UNIT_STATE_UNATTACKABLE) && (!checkFakeDeath || !HasUnitState(UNIT_STATE_DIED));
}
-// function based on function Unit::CanAttack from 13850 client
-bool Unit::IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell /*= nullptr*/, WorldObject const* obj /*= nullptr*/, bool spellCheck /*= true*/) const
-{
- ASSERT(target);
-
- // can't attack self
- if (this == target)
- return false;
-
- // can't attack GMs
- if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster())
- return false;
-
- // CvC case - can attack each other only when one of them is hostile
- if (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && !target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- return IsHostileTo(target) || target->IsHostileTo(this);
-
- // PvP, PvC, CvP case
- // can't attack friendly targets
- if (IsFriendlyTo(target) || target->IsFriendlyTo(this))
- return false;
-
- Player const* playerAffectingAttacker = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
- Player const* playerAffectingTarget = target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
-
- // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
- if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget))
- {
- Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
- Unit const* creature = playerAffectingAttacker ? target : this;
-
- if (creature->IsContestedGuard() && player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
- return true;
-
- if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
- {
- if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
- if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction))
- if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
- if (!repState->Flags.HasFlag(ReputationFlags::AtWar))
- return false;
-
- }
- }
-
- Creature const* creatureAttacker = ToCreature();
- if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))
- return false;
-
- if (spellCheck && !IsValidSpellAttackTarget(target, bySpell, obj))
- return false;
-
- return true;
-}
-
-bool Unit::IsValidSpellAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj /*= nullptr*/) const
-{
- // can't attack unattackable units
- if (target->HasUnitState(UNIT_STATE_UNATTACKABLE))
- return false;
-
- // visibility checks
- // skip visibility check for GO casts, needs removal when go cast is implemented. Also ignore for gameobject and dynauras
- if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT)))
- {
- // can't attack invisible
- if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE))
- {
- if (obj && !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))
- return false;
- else if (!obj)
- {
- // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player
- bool const ignoreStealthCheck = (bySpell && bySpell->IsAffectingArea()) ||
- (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && IsInCombatWith(target));
-
- if (!CanSeeOrDetect(target, ignoreStealthCheck))
- return false;
- }
- }
- }
-
- // can't attack dead
- if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive())
- return false;
-
- // can't attack untargetable
- if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE))
- && target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
- return false;
-
- if (Player const* playerAttacker = ToPlayer())
- {
- if (playerAttacker->HasPlayerFlag(PLAYER_FLAGS_UBER))
- return false;
- }
-
- // check flags
- if (target->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)))
- return false;
-
- if (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToNPC())
- return false;
-
- if (!target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToNPC())
- return false;
-
- if ((!bySpell || bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
- && HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToPC())
- return false;
-
- // check if this is a world trigger cast - GOs are using world triggers to cast their spells, so we need to ignore their immunity flag here, this is a temp workaround, needs removal when go cast is implemented properly
- if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToPC())
- if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT)))
- return false;
-
-
- // check duel - before sanctuary checks
- Player const* playerAffectingAttacker = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
- Player const* playerAffectingTarget = target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
- if (playerAffectingAttacker && playerAffectingTarget)
- if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
- return true;
-
- // PvP case - can't attack when attacker or target are in sanctuary
- // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
- if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && (target->IsInSanctuary() || IsInSanctuary()))
- return false;
-
- // additional checks - only PvP case
- if (playerAffectingAttacker && playerAffectingTarget)
- {
- if (target->IsPvP())
- return true;
-
- if (IsFFAPvP() && target->IsFFAPvP())
- return true;
-
- return HasPvpFlag(UNIT_BYTE2_FLAG_UNK1)
- || target->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1);
- }
-
- return true;
-}
-
-// function based on function Unit::CanAssist from 13850 client
-bool Unit::IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const
-{
- ASSERT(target);
-
- // can assist to self
- if (this == target)
- return true;
-
- // can't assist GMs
- if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster())
- return false;
-
- // can't assist non-friendly targets
- if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)))
- return false;
-
- if (spellCheck && !IsValidSpellAssistTarget(target, bySpell))
- return false;
-
- return true;
-}
-
-bool Unit::IsValidSpellAssistTarget(Unit const* target, SpellInfo const* bySpell) const
-{
- // can't assist unattackable units
- if (target->HasUnitState(UNIT_STATE_UNATTACKABLE))
- return false;
-
- // can't assist own vehicle or passenger
- if (m_vehicle)
- {
- if (IsOnVehicle(target))
- return false;
-
- if (m_vehicle->GetBase()->IsOnVehicle(target))
- return false;
- }
-
- // can't assist invisible
- if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))
- return false;
-
- // can't assist dead
- if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive())
- return false;
-
- // can't assist untargetable
- if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
- return false;
-
- if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
- {
- if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- {
- if (target->IsImmuneToPC())
- return false;
- }
- else
- {
- if (target->IsImmuneToNPC())
- return false;
- }
- }
-
- // PvP case
- if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- {
- Player const* targetPlayerOwner = target->GetAffectingPlayer();
- if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- {
- Player const* selfPlayerOwner = GetAffectingPlayer();
- if (selfPlayerOwner && targetPlayerOwner)
- {
- // can't assist player which is dueling someone
- if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner->duel)
- return false;
- }
- // can't assist player in ffa_pvp zone from outside
- if (target->IsFFAPvP() && !IsFFAPvP())
- return false;
-
- // can't assist player out of sanctuary from sanctuary if has pvp enabled
- if (target->IsPvP())
- if (IsInSanctuary() && !target->IsInSanctuary())
- return false;
- }
- }
- // PvC case - player can assist creature only if has specific type flags
- // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) &&
- else if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
- {
- if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
- if (!target->IsPvP())
- if (Creature const* creatureTarget = target->ToCreature())
- return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST;
- }
-
- return true;
-}
-
int64 Unit::ModifyHealth(int64 dVal)
{
int64 gain = 0;
@@ -8998,175 +8328,6 @@ void Unit::UpdatePetCombatState()
//======================================================================
-float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const
-{
- if (Player* modOwner = GetSpellModOwner())
- {
- modOwner->ApplySpellMod(spellProto, SpellModOp::Points, value);
- switch (effect_index)
- {
- case EFFECT_0:
- modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex0, value);
- break;
- case EFFECT_1:
- modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex1, value);
- break;
- case EFFECT_2:
- modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex2, value);
- break;
- case EFFECT_3:
- modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex3, value);
- break;
- case EFFECT_4:
- modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex4, value);
- break;
- }
- }
- return value;
-}
-
-// function uses real base points (typically value - 1)
-int32 Unit::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const
-{
- SpellEffectInfo const* effect = spellProto->GetEffect(effect_index);
- if (variance)
- *variance = 0.0f;
-
- return effect ? effect->CalcValue(this, basePoints, target, variance, castItemId, itemLevel) : 0;
-}
-
-int32 Unit::CalcSpellDuration(SpellInfo const* spellProto)
-{
- uint32 comboPoints = m_playerMovingMe ? m_playerMovingMe->GetComboPoints() : 0;
-
- int32 minduration = spellProto->GetDuration();
- int32 maxduration = spellProto->GetMaxDuration();
-
- int32 duration;
-
- if (comboPoints && minduration != -1 && minduration != maxduration)
- duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
- else
- duration = minduration;
-
- return duration;
-}
-
-int32 Unit::ModSpellDuration(SpellInfo const* spellProto, Unit const* target, int32 duration, bool positive, uint32 effectMask)
-{
- // don't mod permanent auras duration
- if (duration < 0)
- return duration;
-
- // some auras are not affected by duration modifiers
- if (spellProto->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS))
- return duration;
-
- // cut duration only of negative effects
- if (!positive)
- {
- int32 mechanic = spellProto->GetSpellMechanicMaskByEffectMask(effectMask);
-
- int32 durationMod;
- int32 durationMod_always = 0;
- int32 durationMod_not_stack = 0;
-
- for (uint8 i = 1; i <= MECHANIC_ENRAGED; ++i)
- {
- if (!(mechanic & 1<<i))
- continue;
- // Find total mod value (negative bonus)
- int32 new_durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, i);
- // Find max mod (negative bonus)
- int32 new_durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, i);
- // Check if mods applied before were weaker
- if (new_durationMod_always < durationMod_always)
- durationMod_always = new_durationMod_always;
- if (new_durationMod_not_stack < durationMod_not_stack)
- durationMod_not_stack = new_durationMod_not_stack;
- }
-
- // Select strongest negative mod
- if (durationMod_always > durationMod_not_stack)
- durationMod = durationMod_not_stack;
- else
- durationMod = durationMod_always;
-
- if (durationMod != 0)
- AddPct(duration, durationMod);
-
- // there are only negative mods currently
- durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellProto->Dispel);
- durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK, spellProto->Dispel);
-
- durationMod = 0;
- if (durationMod_always > durationMod_not_stack)
- durationMod += durationMod_not_stack;
- else
- durationMod += durationMod_always;
-
- if (durationMod != 0)
- AddPct(duration, durationMod);
- }
- else
- {
- // else positive mods here, there are no currently
- // when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue
-
- // Mixology - duration boost
- if (target->GetTypeId() == TYPEID_PLAYER)
- {
- if (spellProto->SpellFamilyName == SPELLFAMILY_POTION && (
- sSpellMgr->IsSpellMemberOfSpellGroup(spellProto->Id, SPELL_GROUP_ELIXIR_BATTLE) ||
- sSpellMgr->IsSpellMemberOfSpellGroup(spellProto->Id, SPELL_GROUP_ELIXIR_GUARDIAN)))
- {
- SpellEffectInfo const* effect = spellProto->GetEffect(EFFECT_0);
- if (target->HasAura(53042) && effect && target->HasSpell(effect->TriggerSpell))
- duration *= 2;
- }
- }
- }
-
- return std::max(duration, 0);
-}
-
-void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* spell)
-{
- if (!spellInfo || castTime < 0)
- return;
-
- // called from caster
- if (Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, castTime, spell);
-
- if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) &&
- ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT))
- castTime = CanInstantCast() ? 0 : int32(float(castTime) * m_unitData->ModCastingSpeed);
- else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG))
- castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]);
- else if (IsPartOfSkillLine(SKILL_COOKING, spellInfo->Id) && HasAura(67556)) // cooking with Chef Hat.
- castTime = 500;
-}
-
-void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32& duration, Spell* spell)
-{
- if (!spellInfo || duration < 0)
- return;
-
- if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
- return;
-
- // called from caster
- if (Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, duration, spell);
-
- if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) &&
- ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT))
- duration = int32(float(duration) * m_unitData->ModCastingSpeed);
- else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG))
- duration = int32(float(duration) * m_modAttackSpeedPct[RANGED_ATTACK]);
-}
-
DiminishingLevels Unit::GetDiminishing(DiminishingGroup group) const
{
DiminishingReturn const& diminish = m_Diminishing[group];
@@ -9191,7 +8352,7 @@ void Unit::IncrDiminishing(SpellInfo const* auraSpellInfo)
diminish.hitCount = currentLevel + 1;
}
-bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) const
+bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, WorldObject* caster, DiminishingLevels previousLevel) const
{
DiminishingGroup const group = auraSpellInfo->GetDiminishingReturnsGroupForSpell();
if (duration == -1 || group == DIMINISHING_NONE)
@@ -9206,7 +8367,7 @@ bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& dur
if (limitDuration > 0 && duration > limitDuration)
{
Unit const* target = targetOwner ? targetOwner : this;
- Unit const* source = casterOwner ? casterOwner : caster;
+ WorldObject const* source = casterOwner ? casterOwner : caster;
if (target->IsAffectedByDiminishingReturns() && source->GetTypeId() == TYPEID_PLAYER)
duration = limitDuration;
@@ -9295,28 +8456,6 @@ void Unit::ClearDiminishings()
dim.Clear();
}
-float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
-{
- if (!spellInfo->RangeEntry)
- return 0;
- if (spellInfo->RangeEntry->RangeMax[0] == spellInfo->RangeEntry->RangeMax[1])
- return spellInfo->GetMaxRange();
- if (!target)
- return spellInfo->GetMaxRange(true);
- return spellInfo->GetMaxRange(!IsHostileTo(target));
-}
-
-float Unit::GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
-{
- if (!spellInfo->RangeEntry)
- return 0;
- if (spellInfo->RangeEntry->RangeMin[0] == spellInfo->RangeEntry->RangeMin[1])
- return spellInfo->GetMinRange();
- if (!target)
- return spellInfo->GetMinRange(true);
- return spellInfo->GetMinRange(!IsHostileTo(target));
-}
-
uint32 Unit::GetCreatureType() const
{
if (GetTypeId() == TYPEID_PLAYER)
@@ -10626,28 +9765,6 @@ ObjectGuid Unit::GetCharmerOrOwnerGUID() const
return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID();
}
-ObjectGuid Unit::GetCharmerOrOwnerOrOwnGUID() const
-{
- ObjectGuid guid = GetCharmerOrOwnerGUID();
- if (!guid.IsEmpty())
- return guid;
-
- return GetGUID();
-}
-
-Player* Unit::GetSpellModOwner() const
-{
- if (Player* player = const_cast<Unit*>(this)->ToPlayer())
- return player;
-
- if (HasUnitTypeMask(UNIT_MASK_PET | UNIT_MASK_TOTEM | UNIT_MASK_GUARDIAN))
- {
- if (Unit* owner = GetOwner())
- return owner->ToPlayer();
- }
- return nullptr;
-}
-
///----------Pet responses methods-----------------
void Unit::SendPetActionFeedback(PetActionFeedback msg, uint32 spellId)
{
@@ -10847,6 +9964,45 @@ void Unit::RestoreDisplayId(bool ignorePositiveAurasPreventingMounting /*= false
SetDisplayId(GetNativeDisplayId());
}
+void Unit::AddComboPoints(int8 count, Spell* spell)
+{
+ if (!count)
+ return;
+
+ int8 comboPoints = spell ? spell->m_comboPointGain : GetPower(POWER_COMBO_POINTS);
+
+ comboPoints += count;
+
+ if (comboPoints > 5)
+ comboPoints = 5;
+ else if (comboPoints < 0)
+ comboPoints = 0;
+
+ if (!spell)
+ SetPower(POWER_COMBO_POINTS, comboPoints);
+ else
+ spell->m_comboPointGain = comboPoints;
+}
+
+void Unit::GainSpellComboPoints(int8 count)
+{
+ if (!count)
+ return;
+
+ int8 cp = GetPower(POWER_COMBO_POINTS);
+
+ cp += count;
+ if (cp > 5) cp = 5;
+ else if (cp < 0) cp = 0;
+
+ SetPower(POWER_COMBO_POINTS, cp);
+}
+
+void Unit::ClearComboPoints()
+{
+ SetPower(POWER_COMBO_POINTS, 0);
+}
+
void Unit::ClearAllReactives()
{
for (uint8 i = 0; i < MAX_REACTIVE; ++i)
@@ -12289,9 +11445,6 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target)
if (!spellInfo)
return nullptr;
- if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD))
- return nullptr;
-
return AddAura(spellInfo, MAX_EFFECT_MASK, target);
}
@@ -12300,6 +11453,9 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target)
if (!spellInfo)
return nullptr;
+ if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD))
+ return nullptr;
+
if (target->IsImmunedToSpell(spellInfo, this))
return nullptr;
@@ -14213,37 +13369,7 @@ uint32 Unit::GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const
}
}
- return spellInfo->GetSpellXSpellVisualId(this);
-}
-
-struct CombatLogSender
-{
- WorldPackets::CombatLog::CombatLogServerPacket const* i_message;
-
- explicit CombatLogSender(WorldPackets::CombatLog::CombatLogServerPacket* msg)
- : i_message(msg)
- {
- msg->Write();
- }
-
- void operator()(Player const* player) const
- {
- if (player->IsAdvancedCombatLoggingEnabled())
- player->SendDirectMessage(i_message->GetFullLogPacket());
- else
- player->SendDirectMessage(i_message->GetBasicLogPacket());
- }
-};
-
-void Unit::SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const
-{
- CombatLogSender combatLogSender(combatLog);
-
- if (Player const* self = ToPlayer())
- combatLogSender(self);
-
- Trinity::MessageDistDeliverer<CombatLogSender> notifier(this, combatLogSender, GetVisibilityRange());
- Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
+ return WorldObject::GetCastSpellXSpellVisualId(spellInfo);
}
bool Unit::VisibleAuraSlotCompare::operator()(AuraApplication* left, AuraApplication* right) const
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index ff5c76a1bb5..3e5614ff30a 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -19,13 +19,11 @@
#define __UNIT_H
#include "Object.h"
-#include "EventProcessor.h"
#include "FollowerReference.h"
#include "FollowerRefManager.h"
#include "CombatManager.h"
#include "OptionalFwd.h"
#include "SpellAuraDefines.h"
-#include "SpellDefines.h"
#include "ThreatManager.h"
#include "Timer.h"
#include "UnitDefines.h"
@@ -105,13 +103,6 @@ namespace Movement
class MoveSpline;
struct SpellEffectExtraData;
}
-namespace WorldPackets
-{
- namespace CombatLog
- {
- class CombatLogServerPacket;
- }
-}
typedef std::list<Unit*> UnitList;
@@ -383,15 +374,15 @@ enum MeleeHitOutcome : uint8
class DispelInfo
{
public:
- explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) :
- _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { }
+ explicit DispelInfo(WorldObject* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) :
+ _dispeller(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { }
- Unit* GetDispeller() const { return _dispellerUnit; }
+ WorldObject* GetDispeller() const { return _dispeller; }
uint32 GetDispellerSpellId() const { return _dispellerSpell; }
uint8 GetRemovedCharges() const { return _chargesRemoved; }
void SetRemovedCharges(uint8 amount) { _chargesRemoved = amount; }
private:
- Unit* _dispellerUnit;
+ WorldObject* _dispeller;
uint32 _dispellerSpell;
uint8 _chargesRemoved;
};
@@ -781,19 +772,13 @@ class TC_GAME_API Unit : public WorldObject
void CleanupBeforeRemoveFromMap(bool finalCleanup);
void CleanupsBeforeDelete(bool finalCleanup = true) override; // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units)
- void SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const;
-
virtual bool IsAffectedByDiminishingReturns() const { return (GetCharmerOrOwnerPlayerOrPlayerItself() != nullptr); }
DiminishingLevels GetDiminishing(DiminishingGroup group) const;
void IncrDiminishing(SpellInfo const* auraSpellInfo);
- bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) const;
+ bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, WorldObject* caster, DiminishingLevels previousLevel) const;
void ApplyDiminishingAura(DiminishingGroup group, bool apply);
void ClearDiminishings();
- // target dependent range checks
- float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
- float GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const;
-
virtual void Update(uint32 time) override;
void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; }
@@ -881,7 +866,7 @@ class TC_GAME_API Unit : public WorldObject
int32 GetResistance(SpellSchoolMask mask) const;
void SetResistance(SpellSchools school, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Resistances, school), val); }
void SetBonusResistanceMod(SpellSchools school, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BonusResistanceMods, school), val); }
- static float CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr);
+ static float CalculateAverageResistReduction(WorldObject const* caster, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr);
uint64 GetHealth() const { return m_unitData->Health; }
uint64 GetMaxHealth() const { return m_unitData->MaxHealth; }
@@ -959,17 +944,9 @@ class TC_GAME_API Unit : public WorldObject
void SetSheath(SheathState sheathed);
// faction template id
- uint32 GetFaction() const { return m_unitData->FactionTemplate; }
- void SetFaction(uint32 faction) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FactionTemplate), faction); }
- FactionTemplateEntry const* GetFactionTemplateEntry() const;
-
- ReputationRank GetReactionTo(Unit const* target) const;
- ReputationRank static GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target);
+ uint32 GetFaction() const override { return m_unitData->FactionTemplate; }
+ void SetFaction(uint32 faction) override { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FactionTemplate), faction); }
- bool IsHostileTo(Unit const* unit) const;
- bool IsHostileToPlayers() const;
- bool IsFriendlyTo(Unit const* unit) const;
- bool IsNeutralToAll() const;
bool IsInPartyWith(Unit const* unit) const;
bool IsInRaidWith(Unit const* unit) const;
void GetPartyMembers(std::list<Unit*> &units);
@@ -1057,10 +1034,8 @@ class TC_GAME_API Unit : public WorldObject
int32 CalculateAOEAvoidance(int32 damage, uint32 schoolMask, ObjectGuid const& casterGuid) const;
- float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const;
- SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const;
- SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const;
- SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false);
+ float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const override;
+ SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const override;
float GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const;
float GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const;
@@ -1161,12 +1136,6 @@ class TC_GAME_API Unit : public WorldObject
bool isTargetableForAttack(bool checkFakeDeath = true) const;
- bool IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell = nullptr, WorldObject const* obj = nullptr, bool spellCheck = true) const;
- bool IsValidSpellAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj = nullptr) const;
-
- bool IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const;
- bool IsValidSpellAssistTarget(Unit const* target, SpellInfo const* bySpell) const;
-
virtual bool IsInWater() const;
virtual bool IsUnderWater() const;
bool isInAccessiblePlaceFor(Creature const* c) const;
@@ -1176,10 +1145,6 @@ class TC_GAME_API Unit : public WorldObject
void SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, int32 overEnergize, Powers powerType);
void EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType);
- // CastSpell's third arg can be a variety of things - check out CastSpellExtraArgs' constructors!
- void CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args = {});
- void CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args = {});
- void CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args = {});
Aura* AddAura(uint32 spellId, Unit* target);
Aura* AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target);
void SetAuraStack(uint32 spellId, Unit* target, uint32 stack);
@@ -1256,7 +1221,7 @@ class TC_GAME_API Unit : public WorldObject
DeathState getDeathState() const { return m_deathState; }
virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet
- ObjectGuid GetOwnerGUID() const { return m_unitData->SummonedBy; }
+ ObjectGuid GetOwnerGUID() const override { return m_unitData->SummonedBy; }
void SetOwnerGUID(ObjectGuid owner);
ObjectGuid GetCreatorGUID() const { return m_unitData->CreatedBy; }
void SetCreatorGUID(ObjectGuid creator) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::CreatedBy), creator); }
@@ -1276,21 +1241,14 @@ class TC_GAME_API Unit : public WorldObject
void SetDemonCreatorGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::DemonCreator), guid); }
bool IsControlledByPlayer() const { return m_ControlledByPlayer; }
- ObjectGuid GetCharmerOrOwnerGUID() const;
- ObjectGuid GetCharmerOrOwnerOrOwnGUID() const;
+ ObjectGuid GetCharmerOrOwnerGUID() const override;
bool IsCharmedOwnedByPlayerOrPlayer() const { return GetCharmerOrOwnerOrOwnGUID().IsPlayer(); }
- Player* GetSpellModOwner() const;
-
- Unit* GetOwner() const;
Guardian* GetGuardianPet() const;
Minion* GetFirstMinion() const;
Unit* GetCharmer() const;
Unit* GetCharm() const;
Unit* GetCharmerOrOwner() const;
- Unit* GetCharmerOrOwnerOrSelf() const;
- Player* GetCharmerOrOwnerPlayerOrPlayerItself() const;
- Player* GetAffectingPlayer() const;
void SetMinion(Minion *minion, bool apply);
void GetAllMinionsByEntry(std::list<TempSummon*>& Minions, uint32 entry);
@@ -1378,8 +1336,8 @@ class TC_GAME_API Unit : public WorldObject
void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
void RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT, uint16 num = 1);
- void RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, Unit* dispeller, uint8 chargesRemoved = 1);
- void RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, Unit* stealer);
+ void RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, WorldObject* dispeller, uint8 chargesRemoved = 1);
+ void RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, WorldObject* stealer);
void RemoveAurasDueToItemSpell(uint32 spellId, ObjectGuid castItemGuid);
void RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID = ObjectGuid::Empty, Aura* except = nullptr, bool negative = true, bool positive = true);
void RemoveNotOwnSingleTargetAuras(bool onPhaseChange = false);
@@ -1427,7 +1385,7 @@ class TC_GAME_API Unit : public WorldObject
AuraApplication* GetAuraApplicationOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0, AuraApplication* except = nullptr) const;
Aura* GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0) const;
- void GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect = false) const;
+ void GetDispellableAuraList(WorldObject const* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect = false) const;
bool HasAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster = ObjectGuid::Empty) const;
uint32 GetAuraCount(uint32 spellId) const;
@@ -1530,7 +1488,7 @@ class TC_GAME_API Unit : public WorldObject
Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
int32 GetCurrentSpellCastTime(uint32 spell_id) const;
virtual SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const;
- uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const;
+ uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const override;
virtual bool IsFocusing(Spell const* /*focusSpell*/ = nullptr, bool /*withDelay*/ = false) { return false; }
virtual bool IsMovementPreventedByCasting() const;
@@ -1559,9 +1517,6 @@ class TC_GAME_API Unit : public WorldObject
float m_modAttackSpeedPct[MAX_ATTACK];
uint32 m_attackTimer[MAX_ATTACK];
- // Event handler
- EventProcessor m_Events;
-
// stat system
void HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply);
void ApplyStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float amount);
@@ -1688,7 +1643,6 @@ class TC_GAME_API Unit : public WorldObject
bool HasAuraState(AuraStateType flag, SpellInfo const* spellProto = nullptr, Unit const* Caster = nullptr) const;
void UnsummonAllTotems();
bool IsMagnet() const;
- Unit* GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo);
Unit* GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo = nullptr);
int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const;
@@ -1716,14 +1670,14 @@ class TC_GAME_API Unit : public WorldObject
uint32 GetRemainingPeriodicAmount(ObjectGuid caster, uint32 spellId, AuraType auraType, uint8 effectIndex = 0) const;
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply);
- virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const; // redefined in Creature
+ virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const;
uint32 GetSchoolImmunityMask() const;
uint32 GetDamageImmunityMask() const;
uint32 GetMechanicImmunityMask() const;
bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const;
bool IsImmunedToDamage(SpellInfo const* spellInfo) const;
- virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const; // redefined in Creature
+ virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const;
static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = nullptr, int8 effIndex = -1);
static uint32 CalcArmorReducedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK, uint8 attackerLevel = 0);
@@ -1737,13 +1691,6 @@ class TC_GAME_API Unit : public WorldObject
void SetSpeed(UnitMoveType mtype, float newValue);
void SetSpeedRate(UnitMoveType mtype, float rate);
- float ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const;
- int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const;
- int32 CalcSpellDuration(SpellInfo const* spellProto);
- int32 ModSpellDuration(SpellInfo const* spellProto, Unit const* target, int32 duration, bool positive, uint32 effectMask);
- void ModSpellCastTime(SpellInfo const* spellProto, int32& castTime, Spell* spell = nullptr);
- void ModSpellDurationTime(SpellInfo const* spellProto, int32& castTime, Spell* spell = nullptr);
-
void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); }
void removeFollower(FollowerReference* /*pRef*/) { /* nothing to do yet */ }
@@ -1771,6 +1718,12 @@ class TC_GAME_API Unit : public WorldObject
void SetControlled(bool apply, UnitState state);
void ApplyControlStatesIfNeeded();
+ ///-----------Combo point system-------------------
+ uint32 GetComboPoints() const { return uint32(GetPower(POWER_COMBO_POINTS)); }
+ void AddComboPoints(int8 count, Spell* spell = nullptr);
+ void GainSpellComboPoints(int8 count);
+ void ClearComboPoints();
+
///----------Pet responses methods-----------------
void SendPetActionFeedback(PetActionFeedback msg, uint32 spellId);
void SendPetTalk(uint32 pettalk);