Core/Spells: Fix orientation- and targeting-related issues if the creature isn't currently engaged in combat. (#17120)
Also disable spell focus system for vehicle control auras. Closes #16572 Closes #17016
This commit is contained in:
1 parent
372ef5e5a2
commit
a29913187b
7 files changed
+55
-62
No files matched your search
@@ -183,7 +183,7 @@ m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0
|
||||
m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
|
||||
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
|
||||
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
|
||||
m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0)
|
||||
m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f)
|
||||
{
|
||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||
m_valuesCount = UNIT_END;
|
||||
@@ -572,6 +572,19 @@ void Creature::Update(uint32 diff)
|
||||
if (!IsAlive())
|
||||
break;
|
||||
|
||||
if (m_shouldReacquireTarget && !IsFocusing(nullptr, true))
|
||||
{
|
||||
SetTarget(m_suppressedTarget);
|
||||
if (m_suppressedTarget)
|
||||
{
|
||||
if (WorldObject const* objTarget = ObjectAccessor::GetWorldObject(*this, m_suppressedTarget))
|
||||
SetFacingToObject(objTarget);
|
||||
}
|
||||
else
|
||||
SetFacingTo(m_suppressedOrientation);
|
||||
m_shouldReacquireTarget = false;
|
||||
}
|
||||
|
||||
// if creature is charmed, switch to charmed AI (and back)
|
||||
if (NeedChangeAI)
|
||||
{
|
||||
@@ -2696,31 +2709,39 @@ void Creature::SetDisplayId(uint32 modelId)
|
||||
|
||||
void Creature::SetTarget(ObjectGuid guid)
|
||||
{
|
||||
if (!IsFocusing())
|
||||
if (IsFocusing(nullptr, true))
|
||||
m_suppressedTarget = guid;
|
||||
else
|
||||
SetGuidValue(UNIT_FIELD_TARGET, guid);
|
||||
}
|
||||
|
||||
bool Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
|
||||
void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
|
||||
{
|
||||
// already focused
|
||||
if (m_focusSpell)
|
||||
return false;
|
||||
return;
|
||||
|
||||
// don't use spell focus for vehicle spells
|
||||
if (focusSpell->GetSpellInfo()->HasAura(SPELL_AURA_CONTROL_VEHICLE))
|
||||
return;
|
||||
|
||||
if ((!target || target == this) && !focusSpell->GetCastTime()) // instant cast, untargeted (or self-targeted) spell doesn't need any facing updates
|
||||
return false;
|
||||
return;
|
||||
|
||||
// store pre-cast values for target and orientation (used to later restore)
|
||||
if (!IsFocusing(nullptr, true))
|
||||
{ // only overwrite these fields if we aren't transitioning from one spell focus to another
|
||||
m_suppressedTarget = GetGuidValue(UNIT_FIELD_TARGET);
|
||||
m_suppressedOrientation = GetOrientation();
|
||||
}
|
||||
|
||||
m_focusSpell = focusSpell;
|
||||
|
||||
// "instant" creature casts that require re-targeting will be delayed by a short moment to prevent facing bugs
|
||||
bool shouldDelay = false;
|
||||
|
||||
// set target, then force send update packet to players if it changed to provide appropriate facing
|
||||
ObjectGuid newTarget = target ? target->GetGUID() : ObjectGuid::Empty;
|
||||
if (GetGuidValue(UNIT_FIELD_TARGET) != newTarget)
|
||||
{
|
||||
SetGuidValue(UNIT_FIELD_TARGET, newTarget);
|
||||
if (target)
|
||||
SetFacingToObject(target);
|
||||
|
||||
if ( // here we determine if the (relatively expensive) forced update is worth it, or whether we can afford to wait until the scheduled update tick
|
||||
( // only require instant update for spells that actually have a visual
|
||||
@@ -2738,28 +2759,21 @@ bool Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
|
||||
{
|
||||
// only update players that are known to the client (have already been created)
|
||||
if (player->HaveAtClient(this))
|
||||
{
|
||||
SendUpdateToPlayer(player);
|
||||
shouldDelay = true;
|
||||
}
|
||||
}
|
||||
if (shouldDelay)
|
||||
shouldDelay = !(focusSpell->IsTriggered() || focusSpell->GetCastTime() || focusSpell->GetSpellInfo()->IsChanneled());
|
||||
}
|
||||
}
|
||||
|
||||
bool canTurnDuringCast = !focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST);
|
||||
// Face the target - we need to do this before the unit state is modified for no-turn spells
|
||||
if (target)
|
||||
SetInFront(target);
|
||||
SetFacingTo(GetAngle(target));
|
||||
else if (!canTurnDuringCast)
|
||||
if (Unit* victim = GetVictim())
|
||||
SetInFront(victim); // ensure server-side orientation is correct at beginning of cast
|
||||
SetFacingTo(GetAngle(victim)); // ensure orientation is correct at beginning of cast
|
||||
|
||||
if (!canTurnDuringCast)
|
||||
AddUnitState(UNIT_STATE_CANNOT_TURN);
|
||||
|
||||
return shouldDelay;
|
||||
}
|
||||
|
||||
bool Creature::IsFocusing(Spell const* focusSpell, bool withDelay)
|
||||
@@ -2797,9 +2811,18 @@ void Creature::ReleaseFocus(Spell const* focusSpell, bool withDelay)
|
||||
return;
|
||||
|
||||
if (IsPet()) // player pets do not use delay system
|
||||
SetGuidValue(UNIT_FIELD_TARGET, GetVictim() ? EnsureVictim()->GetGUID() : ObjectGuid::Empty);
|
||||
{
|
||||
SetGuidValue(UNIT_FIELD_TARGET, m_suppressedTarget);
|
||||
if (m_suppressedTarget)
|
||||
{
|
||||
if (WorldObject const* objTarget = ObjectAccessor::GetWorldObject(*this, m_suppressedTarget))
|
||||
SetFacingTo(GetAngle(objTarget));
|
||||
}
|
||||
else
|
||||
SetFacingTo(m_suppressedOrientation);
|
||||
}
|
||||
else
|
||||
// tell the creature that it should reacquire its actual target after the delay expires (this is handled in ::Attack)
|
||||
// tell the creature that it should reacquire its actual target after the delay expires (this is handled in ::Update)
|
||||
// player pets don't need to do this, as they automatically reacquire their target on focus release
|
||||
MustReacquireTarget();
|
||||
|
||||
|
||||
@@ -684,7 +684,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
|
||||
|
||||
// Handling caster facing during spellcast
|
||||
void SetTarget(ObjectGuid guid) override;
|
||||
bool FocusTarget(Spell const* focusSpell, WorldObject const* target);
|
||||
void MustReacquireTarget() { m_shouldReacquireTarget = true; } // flags the Creature for forced (client displayed) target reacquisition in the next ::Update call
|
||||
void FocusTarget(Spell const* focusSpell, WorldObject const* target);
|
||||
bool IsFocusing(Spell const* focusSpell = nullptr, bool withDelay = false);
|
||||
void ReleaseFocus(Spell const* focusSpell = nullptr, bool withDelay = true);
|
||||
|
||||
@@ -760,8 +761,12 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
|
||||
CreatureGroup* m_formation;
|
||||
bool m_TriggerJustRespawned;
|
||||
|
||||
Spell const* m_focusSpell; ///> Locks the target during spell cast for proper facing
|
||||
/* Spell focus system */
|
||||
Spell const* m_focusSpell; // Locks the target during spell cast for proper facing
|
||||
uint32 m_focusDelay;
|
||||
bool m_shouldReacquireTarget;
|
||||
ObjectGuid m_suppressedTarget; // Stores the creature's "real" target while casting
|
||||
float m_suppressedOrientation; // Stores the creature's "real" orientation while casting
|
||||
|
||||
CreatureTextRepeatGroup m_textRepeat;
|
||||
};
|
||||
|
||||
@@ -247,7 +247,6 @@ Unit::Unit(bool isWorldObject) :
|
||||
m_createStats[i] = 0.0f;
|
||||
|
||||
m_attacking = nullptr;
|
||||
m_shouldReacquireTarget = false;
|
||||
m_modMeleeHitChance = 0.0f;
|
||||
m_modRangedHitChance = 0.0f;
|
||||
m_modSpellHitChance = 0.0f;
|
||||
@@ -7681,12 +7680,6 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
||||
if (HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
|
||||
RemoveAurasByType(SPELL_AURA_MOD_UNATTACKABLE);
|
||||
|
||||
if (m_shouldReacquireTarget)
|
||||
{
|
||||
SetTarget(victim->GetGUID());
|
||||
m_shouldReacquireTarget = false;
|
||||
}
|
||||
|
||||
if (m_attacking)
|
||||
{
|
||||
if (m_attacking == victim)
|
||||
|
||||
@@ -1319,7 +1319,6 @@ class TC_GAME_API Unit : public WorldObject
|
||||
void _removeAttacker(Unit* pAttacker); // must be called only from Unit::AttackStop()
|
||||
Unit* getAttackerForHelper() const; // If someone wants to help, who to give them
|
||||
bool Attack(Unit* victim, bool meleeAttack);
|
||||
void MustReacquireTarget() { m_shouldReacquireTarget = true; } // flags the Unit for forced (client displayed) target reacquisition in the next ::Attack call
|
||||
void CastStop(uint32 except_spellid = 0);
|
||||
bool AttackStop();
|
||||
void RemoveAllAttackers();
|
||||
@@ -2210,7 +2209,6 @@ class TC_GAME_API Unit : public WorldObject
|
||||
|
||||
AttackerSet m_attackers;
|
||||
Unit* m_attacking;
|
||||
bool m_shouldReacquireTarget;
|
||||
|
||||
DeathState m_deathState;
|
||||
|
||||
|
||||
@@ -605,8 +605,6 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO
|
||||
//Auto Shot & Shoot (wand)
|
||||
m_autoRepeat = m_spellInfo->IsAutoRepeatRangedSpell();
|
||||
|
||||
m_isDelayedInstantCast = false;
|
||||
|
||||
m_runesState = 0;
|
||||
m_powerCost = 0; // setup to correct value in Spell::prepare, must not be used before.
|
||||
m_casttime = 0; // setup to correct value in Spell::prepare, must not be used before.
|
||||
@@ -2984,21 +2982,9 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
|
||||
if (!(IsNextMeleeSwingSpell() || IsAutoRepeat() || _triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING))
|
||||
{
|
||||
if (m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget())
|
||||
{
|
||||
if (m_caster->ToCreature()->FocusTarget(this, m_targets.GetObjectTarget()))
|
||||
{
|
||||
m_isDelayedInstantCast = true;
|
||||
m_timer = 100; // 100ms delay ensures client has updated creature orientation when cast goes off
|
||||
}
|
||||
}
|
||||
m_caster->ToCreature()->FocusTarget(this, m_targets.GetObjectTarget());
|
||||
else if (m_spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
|
||||
{
|
||||
if (m_caster->ToCreature()->FocusTarget(this, nullptr))
|
||||
{
|
||||
m_isDelayedInstantCast = true;
|
||||
m_timer = 100;
|
||||
}
|
||||
}
|
||||
m_caster->ToCreature()->FocusTarget(this, nullptr);
|
||||
}
|
||||
|
||||
// don't allow channeled spells / spells with cast time to be cast while moving
|
||||
@@ -3045,14 +3031,13 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
|
||||
}
|
||||
|
||||
m_caster->SetCurrentCastSpell(this);
|
||||
if (!m_isDelayedInstantCast)
|
||||
SendSpellStart();
|
||||
SendSpellStart();
|
||||
|
||||
if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD))
|
||||
TriggerGlobalCooldown();
|
||||
|
||||
//item: first cast may destroy item and second cast causes crash
|
||||
if (!m_casttime && !m_isDelayedInstantCast && !m_spellInfo->StartRecoveryTime && !m_castItemGUID && GetCurrentContainer() == CURRENT_GENERIC_SPELL)
|
||||
if (!m_casttime && !m_spellInfo->StartRecoveryTime && !m_castItemGUID && GetCurrentContainer() == CURRENT_GENERIC_SPELL)
|
||||
cast(true);
|
||||
}
|
||||
}
|
||||
@@ -3061,9 +3046,6 @@ void Spell::cancel()
|
||||
{
|
||||
if (m_spellState == SPELL_STATE_FINISHED)
|
||||
return;
|
||||
// delayed instant casts are used for client-side visual orientation; they are treated as instant for all intents and purposes server-side, and thus cannot be interrupted by another cast
|
||||
if (m_isDelayedInstantCast)
|
||||
return;
|
||||
|
||||
uint32 oldState = m_spellState;
|
||||
m_spellState = SPELL_STATE_FINISHED;
|
||||
@@ -3133,9 +3115,6 @@ void Spell::cast(bool skipCheck)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_isDelayedInstantCast)
|
||||
SendSpellStart();
|
||||
|
||||
if (Player* playerCaster = m_caster->ToPlayer())
|
||||
{
|
||||
// now that we've done the basic check, now run the scripts
|
||||
@@ -3220,10 +3199,7 @@ void Spell::cast(bool skipCheck)
|
||||
if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
if (!m_spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
|
||||
if (WorldObject* objTarget = m_targets.GetObjectTarget())
|
||||
{
|
||||
m_caster->SetInFront(objTarget);
|
||||
m_caster->SetFacingToObject(objTarget);
|
||||
}
|
||||
|
||||
SelectSpellTargets();
|
||||
|
||||
|
||||
@@ -534,7 +534,6 @@ class TC_GAME_API Spell
|
||||
int32 m_channeledDuration; // Calculated channeled spell duration in order to calculate correct pushback.
|
||||
bool m_canReflect; // can reflect this spell?
|
||||
bool m_autoRepeat;
|
||||
bool m_isDelayedInstantCast; // whether this is a creature's delayed instant cast
|
||||
uint8 m_runesState;
|
||||
|
||||
uint8 m_delayAtDamageCount;
|
||||
|
||||
@@ -115,7 +115,6 @@ public:
|
||||
}
|
||||
break;
|
||||
}
|
||||
me->SetOrientation(me->GetHomePosition().GetOrientation());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user