Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Unit.cpp
- void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool withInstant, SpellCastResult result, Optional<SpellCastResult> resultOther /*= {}*/)
- {
- //TC_LOG_DEBUG("entities.unit", "Interrupt spell for unit {}.", GetEntry());
- Spell* spell = m_currentSpells[spellType];
- if (spell
- && (withDelayed || spell->getState() != SPELL_STATE_DELAYED)
- && (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING))
- {
- // for example, do not let self-stun aura interrupt itself
- if (!spell->IsInterruptable())
- return;
- // send autorepeat cancel message for autorepeat spells
- if (spellType == CURRENT_AUTOREPEAT_SPELL)
- if (GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->SendAutoRepeatCancel(this);
- if (spell->getState() != SPELL_STATE_FINISHED)
- spell->cancel(result, resultOther);
- else
- {
- m_currentSpells[spellType] = nullptr;
- spell->SetReferencedFromCurrent(false);
- }
- if (GetTypeId() == TYPEID_UNIT && IsAIEnabled())
- ToCreature()->AI()->OnSpellFailed(spell->GetSpellInfo());
- }
- }
- void Unit::_UpdateAutoRepeatSpell()
- {
- SpellInfo const* autoRepeatSpellInfo = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo;
- // check "realtime" interrupts
- if ((GetTypeId() == TYPEID_PLAYER && ToPlayer()->isMoving()) || IsNonMeleeSpellCast(false, false, false, (autoRepeatSpellInfo->Id == 75 || autoRepeatSpellInfo->Id == 5019)))
- {
- // cancel wand shoot
- if (autoRepeatSpellInfo->Id != 75 && autoRepeatSpellInfo->Id != 5019)
- 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 && autoRepeatSpellInfo->Id != 5019)
- setAttackTimer(RANGED_ATTACK, 500);
- m_AutoRepeatFirstCast = false;
- // castroutine
- if (isAttackReady(RANGED_ATTACK))
- {
- // Check if able to cast
- SpellCastResult result = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckCast(true);
- if (result != SPELL_CAST_OK)
- {
- if (autoRepeatSpellInfo->Id != 75 && autoRepeatSpellInfo->Id != 5019)
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- else if (GetTypeId() == TYPEID_PLAYER)
- Spell::SendCastResult(ToPlayer(), autoRepeatSpellInfo, 1, result);
- return;
- }
- // we want to shoot
- Spell* spell = new Spell(this, autoRepeatSpellInfo, TRIGGERED_FULL_MASK);
- spell->prepare(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets);
- // all went good, reset attack
- resetAttackTimer(RANGED_ATTACK);
- }
- }
- bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAutorepeat, bool isAutoshoot, bool skipInstant) const
- {
- // We don't do loop here to explicitly show that melee spell is excluded.
- // Maybe later some special spells will be excluded too.
- // generic spells are cast when they are not finished and not delayed
- if (m_currentSpells[CURRENT_GENERIC_SPELL] &&
- (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
- (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED))
- {
- if (!skipInstant || m_currentSpells[CURRENT_GENERIC_SPELL]->GetCastTime())
- {
- if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)))
- return true;
- }
- }
- // channeled spells may be delayed, but they are still considered cast
- if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] &&
- (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED))
- {
- if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)))
- return true;
- }
- // autorepeat spells may be finished or delayed, but they are still considered cast
- if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
- return true;
- return false;
- }
- void Unit::SetCurrentCastSpell(Spell* pSpell)
- {
- ASSERT(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
- CurrentSpellTypes CSpellType = pSpell->GetCurrentContainer();
- 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:
- {
- // generic spells always break channeled not delayed spells
- InterruptSpell(CURRENT_CHANNELED_SPELL, false);
- // autorepeat breaking
- if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
- {
- // break autorepeat if not Auto Shot
- if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75 &&
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 5019)
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- m_AutoRepeatFirstCast = true;
- }
- if (pSpell->GetCastTime() > 0)
- AddUnitState(UNIT_STATE_CASTING);
- break;
- }
- case CURRENT_CHANNELED_SPELL:
- {
- // channel spells always break generic non-delayed and any channeled spells
- InterruptSpell(CURRENT_GENERIC_SPELL, false);
- InterruptSpell(CURRENT_CHANNELED_SPELL);
- // it also does break autorepeat if not Auto Shot
- if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] &&
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75 &&
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 5019)
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- AddUnitState(UNIT_STATE_CASTING);
- break;
- }
- case CURRENT_AUTOREPEAT_SPELL:
- {
- // only Auto Shoot does not break anything
- if (pSpell->GetSpellInfo()->Id != 75 && pSpell->GetSpellInfo()->Id != 5019)
- {
- // generic autorepeats break generic non-delayed and channeled non-delayed spells
- InterruptSpell(CURRENT_GENERIC_SPELL, false);
- InterruptSpell(CURRENT_CHANNELED_SPELL, false);
- }
- // special action: set first cast flag
- m_AutoRepeatFirstCast = true;
- break;
- }
- default:
- break; // other spell types don't break anything now
- }
- // current spell (if it is still here) may be safely deleted now
- if (m_currentSpells[CSpellType])
- m_currentSpells[CSpellType]->SetReferencedFromCurrent(false);
- // set new current spell
- m_currentSpells[CSpellType] = pSpell;
- pSpell->SetReferencedFromCurrent(true);
- pSpell->m_selfContainer = &(m_currentSpells[pSpell->GetCurrentContainer()]);
- }
- /* -------------------------------------------------------------------------------- */
- Spellhistory.cpp
- void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/)
- {
- if (spellInfo->Id == 5019)
- return;
- // init cooldown values
- uint32 categoryId = 0;
- int32 cooldown = -1;
- int32 categoryCooldown = -1;
- GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown);
- Clock::time_point curTime = GameTime::GetSystemTime();
- Clock::time_point catrecTime;
- Clock::time_point recTime;
- bool needsCooldownPacket = false;
- // overwrite time for selected category
- if (onHold)
- {
- // use +MONTH as infinite cooldown marker
- catrecTime = categoryCooldown > 0 ? (curTime + InfinityCooldownDelay) : curTime;
- recTime = cooldown > 0 ? (curTime + InfinityCooldownDelay) : catrecTime;
- }
- 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 && spellInfo->Id != 5019)))
- cooldown = _owner->GetAttackTime(RANGED_ATTACK);
- // Now we have cooldown data (if found any), time to apply mods
- if (Player* modOwner = _owner->GetSpellModOwner())
- {
- if (cooldown >= 0)
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell);
- if (categoryCooldown >= 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell);
- }
- if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
- {
- // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
- Player* playerOwner = GetPlayerOwner();
- if (!playerOwner || playerOwner->HasSpell(spellInfo->Id))
- {
- needsCooldownPacket = true;
- cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
- }
- }
- // replace negative cooldowns by 0
- if (cooldown < 0)
- cooldown = 0;
- if (categoryCooldown < 0)
- categoryCooldown = 0;
- // no cooldown after applying spell mods
- if (cooldown == 0 && categoryCooldown == 0)
- return;
- catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(categoryCooldown)) : curTime;
- recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldown)) : catrecTime;
- }
- // self spell cooldown
- if (recTime != curTime)
- {
- AddCooldown(spellInfo->Id, itemId, recTime, categoryId, catrecTime, onHold);
- if (needsCooldownPacket)
- {
- if (Player* playerOwner = GetPlayerOwner())
- {
- WorldPacket spellCooldown;
- BuildCooldownPacket(spellCooldown, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, cooldown);
- playerOwner->SendDirectMessage(&spellCooldown);
- }
- }
- }
- }
- /* -------------------------------------------------------------------------------- */
- Spell.cpp
- SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggeredByAura)
- {
- if (m_CastItem)
- {
- m_castItemGUID = m_CastItem->GetGUID();
- m_castItemEntry = m_CastItem->GetEntry();
- }
- else
- {
- m_castItemGUID.Clear();
- m_castItemEntry = 0;
- }
- InitExplicitTargets(targets);
- // Fill aura scaling information
- if (Unit* unitCaster = m_caster->ToUnit())
- {
- if (unitCaster->IsControlledByPlayer() && !m_spellInfo->IsPassive() && m_spellInfo->SpellLevel && !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_SCALING))
- {
- for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
- {
- if (spellEffectInfo.IsEffect(SPELL_EFFECT_APPLY_AURA))
- {
- // Change aura with ranks only if basepoints are taken from spellInfo and aura is positive
- if (m_spellInfo->IsPositiveEffect(spellEffectInfo.EffectIndex))
- {
- m_auraScaleMask |= (1 << spellEffectInfo.EffectIndex);
- if (m_spellValue->EffectBasePoints[spellEffectInfo.EffectIndex] != spellEffectInfo.BasePoints)
- {
- m_auraScaleMask = 0;
- break;
- }
- }
- }
- }
- }
- }
- m_spellState = SPELL_STATE_PREPARING;
- if (triggeredByAura)
- m_triggeredByAuraSpell = triggeredByAura->GetSpellInfo();
- // create and add update event for this spell
- _spellEvent = new SpellEvent(this);
- m_caster->m_Events.AddEvent(_spellEvent, m_caster->m_Events.CalculateTime(1ms));
- // check disables
- if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster))
- {
- SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE);
- finish(false);
- return SPELL_FAILED_SPELL_UNAVAILABLE;
- }
- // Prevent casting at cast another spell (ServerSide check)
- if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS) &&
- m_caster->ToUnit() &&
- m_caster->ToUnit()->IsNonMeleeSpellCast(false, true, false, true) &&
- m_cast_count && m_spellInfo->Id != 5019)
- {
- SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS);
- finish(false);
- return SPELL_FAILED_SPELL_IN_PROGRESS;
- }
- LoadScripts();
- // Fill cost data (do not use power for item casts)
- m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask, this);
- // Set combo point requirement
- if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem)
- m_needComboPoints = false;
- uint32 param1 = 0, param2 = 0;
- SpellCastResult result = CheckCast(true, ¶m1, ¶m2);
- if (result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
- {
- // 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
- // channeled periodic spells should be affected by this (arcane missiles, penance, etc)
- // a possible alternative sollution for those would be validating aura target on unit state change
- if (triggeredByAura && triggeredByAura->IsPeriodic() && !triggeredByAura->GetBase()->IsPassive())
- {
- SendChannelUpdate(0);
- triggeredByAura->GetBase()->SetDuration(0);
- }
- if (param1 || param2)
- SendCastResult(result, ¶m1, ¶m2);
- else
- SendCastResult(result);
- finish(false);
- return result;
- }
- // Prepare data for triggers
- prepareDataForTriggerSystem();
- if (Player* player = m_caster->ToPlayer())
- {
- if (!player->GetCommandStatus(CHEAT_CASTTIME))
- {
- // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail)
- m_casttime = m_spellInfo->CalcCastTime(this);
- }
- else
- m_casttime = 0; // Set cast time to 0 if .cheat casttime is enabled.
- }
- else
- m_casttime = m_spellInfo->CalcCastTime(this);
- SpellCastResult movementResult = SPELL_CAST_OK;
- if (m_caster->IsUnit() && m_caster->ToUnit()->isMoving())
- movementResult = CheckMovement();
- // Creatures focus their target when possible
- if (m_casttime && m_caster->IsCreature() && !m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_caster->ToUnit()->HasUnitFlag(UNIT_FLAG_POSSESSED))
- {
- // Channeled spells and some triggered spells do not focus a cast target. They face their target later on via channel object guid and via spell attribute or not at all
- bool const focusTarget = !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING);
- if (focusTarget && m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget())
- m_caster->ToCreature()->SetSpellFocus(this, m_targets.GetObjectTarget());
- else
- m_caster->ToCreature()->SetSpellFocus(this, nullptr);
- }
- if (movementResult != SPELL_CAST_OK)
- {
- if (m_caster->ToUnit()->IsControlledByPlayer() || !CanStopMovementForSpellCasting(m_caster->ToUnit()->GetMotionMaster()->GetCurrentMovementGeneratorType()))
- {
- SendCastResult(movementResult);
- finish(movementResult);
- return movementResult;
- }
- else
- {
- // Creatures (not controlled) give priority to spell casting over movement.
- // We assume that the casting is always valid and the current movement
- // is stopped immediately (because spells are updated before movement, so next Unit::Update would cancel the spell before stopping movement)
- // and future attempts are stopped by by Unit::IsMovementPreventedByCasting in movement generators to prevent casting interruption.
- m_caster->ToUnit()->StopMoving();
- }
- }
- // set timer base at cast time
- ReSetTimer();
- TC_LOG_DEBUG("spells", "Spell::prepare: spell id {} source {} caster {} customCastFlags {} mask {}", m_spellInfo->Id, m_caster->GetEntry(), m_originalCaster ? m_originalCaster->GetEntry() : -1, _triggeredCastFlags, m_targets.GetTargetMask());
- //Containers for channeled spells have to be set
- /// @todoApply this to all cast spells if needed
- // Why check duration? 29350: channelled triggers channelled
- if ((_triggeredCastFlags & TRIGGERED_CAST_DIRECTLY) && (!m_spellInfo->IsChanneled() || !m_spellInfo->GetMaxDuration()))
- cast(true);
- else
- {
- if (Unit* unitCaster = m_caster->ToUnit())
- {
- // stealth must be removed at cast starting (at show channel bar)
- // 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_spellInfo->Id != 75) // 5019 ahora interrumpe stealth
- {
- unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST);
- for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
- {
- if (spellEffectInfo.GetUsedTargetObjectType() == TARGET_OBJECT_TYPE_UNIT)
- {
- unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK);
- break;
- }
- }
- }
- unitCaster->SetCurrentCastSpell(this);
- }
- SendSpellStart();
- if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD))
- TriggerGlobalCooldown();
- // Call CreatureAI hook OnSpellStart
- if (Creature* caster = m_caster->ToCreature())
- if (caster->IsAIEnabled())
- caster->AI()->OnSpellStart(GetSpellInfo());
- // commented out !m_spellInfo->StartRecoveryTime, it forces instant spells with global cooldown to be processed in spell::update
- // as a result a spell that passed CheckCast and should be processed instantly may suffer from this delayed process
- // the easiest bug to observe is LoS check in AddUnitTarget, even if spell passed the CheckCast LoS check the situation can change in spell::update
- // because target could be relocated in the meantime, making the spell fly to the air (no targets can be registered, so no effects processed, nothing in combat log)
- if (!m_casttime && /*!m_spellInfo->StartRecoveryTime && */ GetCurrentContainer() == CURRENT_GENERIC_SPELL)
- cast(true);
- return SPELL_CAST_OK;
- }
- return SPELL_CAST_OK;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement