diff options
| author | ariel- <ariel-@users.noreply.github.com> | 2018-02-10 16:43:01 -0300 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2021-08-28 15:59:11 +0200 |
| commit | 962f6d7988b9003e550f6745be7cff812e9d8efa (patch) | |
| tree | ee6ab5872b947afb00f4ca99e87c7dddea35bdb3 /src/server/game/Entities | |
| parent | 65dca120d34febdaa84a63e17f638ab0fa59b3df (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')
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); |
