fix(Core/Spells): Implement TrinityCore spell_group and spell_group_stack_rules (#23346)

Co-authored-by: treeston <treeston.mmoc@gmail.com>
Co-authored-by: Trisjdc <trisjdc@gmail.com>
Co-authored-by: QAston <none@none>
Co-authored-by: ariel- <ariel-@users.noreply.github.com>
Co-authored-by: Shauren <shauren.trinity@gmail.com>
Co-authored-by: Jelle Meeus <sogladev@gmail.com>
This commit is contained in:
Tereneckla
2025-11-28 19:01:25 +00:00
committed by GitHub
parent 55989205e7
commit 2f7f9bd72f
42 changed files with 1976 additions and 1705 deletions
+8 -171
View File
@@ -326,9 +326,9 @@ std::array<SpellImplicitTargetInfo::StaticData, TOTAL_SPELL_TARGETS> SpellImplic
SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex)
{
_spellInfo = spellInfo;
_effIndex = effIndex;
EffectIndex = effIndex;
Effect = spellEntry->Effect[effIndex];
ApplyAuraName = spellEntry->EffectApplyAuraName[effIndex];
ApplyAuraName = AuraType(spellEntry->EffectApplyAuraName[effIndex]);
Amplitude = spellEntry->EffectAmplitude[effIndex];
DieSides = spellEntry->EffectDieSides[effIndex];
RealPointsPerLevel = spellEntry->EffectRealPointsPerLevel[effIndex];
@@ -456,7 +456,7 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const
value += PointsPerComboPoint * comboPoints;
}
value = caster->ApplyEffectModifiers(_spellInfo, _effIndex, value);
value = caster->ApplyEffectModifiers(_spellInfo, EffectIndex, value);
// amount multiplication based on caster's level
if (!caster->IsControlledByPlayer() &&
@@ -501,7 +501,7 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const
break;
}
if ((sSpellMgr->GetSpellInfo(_spellInfo->Effects[_effIndex].TriggerSpell) && sSpellMgr->GetSpellInfo(_spellInfo->Effects[_effIndex].TriggerSpell)->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) && _spellInfo->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL))
if ((sSpellMgr->GetSpellInfo(_spellInfo->Effects[EffectIndex].TriggerSpell) && sSpellMgr->GetSpellInfo(_spellInfo->Effects[EffectIndex].TriggerSpell)->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) && _spellInfo->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL))
canEffectScale = false;
if (canEffectScale)
@@ -1579,122 +1579,6 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a
return SPELL_CAST_OK;
}
bool SpellInfo::IsStrongerAuraActive(Unit const* caster, Unit const* target) const
{
if (!target)
return false;
// xinef: check spell group
uint32 groupId = sSpellMgr->GetSpellGroup(Id);
if (!groupId)
return false;
SpellGroupSpecialFlags sFlag = sSpellMgr->GetSpellGroupSpecialFlags(Id);
if (sFlag & SPELL_GROUP_SPECIAL_FLAG_SKIP_STRONGER_CHECK)
return false;
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
{
// xinef: Skip Empty effects
if (!Effects[i].IsEffect())
continue;
// xinef: if non-aura effect is preset - return false
if (!Effects[i].IsAura())
return false;
// xinef: aura is periodic - return false
if (Effects[i].Amplitude)
return false;
// xinef: exclude dummy auras
if (Effects[i].ApplyAuraName == SPELL_AURA_DUMMY)
return false;
}
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
{
// xinef: skip non-aura efects
if (!Effects[i].IsAura())
return false;
Unit::AuraEffectList const& auraList = target->GetAuraEffectsByType((AuraType)Effects[i].ApplyAuraName);
for (Unit::AuraEffectList::const_iterator iter = auraList.begin(); iter != auraList.end(); ++iter)
{
// xinef: aura is not groupped or in different group
uint32 auraGroup = (*iter)->GetAuraGroup();
if (!auraGroup || auraGroup != groupId)
continue;
if (IsRankOf((*iter)->GetSpellInfo()) && (sFlag & SPELL_GROUP_SPECIAL_FLAG_SKIP_STRONGER_SAME_SPELL))
{
continue;
}
// xinef: check priority before effect mask
if (sFlag >= SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 && sFlag <= SPELL_GROUP_SPECIAL_FLAG_PRIORITY4)
{
SpellGroupSpecialFlags sFlagCurr = sSpellMgr->GetSpellGroupSpecialFlags((*iter)->GetId());
if (sFlagCurr >= SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 && sFlagCurr <= SPELL_GROUP_SPECIAL_FLAG_PRIORITY4 && sFlagCurr < sFlag)
{
return true;
}
}
// xinef: check aura effect equal auras only, some auras have different effects on different ranks - check rank also
if (!IsAuraEffectEqual((*iter)->GetSpellInfo()) && !IsRankOf((*iter)->GetSpellInfo()))
continue;
// xinef: misc value mismatches
// xinef: commented, checked above
//if (Effects[i].MiscValue != (*iter)->GetMiscValue())
// continue;
// xinef: should not happen, or effect is not active - stronger one is present
AuraApplication* aurApp = (*iter)->GetBase()->GetApplicationOfTarget(target->GetGUID());
if (!aurApp || !aurApp->IsActive((*iter)->GetEffIndex()))
continue;
// xinef: assume that all spells are either positive or negative, otherwise they should not be in one group
// xinef: take custom values into account
int32 basePoints = Effects[i].BasePoints;
int32 duration = GetMaxDuration();
// xinef: should have the same id, can be different if spell is triggered
// xinef: have to fix spell mods for triggered spell, turn off current spellmodtakingspell for preparing and restore after
if (Player const* player = caster->GetSpellModOwner())
if (player->m_spellModTakingSpell && player->m_spellModTakingSpell->m_spellInfo->Id == Id)
basePoints = player->m_spellModTakingSpell->GetSpellValue()->EffectBasePoints[i];
int32 curValue = std::abs(Effects[i].CalcValue(caster, &basePoints));
int32 auraValue = (sFlag & SPELL_GROUP_SPECIAL_FLAG_BASE_AMOUNT_CHECK) ?
std::abs((*iter)->GetSpellInfo()->Effects[(*iter)->GetEffIndex()].CalcValue((*iter)->GetCaster())) :
std::abs((*iter)->GetAmount());
// xinef: for same spells, divide amount by stack amount
if (Id == (*iter)->GetId())
auraValue /= (*iter)->GetBase()->GetStackAmount();
if (curValue < auraValue)
return true;
// xinef: little hack, if current spell is the same as aura spell, asume it is not stronger
// xinef: if values are the same, duration mods should be taken into account but they are almost always passive
if (curValue == auraValue)
{
if (Id == (*iter)->GetId())
continue;
if (!(*iter)->GetBase()->IsPassive() && duration < (*iter)->GetBase()->GetDuration())
return true;
}
}
}
return false;
}
bool SpellInfo::IsAuraEffectEqual(SpellInfo const* otherSpellInfo) const
{
uint8 matchCount = 0;
@@ -1938,10 +1822,6 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta
if (HasEffect(SPELL_EFFECT_SELF_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT_NEW))
return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED;
// xinef: check if stronger aura is active
if (IsStrongerAuraActive(caster, unitTarget))
return SPELL_FAILED_AURA_BOUNCED;
return SPELL_CAST_OK;
}
@@ -2316,6 +2196,8 @@ SpellSpecificType SpellInfo::LoadSpellSpecific() const
case SPELL_AURA_TRACK_RESOURCES:
case SPELL_AURA_TRACK_STEALTHED:
return SPELL_SPECIFIC_TRACKER;
default:
break;
}
}
}
@@ -2399,6 +2281,8 @@ uint32 SpellInfo::GetMaxTicks() const
if (Effects[x].Amplitude != 0)
return DotDuration / Effects[x].Amplitude;
break;
default:
break;
}
}
@@ -2889,50 +2773,3 @@ void SpellInfo::_UnloadImplicitTargetConditionLists()
delete cur;
}
}
bool SpellInfo::CheckElixirStacking(Unit const* caster) const
{
if (!caster)
{
return true;
}
// xinef: check spell group
uint32 groupId = sSpellMgr->GetSpellGroup(Id);
if (groupId != SPELL_GROUP_GUARDIAN_AND_BATTLE_ELIXIRS)
{
return true;
}
SpellGroupSpecialFlags sFlag = sSpellMgr->GetSpellGroupSpecialFlags(Id);
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!Effects[i].IsAura())
{
continue;
}
Unit::AuraApplicationMap const& Auras = caster->GetAppliedAuras();
for (Unit::AuraApplicationMap::const_iterator itr = Auras.begin(); itr != Auras.end(); ++itr)
{
// xinef: aura is not groupped or in different group
uint32 auraGroup = sSpellMgr->GetSpellGroup(itr->first);
if (auraGroup != groupId)
{
continue;
}
// Cannot apply guardian/battle elixir if flask is present
if (sFlag == SPELL_GROUP_SPECIAL_FLAG_ELIXIR_BATTLE || sFlag == SPELL_GROUP_SPECIAL_FLAG_ELIXIR_GUARDIAN)
{
SpellGroupSpecialFlags sAuraFlag = sSpellMgr->GetSpellGroupSpecialFlags(itr->first);
if ((sAuraFlag & SPELL_GROUP_SPECIAL_FLAG_FLASK) == SPELL_GROUP_SPECIAL_FLAG_FLASK)
{
return false;
}
}
}
}
return true;
}