diff --git a/data/sql/updates/pending_db_world/rev_1656252824985469400.sql b/data/sql/updates/pending_db_world/rev_1656252824985469400.sql new file mode 100644 index 000000000..2a73f0ef9 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1656252824985469400.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=24324; +INSERT INTO `spell_script_names` VALUES +(24324,'spell_blood_siphon'); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 48ff211d4..6af1be441 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3439,7 +3439,7 @@ bool Creature::IsMovementPreventedByCasting() const { Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]; // first check if currently a movement allowed channel is active and we're not casting - if (spell && spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive() && spell->GetSpellInfo()->IsMoveAllowedChannel()) + if (spell && spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive() && spell->GetSpellInfo()->IsActionAllowedChannel()) { return false; } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 07e9c6288..1bfbb3d5c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3622,8 +3622,12 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell) { // generic spells always break channeled not delayed spells if (Spell* s = GetCurrentSpell(CURRENT_CHANNELED_SPELL)) - if (s->GetSpellInfo()->Id != 69051) // pussywizard: FoS, boss Devourer of Souls, Mirrored Soul, does not have any special attribute + { + if (!s->GetSpellInfo()->IsActionAllowedChannel()) + { InterruptSpell(CURRENT_CHANNELED_SPELL, false); + } + } // autorepeat breaking if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) @@ -3658,7 +3662,14 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell) if (pSpell->m_spellInfo->Id != 75) { // generic autorepeats break generic non-delayed and channeled non-delayed spells - InterruptSpell(CURRENT_GENERIC_SPELL, false); + if (Spell* s = GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + { + if (!s->GetSpellInfo()->IsActionAllowedChannel()) + { + InterruptSpell(CURRENT_CHANNELED_SPELL, false); + } + } + InterruptSpell(CURRENT_CHANNELED_SPELL, false); } // special action: set first cast flag @@ -3793,7 +3804,7 @@ bool Unit::IsMovementPreventedByCasting() const { if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive()) { - if (spell->GetSpellInfo()->IsMoveAllowedChannel()) + if (spell->GetSpellInfo()->IsActionAllowedChannel()) { return false; } @@ -3804,15 +3815,6 @@ bool Unit::IsMovementPreventedByCasting() const return true; } -bool Unit::CanMoveDuringChannel() const -{ - if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) - if (spell->getState() != SPELL_STATE_FINISHED) - return spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL) && spell->IsChannelActive(); - - return false; -} - bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const { return IsWithinDistInMap(target, distance) && HasInArc(arc, target); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index f40b8e533..39983f0d0 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2029,9 +2029,6 @@ public: // delayed+channeled spells are always interrupted void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0, bool withInstant = true, bool bySelf = false); - // Check if our current channel spell has attribute SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL - [[nodiscard]] bool CanMoveDuringChannel() const; - [[nodiscard]] Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } [[nodiscard]] Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; } [[nodiscard]] Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index be3d6c522..407201c1d 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -491,7 +491,7 @@ void WorldSession::HandleCancelAuraOpcode(WorldPacket& recvPacket) if (!spellInfo) return; - // not allow remove spells with attr SPELL_ATTR0_CANT_CANCEL + // not allow remove spells with attr SPELL_ATTR0_NO_AURA_CANCEL if (spellInfo->HasAttribute(SPELL_ATTR0_NO_AURA_CANCEL)) { return; @@ -569,12 +569,33 @@ void WorldSession::HandleCancelAutoRepeatSpellOpcode(WorldPacket& /*recvPacket*/ void WorldSession::HandleCancelChanneling(WorldPacket& recvData) { - recvData.read_skip(); // spellid, not used + uint32 spellID = 0; + recvData >> spellID; // ignore for remote control state (for player case) Unit* mover = _player->m_mover; - if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) + if (!mover) + { return; + } + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); + if (!spellInfo) + { + return; + } + + // not allow remove spells with attr SPELL_ATTR0_NO_AURA_CANCEL + if (spellInfo->HasAttribute(SPELL_ATTR0_NO_AURA_CANCEL)) + { + return; + } + + Spell* spell = mover->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + if (!spell || spell->GetSpellInfo()->Id != spellInfo->Id) + { + return; + } mover->InterruptSpell(CURRENT_CHANNELED_SPELL); } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8bcb30778..75b252b20 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3555,9 +3555,13 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && !IsTriggered()) { - SendCastResult(SPELL_FAILED_MOVING); - finish(false); - return SPELL_FAILED_MOVING; + // 1. Has casttime, 2. Or doesn't have flag to allow action during channel + if (m_casttime || !m_spellInfo->IsActionAllowedChannel()) + { + SendCastResult(SPELL_FAILED_MOVING); + finish(false); + return SPELL_FAILED_MOVING; + } } // xinef: if spell have nearby target entry only, do not allow to cast if no targets are found diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 6f5257301..7bb066785 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1241,7 +1241,7 @@ bool SpellInfo::IsChanneled() const return (AttributesEx & (SPELL_ATTR1_IS_CHANNELED | SPELL_ATTR1_IS_SELF_CHANNELED)); } -bool SpellInfo::IsMoveAllowedChannel() const +bool SpellInfo::IsActionAllowedChannel() const { return IsChanneled() && HasAttribute(SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL); } diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index d93e9e6b3..dc300600b 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -457,7 +457,7 @@ public: bool IsPositive() const; bool IsPositiveEffect(uint8 effIndex) const; bool IsChanneled() const; - [[nodiscard]] bool IsMoveAllowedChannel() const; + [[nodiscard]] bool IsActionAllowedChannel() const; bool NeedsComboPoints() const; bool IsBreakingStealth() const; bool IsRangedWeaponSpell() const; diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 2ef18974e..17542822f 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -4301,6 +4301,16 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].MiscValueB = 64; }); + // Blood Siphon + ApplySpellFix({ 24322, 24323 }, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_1].ApplyAuraName = SPELL_AURA_MOD_STUN; + spellInfo->Effects[EFFECT_2].Effect = 0; + spellInfo->Attributes |= SPELL_ATTR0_NO_AURA_CANCEL; + spellInfo->AttributesEx5 |= SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL; + spellInfo->ChannelInterruptFlags &= ~AURA_INTERRUPT_FLAG_MOVE; + }); + // Place Fake Fur ApplySpellFix({ 46085 }, [](SpellInfo* spellInfo) { diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp index 780669e1d..3599a943b 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp @@ -39,10 +39,9 @@ enum Says enum Spells { - SPELL_POISONOUS_BLOOD = 24321, - SPELL_BLOOD_SIPHON_HEAL = 24322, - SPELL_BLOOD_SIPHON_DMG = 24323, SPELL_BLOOD_SIPHON = 24324, + SPELL_BLOOD_SIPHON_HEAL = 24322, + SPELL_BLOOD_SIPHON_DAMAGE = 24323, SPELL_CORRUPTED_BLOOD = 24328, SPELL_CAUSE_INSANITY = 24327, SPELL_ENRAGE = 24318, @@ -51,7 +50,8 @@ enum Spells SPELL_ASPECT_OF_VENOXIS = 24688, SPELL_ASPECT_OF_MARLI = 24686, SPELL_ASPECT_OF_THEKAL = 24689, - SPELL_ASPECT_OF_ARLOKK = 24690 + SPELL_ASPECT_OF_ARLOKK = 24690, + SPELL_POISONOUS_BLOOD = 24321 }; enum Events @@ -265,31 +265,39 @@ public: } }; -class spell_hakkar_blood_siphon : public SpellScript +class spell_blood_siphon : public SpellScript { - PrepareSpellScript(spell_hakkar_blood_siphon); + PrepareSpellScript(spell_blood_siphon); bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_BLOOD_SIPHON_HEAL, SPELL_BLOOD_SIPHON_DMG }); + return ValidateSpellInfo({ SPELL_BLOOD_SIPHON_DAMAGE, SPELL_BLOOD_SIPHON_HEAL }); } - void OnSpellHit() + void FilterTargets(std::list& targets) { - Unit* caster = GetCaster(); - Unit* target = GetHitUnit(); - if (!caster || !target) - return; + // Max. 20 targets + if (!targets.empty()) + { + Acore::Containers::RandomResize(targets, 20); + } + } - if (target->HasAura(SPELL_POISONOUS_BLOOD)) - target->CastSpell(caster, SPELL_BLOOD_SIPHON_DMG, true); - else - target->CastSpell(caster, SPELL_BLOOD_SIPHON_HEAL, true); + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + if (Player* player = GetHitPlayer()) + { + player->CastSpell(caster, player->HasAura(SPELL_POISONOUS_BLOOD) ? SPELL_BLOOD_SIPHON_DAMAGE : SPELL_BLOOD_SIPHON_HEAL, true); + } + } } void Register() override { - OnHit += SpellHitFn(spell_hakkar_blood_siphon::OnSpellHit); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_blood_siphon::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_blood_siphon::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; @@ -299,5 +307,5 @@ void AddSC_boss_hakkar() new at_zulgurub_entrance_speech(); new at_zulgurub_bridge_speech(); new at_zulgurub_temple_speech(); - RegisterSpellScript(spell_hakkar_blood_siphon); + RegisterSpellScript(spell_blood_siphon); } diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 16069658b..37c91c9e4 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -536,7 +536,7 @@ enum SpellAttr4 : uint32 // EnumUtils: DESCRIBE THIS enum SpellAttr5 : uint32 { - SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL = 0x00000001, // TITLE Can be channeled while moving + SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL = 0x00000001, // TITLE Can be channeled while moving/casting SPELL_ATTR5_NO_REAGENT_COST_WITH_AURA = 0x00000002, // TITLE No reagents during arena preparation SPELL_ATTR5_REMOVE_ENTERING_ARENA = 0x00000004, // TITLE Remove when entering arena DESCRIPTION Force this aura to be removed on entering arena, regardless of other properties SPELL_ATTR5_ALLOW_WHILE_STUNNED = 0x00000008, // TITLE Usable while stunned diff --git a/src/server/shared/enuminfo_SharedDefines.cpp b/src/server/shared/enuminfo_SharedDefines.cpp index 06ea37987..751437861 100644 --- a/src/server/shared/enuminfo_SharedDefines.cpp +++ b/src/server/shared/enuminfo_SharedDefines.cpp @@ -802,7 +802,7 @@ AC_API_EXPORT EnumText EnumUtils::ToString(SpellAttr5 value) { switch (value) { - case SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL: return { "SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL", "Can be channeled while moving", "" }; + case SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL: return { "SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL", "Can be channeled while moving/casting", "" }; case SPELL_ATTR5_NO_REAGENT_COST_WITH_AURA: return { "SPELL_ATTR5_NO_REAGENT_COST_WITH_AURA", "No reagents during arena preparation", "" }; case SPELL_ATTR5_REMOVE_ENTERING_ARENA: return { "SPELL_ATTR5_REMOVE_ENTERING_ARENA", "Remove when entering arena", "Force this aura to be removed on entering arena, regardless of other properties" }; case SPELL_ATTR5_ALLOW_WHILE_STUNNED: return { "SPELL_ATTR5_ALLOW_WHILE_STUNNED", "Usable while stunned", "" };