From 98eda3684d7dfa0da160c88539ac762336b7dc70 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Tue, 29 Jul 2025 05:36:36 -0700 Subject: [PATCH] feat(Core/Spell): implement `SPELL_ATTR2_CHAIN_FROM_CASTER` (#22515) Co-authored-by: Shauren --- src/server/game/Spells/Spell.cpp | 25 +++++++++++-------------- src/server/shared/SharedDefines.h | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index af13154c8..a0f46e9a8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2238,21 +2238,15 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (isBouncingFar) searchRadius *= chainTargets; + WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list tempTargets; - SearchAreaTargets(tempTargets, searchRadius, target, m_caster, objectType, selectType, condList); + SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList); tempTargets.remove(target); // remove targets which are always invalid for chain spells // for some spells allow only chain targets in front of caster (swipe for example) if (!isBouncingFar) - { - for (std::list::iterator itr = tempTargets.begin(); itr != tempTargets.end();) - { - std::list::iterator checkItr = itr++; - if (!m_caster->HasInArc(static_cast(M_PI), *checkItr)) - tempTargets.erase(checkItr); - } - } + tempTargets.remove_if([this](WorldObject* target) { return !m_caster->HasInArc(static_cast(M_PI), target); }); while (chainTargets) { @@ -2267,7 +2261,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if (deficit > maxHPDeficit && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2)) + if (deficit > maxHPDeficit && chainSource->IsWithinDist(unit, jumpRadius) && chainSource->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2)) { foundItr = itr; maxHPDeficit = deficit; @@ -2282,19 +2276,22 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) + if ((!isBouncingFar || chainSource->IsWithinDist(*itr, jumpRadius)) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } - else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) + else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } } // not found any valid target - chain ends if (foundItr == tempTargets.end()) break; - target = *foundItr; + + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER)) + chainSource = *foundItr; + + targets.push_back(*foundItr); tempTargets.erase(foundItr); - targets.push_back(target); --chainTargets; } } diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 2788167c4..e114c997d 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -465,7 +465,7 @@ enum SpellAttr2 : uint32 SPELL_ATTR2_ALWAYS_CAST_AS_UNIT = 0x00000200, // TITLE Unknown attribute 9@Attr2 SPELL_ATTR2_SPECIAL_TAMING_FLAG = 0x00000400, // TITLE Unknown attribute 10@Attr2 DESCRIPTION Related to taming? SPELL_ATTR2_NO_TARGET_PER_SECOND_COST = 0x00000800, // TITLE Health Funnel - SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Unknown attribute 12@Attr2 DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe + SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Chain from caster DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // TITLE Enchant persists when entering arena SPELL_ATTR2_ALLOW_WHILE_INVISIBLE = 0x00004000, // TITLE Unknown attribute 14@Attr2 SPELL_ATTR2_DO_NOT_CONSUME_IF_GAINED_DURING_CAST = 0x00008000, // TITLE Unused attribute 15@Attr2 DESCRIPTION not set in 3.3.5a