From c78371f32113c286bd1091cf688a2b800e95b7bb Mon Sep 17 00:00:00 2001 From: Shauren Date: Fri, 30 Sep 2022 11:08:20 +0200 Subject: [PATCH] ore/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) --- src/server/game/Entities/Unit/Unit.cpp | 29 ++++++--------------- src/server/game/Entities/Unit/Unit.h | 2 -- src/server/game/Spells/Spell.cpp | 34 ++++++++++++++++++++----- src/server/game/Spells/SpellHistory.cpp | 5 ---- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 64c26e1151..8bbe43bcfd 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -295,7 +295,7 @@ bool DispelableAura::RollDispel() const Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_lastSanctuaryTime(0), LastCharmerGUID(), m_ControlledByPlayer(false), movespline(new Movement::MoveSpline()), - m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0), + m_procDeep(0), m_removedAurasCount(0), m_interruptMask(SpellAuraInterruptFlags::None), m_interruptMask2(SpellAuraInterruptFlags2::None), m_charmer(nullptr), m_charmed(nullptr), i_motionMaster(new MotionMaster(this)), m_vehicle(nullptr), @@ -2611,6 +2611,8 @@ void Unit::_DeleteRemovedAuras() void Unit::_UpdateSpells(uint32 time) { + m_spellHistory->Update(); + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) _UpdateAutoRepeatSpell(); @@ -2663,8 +2665,6 @@ void Unit::_UpdateSpells(uint32 time) ++itr; } } - - m_spellHistory->Update(); } void Unit::_UpdateAutoRepeatSpell() @@ -2678,17 +2678,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); @@ -2703,11 +2697,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); } } @@ -2720,9 +2711,6 @@ void Unit::SetCurrentCastSpell(Spell* spell) if (spell == m_currentSpells[CSpellType]) // avoid breaking self return; - // break same type spell if it is not delayed - InterruptSpell(CSpellType, false, true); - // special breakage effects: switch (CSpellType) { @@ -2738,7 +2726,6 @@ void Unit::SetCurrentCastSpell(Spell* spell) // break autorepeat if not Auto Shot if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); - m_AutoRepeatFirstCast = true; } if (spell->GetCastTime() > 0) AddUnitState(UNIT_STATE_CASTING); @@ -2761,6 +2748,9 @@ void Unit::SetCurrentCastSpell(Spell* spell) } 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 (spell->GetSpellInfo()->Id != 75) { @@ -2768,9 +2758,6 @@ void Unit::SetCurrentCastSpell(Spell* spell) InterruptSpell(CURRENT_GENERIC_SPELL, false); InterruptSpell(CURRENT_CHANNELED_SPELL, false); } - // special action: set first cast flag - m_AutoRepeatFirstCast = true; - break; } default: diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 51bc6ef16e..71650f5a05 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1823,8 +1823,6 @@ class TC_GAME_API Unit : public WorldObject void _UpdateAutoRepeatSpell(); - bool m_AutoRepeatFirstCast; - uint32 m_attackTimer[MAX_ATTACK]; float m_createStats[MAX_STATS]; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 75f01477f4..ec537a8b47 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3286,7 +3286,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 @@ -3307,6 +3307,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; } @@ -3997,7 +4001,13 @@ void Spell::SendSpellCooldown() if (!m_caster->IsUnit()) return; - m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + if (m_CastItem) + 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) @@ -4040,7 +4050,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; @@ -4100,6 +4110,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(); @@ -5570,12 +5584,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NOT_READY; } - if (m_caster->ToUnit() && !m_caster->ToUnit()->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) + if (!IsIgnoringCooldowns() && m_caster->ToUnit()) { - if (IsTriggered() || 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->SpellCategoriesId == 76) && !m_caster->ToUnit()->isAttackReady(RANGED_ATTACK)) return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_NOT_READY; } } diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 240a01a517..61ee3ad4d8 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -294,11 +294,6 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel } else { - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) - // prevent 0 cooldowns set by another way - if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75))) - cooldown = _owner->GetAttackTime(RANGED_ATTACK); - // Now we have cooldown data (if found any), time to apply mods if (Player* modOwner = _owner->GetSpellModOwner()) {