diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp
index 4555f83c8..46902575f 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp
@@ -15,6 +15,7 @@
* with this program. If not, see .
*/
+#include "SpellAuraEffects.h"
#include "GameTime.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
@@ -26,7 +27,7 @@
#include "World.h"
DynamicObject::DynamicObject(bool isWorldObject) : WorldObject(isWorldObject), MovableMapObject(),
- _aura(nullptr), _removedAura(nullptr), _caster(nullptr), _duration(0), _isViewpoint(false)
+ _aura(nullptr), _removedAura(nullptr), _caster(nullptr), _duration(0), _isViewpoint(false), _updateViewerVisibilityTimer(0)
{
m_objectType |= TYPEMASK_DYNAMICOBJECT;
m_objectTypeId = TYPEID_DYNAMICOBJECT;
@@ -125,15 +126,17 @@ bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caste
SetFloatValue(DYNAMICOBJECT_RADIUS, radius);
SetUInt32Value(DYNAMICOBJECT_CASTTIME, GameTime::GetGameTimeMS().count());
- if (IsWorldObject())
- setActive(true); //must before add to map to be put in world container
-
if (!GetMap()->AddToMap(this, true))
{
// Returning false will cause the object to be deleted - remove from transport
return false;
}
+ if (IsWorldObject())
+ {
+ setActive(true);
+ }
+
return true;
}
@@ -165,7 +168,22 @@ void DynamicObject::Update(uint32 p_time)
if (expired)
Remove();
else
+ {
+ if (_updateViewerVisibilityTimer)
+ {
+ if (_updateViewerVisibilityTimer <= p_time)
+ {
+ _updateViewerVisibilityTimer = 0;
+
+ if (Player* playerCaster = _caster->ToPlayer())
+ playerCaster->UpdateVisibilityForPlayer();
+ }
+ else
+ _updateViewerVisibilityTimer -= p_time;
+ }
+
sScriptMgr->OnDynamicObjectUpdate(this, p_time);
+ }
}
void DynamicObject::Remove()
@@ -214,13 +232,22 @@ void DynamicObject::RemoveAura()
_removedAura->_Remove(AURA_REMOVE_BY_DEFAULT);
}
-void DynamicObject::SetCasterViewpoint()
+void DynamicObject::SetCasterViewpoint(bool updateViewerVisibility)
{
if (Player* caster = _caster->ToPlayer())
{
+ // Remove old farsight viewpoint
+ if (Unit* farsightObject = ObjectAccessor::GetUnit(*caster, caster->GetGuidValue(PLAYER_FARSIGHT)))
+ {
+ _oldFarsightGUID = caster->GetGuidValue(PLAYER_FARSIGHT);
+ caster->SetViewpoint(farsightObject, false);
+ }
+
caster->SetViewpoint(this, true);
_isViewpoint = true;
}
+
+ _updateViewerVisibilityTimer = updateViewerVisibility ? 100 : 0;
}
void DynamicObject::RemoveCasterViewpoint()
@@ -229,6 +256,13 @@ void DynamicObject::RemoveCasterViewpoint()
{
caster->SetViewpoint(this, false);
_isViewpoint = false;
+
+ // Restore prev farsight viewpoint
+ if (Unit* farsightObject = ObjectAccessor::GetUnit(*caster, _oldFarsightGUID))
+ {
+ caster->SetViewpoint(farsightObject, true);
+ }
+ _oldFarsightGUID.Clear();
}
}
diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h
index 90d89c060..f7cef0326 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.h
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.h
@@ -50,7 +50,7 @@ public:
void Delay(int32 delaytime);
void SetAura(Aura* aura);
void RemoveAura();
- void SetCasterViewpoint();
+ void SetCasterViewpoint(bool updateViewerVisibility);
void RemoveCasterViewpoint();
[[nodiscard]] Unit* GetCaster() const { return _caster; }
void BindToCaster();
@@ -60,11 +60,15 @@ public:
[[nodiscard]] float GetRadius() const { return GetFloatValue(DYNAMICOBJECT_RADIUS); }
[[nodiscard]] bool IsViewpoint() const { return _isViewpoint; }
+ ObjectGuid const& GetOldFarsightGUID() const { return _oldFarsightGUID; }
+
protected:
Aura* _aura;
Aura* _removedAura;
Unit* _caster;
int32 _duration; // for non-aura dynobjects
bool _isViewpoint;
+ uint32 _updateViewerVisibilityTimer;
+ ObjectGuid _oldFarsightGUID;
};
#endif
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 5d07919a0..023632c0f 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -65,7 +65,7 @@ constexpr float VisibilityDistances[AsUnderlyingType(VisibilityDistanceType::Max
VISIBILITY_DISTANCE_SMALL,
VISIBILITY_DISTANCE_LARGE,
VISIBILITY_DISTANCE_GIGANTIC,
- MAX_VISIBILITY_DISTANCE
+ VISIBILITY_DISTANCE_INFINITE
};
Object::Object() : m_PackGUID(sizeof(uint64) + 1)
@@ -1631,7 +1631,7 @@ float WorldObject::GetGridActivationRange() const
{
return ToCreature()->m_SightDistance;
}
- else if (GetTypeId() == TYPEID_GAMEOBJECT && ToGameObject()->IsTransport() && isActiveObject())
+ else if (((GetTypeId() == TYPEID_GAMEOBJECT && ToGameObject()->IsTransport()) || GetTypeId() == TYPEID_DYNAMICOBJECT) && isActiveObject())
{
return GetMap()->GetVisibilityRange();
}
@@ -1643,7 +1643,7 @@ float WorldObject::GetVisibilityRange() const
{
if (IsVisibilityOverridden() && GetTypeId() == TYPEID_UNIT)
{
- return MAX_VISIBILITY_DISTANCE;
+ return *m_visibilityDistanceOverride;
}
else if (GetTypeId() == TYPEID_GAMEOBJECT)
{
@@ -1654,7 +1654,7 @@ float WorldObject::GetVisibilityRange() const
}
else if (IsVisibilityOverridden())
{
- return MAX_VISIBILITY_DISTANCE;
+ return *m_visibilityDistanceOverride;
}
else
{
@@ -1676,7 +1676,7 @@ float WorldObject::GetSightRange(WorldObject const* target) const
{
if (target->IsVisibilityOverridden() && target->GetTypeId() == TYPEID_UNIT)
{
- return MAX_VISIBILITY_DISTANCE;
+ return *target->m_visibilityDistanceOverride;
}
else if (target->GetTypeId() == TYPEID_GAMEOBJECT)
{
@@ -1686,7 +1686,7 @@ float WorldObject::GetSightRange(WorldObject const* target) const
}
else if (target->IsVisibilityOverridden())
{
- return MAX_VISIBILITY_DISTANCE;
+ return *target->m_visibilityDistanceOverride;
}
else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
{
diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h
index b578b5e6a..2dbd40426 100644
--- a/src/server/game/Entities/Object/ObjectDefines.h
+++ b/src/server/game/Entities/Object/ObjectDefines.h
@@ -31,6 +31,7 @@
#define MAX_VISIBILITY_DISTANCE 250.0f // max distance for visible objects, experimental
#define SIGHT_RANGE_UNIT 50.0f
#define MAX_SEARCHER_DISTANCE 150.0f // pussywizard: replace the use of MAX_VISIBILITY_DISTANCE in searchers, because MAX_VISIBILITY_DISTANCE is quite too big for this purpose
+#define VISIBILITY_DISTANCE_INFINITE 533.0f
#define VISIBILITY_DISTANCE_GIGANTIC 400.0f
#define VISIBILITY_DISTANCE_LARGE 200.0f
#define VISIBILITY_DISTANCE_NORMAL 100.0f
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 603b3d508..b582ecdd5 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -12818,7 +12818,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
if (!AddGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
{
- LOG_FATAL("entities.player", "Player::CreateViewpoint: Player {} cannot add new viewpoint!", GetName());
+ LOG_DEBUG("entities.player", "Player::CreateViewpoint: Player {} cannot add new viewpoint!", GetName());
return;
}
@@ -12838,7 +12838,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
if (!RemoveGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
{
- LOG_FATAL("entities.player", "Player::CreateViewpoint: Player {} cannot remove current viewpoint!", GetName());
+ LOG_DEBUG("entities.player", "Player::CreateViewpoint: Player {} cannot remove current viewpoint!", GetName());
return;
}
diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp
index e487c3f50..b41974cc5 100644
--- a/src/server/game/Entities/Totem/Totem.cpp
+++ b/src/server/game/Entities/Totem/Totem.cpp
@@ -97,6 +97,10 @@ void Totem::InitSummon()
{
SetReactState(REACT_AGGRESSIVE);
GetOwner()->CastSpell(this, 6277, true);
+
+ // Farsight objects should be active
+ setActive(true);
+ SetVisibilityDistanceOverride(VisibilityDistanceType::Infinite);
}
if (!IsInWater())
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 790544196..a07cc3f09 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -3604,8 +3604,10 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell)
if (pSpell == m_currentSpells[CSpellType]) // avoid breaking self
return;
+ bool bySelf = m_currentSpells[CSpellType] && m_currentSpells[CSpellType]->m_spellInfo->Id == pSpell->m_spellInfo->Id;
+
// break same type spell if it is not delayed
- InterruptSpell(CSpellType, false);
+ InterruptSpell(CSpellType, false, true, bySelf);
// special breakage effects:
switch (CSpellType)
@@ -3634,7 +3636,7 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell)
{
// channel spells always break generic non-delayed and any channeled spells
InterruptSpell(CURRENT_GENERIC_SPELL, false);
- InterruptSpell(CURRENT_CHANNELED_SPELL);
+ InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, bySelf);
// it also does break autorepeat if not Auto Shot
if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] &&
@@ -5728,10 +5730,12 @@ DynamicObject* Unit::GetDynObject(uint32 spellId)
return nullptr;
}
-void Unit::RemoveDynObject(uint32 spellId)
+bool Unit::RemoveDynObject(uint32 spellId)
{
if (m_dynObj.empty())
- return;
+ return false;
+
+ bool result = false;
for (DynObjectList::iterator i = m_dynObj.begin(); i != m_dynObj.end();)
{
DynamicObject* dynObj = *i;
@@ -5739,10 +5743,13 @@ void Unit::RemoveDynObject(uint32 spellId)
{
dynObj->Remove();
i = m_dynObj.begin();
+ result = true;
}
else
++i;
}
+
+ return result;
}
void Unit::RemoveAllDynObjects()
@@ -9922,7 +9929,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
}
// switch target
- InterruptSpell(CURRENT_MELEE_SPELL);
+ InterruptSpell(CURRENT_MELEE_SPELL, true, true, true);
if (!meleeAttack)
ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 082950ffc..8beeb55a5 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -2140,7 +2140,7 @@ public:
void _RegisterDynObject(DynamicObject* dynObj);
void _UnregisterDynObject(DynamicObject* dynObj);
DynamicObject* GetDynObject(uint32 spellId);
- void RemoveDynObject(uint32 spellId);
+ bool RemoveDynObject(uint32 spellId);
void RemoveAllDynObjects();
[[nodiscard]] GameObject* GetGameObject(uint32 spellId) const;
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 2719d2f88..973cee758 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -1225,14 +1225,37 @@ void WorldSession::HandleFarSightOpcode(WorldPacket& recvData)
if (WorldObject* target = _player->GetViewpoint())
_player->SetSeer(target);
else
- {
LOG_DEBUG("network.opcode", "Player {} requests non-existing seer {}", _player->GetName(), _player->GetGuidValue(PLAYER_FARSIGHT).ToString());
- }
}
else
{
- LOG_DEBUG("network", "Player {} set vision to self", _player->GetGUID().ToString());
- _player->SetSeer(_player);
+ WorldObject* newFarsightobject = nullptr;
+ if (WorldObject* viewpoint = _player->GetViewpoint())
+ {
+ if (DynamicObject* viewpointDynamicObject = viewpoint->ToDynObject())
+ {
+ newFarsightobject = ObjectAccessor::GetUnit(*viewpointDynamicObject, viewpointDynamicObject->GetOldFarsightGUID());
+ }
+ else if (DynamicObject* viewpointDynamicObject = _player->GetDynObject(_player->GetUInt32Value(UNIT_CHANNEL_SPELL)))
+ {
+ if (viewpointDynamicObject->IsViewpoint() && viewpointDynamicObject->GetCasterGUID() == _player->GetGUID())
+ {
+ newFarsightobject = viewpointDynamicObject;
+ }
+ }
+ }
+
+ if (newFarsightobject)
+ {
+ LOG_DEBUG("network", "Player {} set vision to old farsight {}", _player->GetGUID().ToString(), newFarsightobject->GetGUID().ToString());
+ _player->SetViewpoint(_player->GetViewpoint(), false);
+ _player->SetViewpoint(newFarsightobject, true);
+ }
+ else
+ {
+ LOG_DEBUG("network", "Player {} set vision to self", _player->GetGUID().ToString());
+ _player->SetSeer(_player);
+ }
}
GetPlayer()->UpdateVisibilityForPlayer();
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 5f0c3cc97..be3d6c522 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -576,7 +576,7 @@ void WorldSession::HandleCancelChanneling(WorldPacket& recvData)
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
return;
- mover->InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, true);
+ mover->InterruptSpell(CURRENT_CHANNELED_SPELL);
}
void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& totemDestroyed)
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index dbc1db87e..751033fe0 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -3639,8 +3639,15 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
// skip triggered spell (item equip spell casting and other not explicit character casts/item uses)
if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth())
{
- m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST, 0, m_spellInfo->Id == 75);
- m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK, 0, m_spellInfo->Id == 75);
+ // Farsight spells exception
+ uint32 exceptSpellId = 0;
+ if (m_spellInfo->HasEffect(SPELL_EFFECT_ADD_FARSIGHT))
+ {
+ exceptSpellId = m_spellInfo->Id;
+ }
+
+ m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST, exceptSpellId, m_spellInfo->Id == 75);
+ m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK, exceptSpellId, m_spellInfo->Id == 75);
}
m_caster->SetCurrentCastedSpell(this);
@@ -3700,14 +3707,17 @@ void Spell::cancel(bool bySelf)
break;
case SPELL_STATE_CASTING:
- for (std::list::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
- if ((*ihit).missCondition == SPELL_MISS_NONE)
- if (Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID))
- unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL);
+ if (!bySelf)
+ {
+ for (std::list::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
+ if ((*ihit).missCondition == SPELL_MISS_NONE)
+ if (Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID))
+ unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL);
- SendChannelUpdate(0);
- SendInterrupted(0);
- SendCastResult(SPELL_FAILED_INTERRUPTED);
+ SendChannelUpdate(0);
+ SendInterrupted(0);
+ SendCastResult(SPELL_FAILED_INTERRUPTED);
+ }
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->NeedSendSpectatorData())
ArenaSpectator::SendCommand_Spell(m_caster->FindMap(), m_caster->GetGUID(), "SPE", m_spellInfo->Id, bySelf ? 99998 : 99999);
@@ -3718,7 +3728,6 @@ void Spell::cancel(bool bySelf)
m_appliedMods.clear();
break;
-
default:
break;
}
@@ -3727,7 +3736,12 @@ void Spell::cancel(bool bySelf)
if (m_selfContainer && *m_selfContainer == this)
*m_selfContainer = nullptr;
- m_caster->RemoveDynObject(m_spellInfo->Id);
+ // Do not remove current far sight object (already done in Spell::EffectAddFarsight) to prevent from reset viewpoint to player
+ if (!(bySelf && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_FARSIGHT)))
+ {
+ m_caster->RemoveDynObject(m_spellInfo->Id);
+ }
+
if (m_spellInfo->IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet
m_caster->RemoveGameObject(m_spellInfo->Id, true);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index aa16c4026..1e7865b2f 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2719,6 +2719,9 @@ void Spell::EffectAddFarsight(SpellEffIndex effIndex)
if (!m_caster->IsInWorld())
return;
+ // Remove old farsight if exist
+ bool updateViewerVisibility = m_caster->RemoveDynObject(m_spellInfo->Id);
+
DynamicObject* dynObj = new DynamicObject(true);
if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid(), m_caster, m_spellInfo->Id, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS))
{
@@ -2727,9 +2730,7 @@ void Spell::EffectAddFarsight(SpellEffIndex effIndex)
}
dynObj->SetDuration(duration);
- dynObj->SetCasterViewpoint();
-
- m_caster->ToPlayer()->UpdateVisibilityForPlayer();
+ dynObj->SetCasterViewpoint(updateViewerVisibility);
}
void Spell::EffectUntrainTalents(SpellEffIndex /*effIndex*/)