fix(Core/Spells): Improvements to Far Sight spell: (#11683)
* fix(Core/Spells): Improvements to Far Sight spell: Far Sight should not interrupt while casting another spell. Corrected setting Far Sight object as an active object. Fixed grid activation range for active dynamic objects. When Far Sight is over, the camera be reset to player. Enable swapping camera between Far Sight and Sentry Totem. Fixes #6368 * Update. * Update.
This commit is contained in:
parent
0c209dae75
commit
99f1cd84e2
12 changed files with 127 additions and 39 deletions
|
|
@ -15,6 +15,7 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<TargetInfo>::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<TargetInfo>::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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<HighGuid::DynamicObject>(), 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*/)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue