mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 15:40:45 +01:00
Core/Spells: Autorepeat casting fixes
* Fixed cast bars of ranged spells not disappearing when canceled while autoshooting * Replaced hacky 500ms delay on non auto shot autorepeat spells with native spell cast time (wands have 500ms cast time) * Update cooldowns before attempting to cast autorepeat spells (for cases when cooldown ends on the same server tick as cast attempt)
This commit is contained in:
@@ -299,7 +299,7 @@ SpellNonMeleeDamage::SpellNonMeleeDamage(Unit* _attacker, Unit* _target, SpellIn
|
||||
|
||||
Unit::Unit(bool isWorldObject) :
|
||||
WorldObject(isWorldObject), m_lastSanctuaryTime(0), LastCharmerGUID(), movespline(new Movement::MoveSpline()),
|
||||
m_ControlledByPlayer(false), m_AutoRepeatFirstCast(false), m_procDeep(0), m_transformSpell(0),
|
||||
m_ControlledByPlayer(false), m_procDeep(0), m_transformSpell(0),
|
||||
m_removedAurasCount(0), m_interruptMask(SpellAuraInterruptFlags::None), m_interruptMask2(SpellAuraInterruptFlags2::None),
|
||||
m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr),
|
||||
i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_vehicle(nullptr),
|
||||
@@ -2721,6 +2721,8 @@ void Unit::_DeleteRemovedAuras()
|
||||
|
||||
void Unit::_UpdateSpells(uint32 time)
|
||||
{
|
||||
_spellHistory->Update();
|
||||
|
||||
if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
|
||||
_UpdateAutoRepeatSpell();
|
||||
|
||||
@@ -2774,8 +2776,6 @@ void Unit::_UpdateSpells(uint32 time)
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
_spellHistory->Update();
|
||||
}
|
||||
|
||||
void Unit::_UpdateAutoRepeatSpell()
|
||||
@@ -2789,17 +2789,11 @@ void Unit::_UpdateAutoRepeatSpell()
|
||||
// cancel wand shoot
|
||||
if (autoRepeatSpellInfo->Id != 75)
|
||||
InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
|
||||
m_AutoRepeatFirstCast = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// apply delay (Auto Shot (spellID 75) not affected)
|
||||
if (m_AutoRepeatFirstCast && getAttackTimer(RANGED_ATTACK) < 500 && autoRepeatSpellInfo->Id != 75)
|
||||
setAttackTimer(RANGED_ATTACK, 500);
|
||||
m_AutoRepeatFirstCast = false;
|
||||
|
||||
// castroutine
|
||||
if (isAttackReady(RANGED_ATTACK))
|
||||
if (isAttackReady(RANGED_ATTACK) && m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_PREPARING)
|
||||
{
|
||||
// Check if able to cast
|
||||
SpellCastResult result = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckCast(true);
|
||||
@@ -2814,11 +2808,8 @@ void Unit::_UpdateAutoRepeatSpell()
|
||||
}
|
||||
|
||||
// we want to shoot
|
||||
Spell* spell = new Spell(this, autoRepeatSpellInfo, TRIGGERED_FULL_MASK);
|
||||
Spell* spell = new Spell(this, autoRepeatSpellInfo, TRIGGERED_IGNORE_GCD);
|
||||
spell->prepare(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets);
|
||||
|
||||
// all went good, reset attack
|
||||
resetAttackTimer(RANGED_ATTACK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2831,14 +2822,13 @@ void Unit::SetCurrentCastSpell(Spell* pSpell)
|
||||
if (pSpell == m_currentSpells[CSpellType]) // avoid breaking self
|
||||
return;
|
||||
|
||||
// break same type spell if it is not delayed
|
||||
InterruptSpell(CSpellType, false);
|
||||
|
||||
// special breakage effects:
|
||||
switch (CSpellType)
|
||||
{
|
||||
case CURRENT_GENERIC_SPELL:
|
||||
{
|
||||
InterruptSpell(CURRENT_GENERIC_SPELL, false);
|
||||
|
||||
// generic spells always break channeled not delayed spells
|
||||
if (m_currentSpells[CURRENT_CHANNELED_SPELL] && !m_currentSpells[CURRENT_CHANNELED_SPELL]->GetSpellInfo()->HasAttribute(SPELL_ATTR5_ALLOW_ACTIONS_DURING_CHANNEL))
|
||||
InterruptSpell(CURRENT_CHANNELED_SPELL, false);
|
||||
@@ -2849,7 +2839,6 @@ void Unit::SetCurrentCastSpell(Spell* pSpell)
|
||||
// break autorepeat if not Auto Shot
|
||||
if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75)
|
||||
InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
|
||||
m_AutoRepeatFirstCast = true;
|
||||
}
|
||||
if (pSpell->GetCastTime() > 0)
|
||||
AddUnitState(UNIT_STATE_CASTING);
|
||||
@@ -2872,6 +2861,9 @@ void Unit::SetCurrentCastSpell(Spell* pSpell)
|
||||
}
|
||||
case CURRENT_AUTOREPEAT_SPELL:
|
||||
{
|
||||
if (m_currentSpells[CSpellType] && m_currentSpells[CSpellType]->getState() == SPELL_STATE_IDLE)
|
||||
m_currentSpells[CSpellType]->setState(SPELL_STATE_FINISHED);
|
||||
|
||||
// only Auto Shoot does not break anything
|
||||
if (pSpell->GetSpellInfo()->Id != 75)
|
||||
{
|
||||
@@ -2879,8 +2871,6 @@ void Unit::SetCurrentCastSpell(Spell* pSpell)
|
||||
InterruptSpell(CURRENT_GENERIC_SPELL, false);
|
||||
InterruptSpell(CURRENT_CHANNELED_SPELL, false);
|
||||
}
|
||||
// special action: set first cast flag
|
||||
m_AutoRepeatFirstCast = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1919,8 +1919,6 @@ class TC_GAME_API Unit : public WorldObject
|
||||
|
||||
bool m_ControlledByPlayer;
|
||||
|
||||
bool m_AutoRepeatFirstCast;
|
||||
|
||||
float m_createStats[MAX_STATS];
|
||||
float m_floatStatPosBuff[MAX_STATS];
|
||||
float m_floatStatNegBuff[MAX_STATS];
|
||||
|
||||
@@ -3338,7 +3338,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const
|
||||
// handle just the general SPELL_FAILED_BAD_TARGETS result which is the default result for most DBC target checks
|
||||
if (_triggeredCastFlags & TRIGGERED_IGNORE_TARGET_CHECK && result == SPELL_FAILED_BAD_TARGETS)
|
||||
result = SPELL_CAST_OK;
|
||||
if (result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
|
||||
if (result != SPELL_CAST_OK)
|
||||
{
|
||||
// Periodic auras should be interrupted when aura triggers a spell which can't be cast
|
||||
// for example bladestorm aura should be removed on disarm as of patch 3.3.5
|
||||
@@ -3355,6 +3355,10 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const
|
||||
else
|
||||
SendCastResult(result);
|
||||
|
||||
// queue autorepeat spells for future repeating
|
||||
if (GetCurrentContainer() == CURRENT_AUTOREPEAT_SPELL && m_caster->IsUnit())
|
||||
m_caster->ToUnit()->SetCurrentCastSpell(this);
|
||||
|
||||
finish(false);
|
||||
return result;
|
||||
}
|
||||
@@ -4084,6 +4088,9 @@ void Spell::SendSpellCooldown()
|
||||
m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this);
|
||||
else
|
||||
m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_castItemEntry, this);
|
||||
|
||||
if (IsAutoRepeat())
|
||||
m_caster->ToUnit()->resetAttackTimer(RANGED_ATTACK);
|
||||
}
|
||||
|
||||
void Spell::update(uint32 difftime)
|
||||
@@ -4126,7 +4133,7 @@ void Spell::update(uint32 difftime)
|
||||
m_timer -= difftime;
|
||||
}
|
||||
|
||||
if (m_timer == 0 && !m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat())
|
||||
if (m_timer == 0 && !m_spellInfo->IsNextMeleeSwingSpell())
|
||||
// don't CheckCast for instant spells - done in spell::prepare, skip duplicate checks, needed for range checks for example
|
||||
cast(!m_casttime);
|
||||
break;
|
||||
@@ -4186,6 +4193,10 @@ void Spell::finish(bool ok)
|
||||
if (!unitCaster)
|
||||
return;
|
||||
|
||||
// successful cast of the initial autorepeat spell is moved to idle state so that it is not deleted as long as autorepeat is active
|
||||
if (IsAutoRepeat() && unitCaster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL) == this)
|
||||
m_spellState = SPELL_STATE_IDLE;
|
||||
|
||||
if (m_spellInfo->IsChanneled())
|
||||
unitCaster->UpdateInterruptMask();
|
||||
|
||||
@@ -5464,12 +5475,18 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32
|
||||
return SPELL_FAILED_NOT_READY;
|
||||
}
|
||||
|
||||
if (!IsIgnoringCooldowns() && m_caster->ToUnit() && !m_caster->ToUnit()->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry))
|
||||
if (!IsIgnoringCooldowns() && m_caster->ToUnit())
|
||||
{
|
||||
if (m_triggeredByAuraSpell)
|
||||
if (!m_caster->ToUnit()->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry))
|
||||
{
|
||||
if (m_triggeredByAuraSpell)
|
||||
return SPELL_FAILED_DONT_REPORT;
|
||||
else
|
||||
return SPELL_FAILED_NOT_READY;
|
||||
}
|
||||
|
||||
if ((IsAutoRepeat() || m_spellInfo->CategoryId == 76) && !m_caster->ToUnit()->isAttackReady(RANGED_ATTACK))
|
||||
return SPELL_FAILED_DONT_REPORT;
|
||||
else
|
||||
return SPELL_FAILED_NOT_READY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -430,11 +430,6 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
|
||||
{
|
||||
if (!forcedCooldown)
|
||||
{
|
||||
// shoot spells used equipped item cooldown values already assigned in SetBaseAttackTime(RANGED_ATTACK)
|
||||
// prevent 0 cooldowns set by another way
|
||||
if (cooldown <= Duration::zero() && categoryCooldown <= Duration::zero() && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75)))
|
||||
cooldown = Milliseconds(*_owner->m_unitData->RangedAttackRoundBaseTime);
|
||||
|
||||
// Now we have cooldown data (if found any), time to apply mods
|
||||
if (Player* modOwner = _owner->GetSpellModOwner())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user