Core/Spells: rework part 1: Improved positive detection logic

# Conflicts:
#	src/server/game/Spells/SpellInfo.cpp
This commit is contained in:
ariel- authored and Ovahlord committed 2022-09-12 09:15:12 +02:00
1 parent 36b5e120af
commit 95bc5dcc40
5 files changed
+396 -228

No files matched your search

@@ -509,7 +509,7 @@ enum SpellAttr4
SPELL_ATTR4_TRIGGER_ACTIVATE = 0x00000200, // 9 initially disabled / trigger activate from event (Execute, Riposte, Deep Freeze end other)
SPELL_ATTR4_SPELL_VS_EXTEND_COST = 0x00000400, // 10 Rogue Shiv have this flag
SPELL_ATTR4_UNK11 = 0x00000800, // 11
SPELL_ATTR4_UNK12 = 0x00001000, // 12
SPELL_ATTR4_AURA_IS_BUFF = 0x00001000, // 12 Aura Is Buff
SPELL_ATTR4_UNK13 = 0x00002000, // 13
SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS = 0x00004000, // 14 doesn't break auras by damage from these spells
SPELL_ATTR4_HIDDEN_IN_SPELLBOOK = 0x00008000, // 15
+2 -1
View File
@@ -2911,7 +2911,8 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if ((effectMask & (1 << i)) && !aurSpellInfo->IsPositiveEffect(i))
// mod duration only for effects applying aura!
if ((aura_effmask & (1 << i)) && !aurSpellInfo->IsPositiveEffect(i))
{
positive = false;
break;
+377 -214
View File
@@ -3825,73 +3825,86 @@ void SpellInfo::_InitializeExplicitTargetMask()
ExplicitTargetMask = targetMask;
}
bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const
inline bool _isPositiveTarget(SpellInfo const* spellInfo, uint8 effIndex)
{
// not found a single positive spell with this attribute
if (HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF))
if (!spellInfo->Effects[effIndex].IsEffect())
return true;
return (spellInfo->Effects[effIndex].TargetA.GetCheckType() != TARGET_CHECK_ENEMY &&
spellInfo->Effects[effIndex].TargetB.GetCheckType() != TARGET_CHECK_ENEMY);
}
bool _isPositiveEffectImpl(SpellInfo const* spellInfo, uint8 effIndex, std::unordered_set<std::pair<uint32, uint8>>& visited)
{
if (!spellInfo->Effects[effIndex].IsEffect())
return true;
// attribute may be already set in DB
if (!spellInfo->IsPositiveEffect(effIndex))
return false;
switch (SpellFamilyName)
// passive auras like talents are all positive
if (spellInfo->IsPassive())
return true;
// not found a single positive spell with this attribute
if (spellInfo->HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF))
return false;
if (spellInfo->HasAttribute(SPELL_ATTR4_AURA_IS_BUFF))
return true;
visited.insert({ spellInfo->Id, effIndex });
int32 bp = spellInfo->Effects[effIndex].CalcValue();
switch (spellInfo->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
switch (Id)
switch (spellInfo->Id)
{
case 29214: // Wrath of the Plaguebringer
case 34700: // Allergic Reaction
case 54836: // Wrath of the Plaguebringer
case 41914: // Parasitic Shadowfiend (Illidan)
case 41917: // Parasitic Shadowfiend (Illidan)
case 40268: // Spiritual Vengeance, Teron Gorefiend, Black Temple
case 61987: // Avenging Wrath Marker
case 61988: // Divine Shield exclude aura
case 64412: // Phase Punch, Algalon the Observer, Ulduar
case 72410: // Rune of Blood, Saurfang, Icecrown Citadel
case 71204: // Touch of Insignificance, Lady Deathwhisper, Icecrown Citadel
return false;
case 24732: // Bat Costume
case 30877: // Tag Murloc
case 61716: // Rabbit Costume
case 61734: // Noblegarden Bunny
case 62344: // Fists of Stone
case 50344: // Dream Funnel
case 61819: // Manabonked! (item)
case 61834: // Manabonked! (minigob)
return true;
default:
break;
}
break;
case SPELLFAMILY_MAGE:
// Impact
if (SpellIconID == 45)
return true;
// Arcane Missiles
if (SpellFamilyFlags[0] == 0x00000800)
return false;
break;
case SPELLFAMILY_PRIEST:
switch (Id)
{
case 64844: // Divine Hymn
case 64904: // Hymn of Hope
case 47585: // Dispersion
case 73523: // Rigor Mortis
return true;
default:
break;
}
break;
case SPELLFAMILY_ROGUE:
switch (Id)
switch (spellInfo->Id)
{
// Envenom must be considered as a positive effect even though it deals damage
case 32645: // Envenom
// Slice and Dice. Prevents breaking Stealth
case 5171: // Slice and Dice (Rank 1)
case 6774: // Slice and Dice (Rank 2)
return true;
case 40251: // Shadow of Death, Teron Gorefiend, Black Temple
return false;
default:
break;
}
break;
case SPELLFAMILY_PALADIN:
case SPELLFAMILY_WARRIOR:
// Slam, Execute
if ((spellInfo->SpellFamilyFlags[0] & 0x20200000) != 0)
return false;
break;
default:
break;
}
switch (Mechanic)
switch (spellInfo->Mechanic)
{
case MECHANIC_IMMUNE_SHIELD:
return true;
@@ -3900,223 +3913,373 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const
}
// Special case: effects which determine positivity of whole spell
if (spellInfo->HasAttribute(SPELL_ATTR1_DONT_REFRESH_DURATION_ON_RECAST)) // @todo: rename to and handle SPELL_ATTR1_AURA_UNIQUE
{
// check for targets, there seems to be an assortment of dummy triggering spells that should be negative
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (!_isPositiveTarget(spellInfo, i))
return false;
}
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (Effects[i].IsAura())
switch (spellInfo->Effects[i].Effect)
{
switch (Effects[i].ApplyAuraName)
case SPELL_EFFECT_HEAL:
case SPELL_EFFECT_LEARN_SPELL:
case SPELL_EFFECT_SKILL_STEP:
case SPELL_EFFECT_HEAL_PCT:
case SPELL_EFFECT_ENERGIZE_PCT:
return true;
case SPELL_EFFECT_INSTAKILL:
if (i != effIndex && // for spells like 38044: instakill effect is negative but auras on target must count as buff
spellInfo->Effects[i].TargetA.GetTarget() == spellInfo->Effects[effIndex].TargetA.GetTarget() &&
spellInfo->Effects[i].TargetB.GetTarget() == spellInfo->Effects[effIndex].TargetB.GetTarget())
return false;
break;
default:
break;
}
if (spellInfo->Effects[i].IsAura())
{
switch (spellInfo->Effects[i].ApplyAuraName)
{
case SPELL_AURA_MOD_STEALTH:
case SPELL_AURA_MOD_UNATTACKABLE:
return true;
case SPELL_AURA_SCHOOL_HEAL_ABSORB:
case SPELL_AURA_CHANNEL_DEATH_ITEM:
case SPELL_AURA_EMPATHY:
return false;
default:
break;
}
}
}
switch (Effects[effIndex].Effect)
switch (spellInfo->Effects[effIndex].Effect)
{
case SPELL_EFFECT_DUMMY:
// some explicitly required dummy effect sets
switch (Id)
{
case 28441:
return false; // AB Effect 000
default:
break;
}
break;
// always positive effects (check before target checks that provided non-positive result in some case for positive effects)
case SPELL_EFFECT_HEAL:
case SPELL_EFFECT_LEARN_SPELL:
case SPELL_EFFECT_SKILL_STEP:
case SPELL_EFFECT_HEAL_PCT:
case SPELL_EFFECT_ENERGIZE_PCT:
return true;
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
case SPELL_EFFECT_SCHOOL_DAMAGE:
case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE:
case SPELL_EFFECT_HEALTH_LEECH:
case SPELL_EFFECT_INSTAKILL:
case SPELL_EFFECT_POWER_DRAIN:
case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF:
case SPELL_EFFECT_INTERRUPT_CAST:
case SPELL_EFFECT_PICKPOCKET:
case SPELL_EFFECT_GAMEOBJECT_DAMAGE:
case SPELL_EFFECT_DURABILITY_DAMAGE:
case SPELL_EFFECT_DURABILITY_DAMAGE_PCT:
case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
case SPELL_EFFECT_TAMECREATURE:
case SPELL_EFFECT_DISTRACT:
return false;
// non-positive aura use
case SPELL_EFFECT_APPLY_AURA:
case SPELL_EFFECT_APPLY_AURA_2:
case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
{
switch (Effects[effIndex].ApplyAuraName)
case SPELL_EFFECT_ENERGIZE:
case SPELL_EFFECT_ENERGIZE_PCT:
case SPELL_EFFECT_HEAL_PCT:
case SPELL_EFFECT_HEAL_MAX_HEALTH:
case SPELL_EFFECT_HEAL_MECHANICAL:
return true;
case SPELL_EFFECT_KNOCK_BACK:
case SPELL_EFFECT_CHARGE:
case SPELL_EFFECT_PERSISTENT_AREA_AURA:
case SPELL_EFFECT_ATTACK_ME:
case SPELL_EFFECT_POWER_BURN:
// check targets
if (!_isPositiveTarget(spellInfo, effIndex))
return false;
break;
case SPELL_EFFECT_DISPEL:
// non-positive dispel
switch (spellInfo->Effects[effIndex].MiscValue)
{
case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative)
case SPELL_AURA_MOD_STAT:
case SPELL_AURA_MOD_SKILL:
case SPELL_AURA_MOD_DODGE_PERCENT:
case SPELL_AURA_MOD_HEALING_PCT:
case SPELL_AURA_MOD_HEALING_DONE:
case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
if (Effects[effIndex].CalcValue() < 0)
return false;
break;
case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative)
if (Effects[effIndex].CalcValue() > 0)
return false;
break;
case SPELL_AURA_MOD_CRIT_PCT:
case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
if (Effects[effIndex].CalcValue() > 0)
return true; // some expected positive spells have SPELL_ATTR1_NEGATIVE
break;
case SPELL_AURA_ADD_TARGET_TRIGGER:
return true;
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
if (!deep)
{
if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(Effects[effIndex].TriggerSpell))
{
// negative targets of main spell return early
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!spellTriggeredProto->Effects[i].Effect)
continue;
// if non-positive trigger cast targeted to positive target this main cast is non-positive
// this will place this spell auras as debuffs
if (_IsPositiveTarget(spellTriggeredProto->Effects[i].TargetA.GetTarget(), spellTriggeredProto->Effects[i].TargetB.GetTarget()) && !spellTriggeredProto->_IsPositiveEffect(i, true))
return false;
}
}
}
break;
case SPELL_AURA_PROC_TRIGGER_SPELL:
// many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example)
break;
case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment.
if (effIndex == 0 && Effects[1].Effect == 0 && Effects[2].Effect == 0)
return false; // but all single stun aura spells is negative
break;
case SPELL_AURA_MOD_PACIFY_SILENCE:
if (Id == 24740) // Wisp Costume
return true;
case DISPEL_STEALTH:
case DISPEL_INVISIBILITY:
case DISPEL_ENRAGE:
return false;
case SPELL_AURA_MOD_ROOT:
case SPELL_AURA_MOD_SILENCE:
case SPELL_AURA_GHOST:
case SPELL_AURA_PERIODIC_LEECH:
case SPELL_AURA_MOD_STALKED:
case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
case SPELL_AURA_PREVENT_RESURRECTION:
return false;
case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also.
// part of negative spell if cast at self (prevent cancel)
if (Effects[effIndex].TargetA.GetTarget() == TARGET_UNIT_CASTER)
return false;
break;
case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also
// part of positive spell if cast at self
if (Effects[effIndex].TargetA.GetTarget() != TARGET_UNIT_CASTER)
return false;
// but not this if this first effect (didn't find better check)
if (HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF) && effIndex == 0)
return false;
break;
case SPELL_AURA_MECHANIC_IMMUNITY:
{
// non-positive immunities
switch (Effects[effIndex].MiscValue)
{
case MECHANIC_BANDAGE:
case MECHANIC_SHIELD:
case MECHANIC_MOUNT:
case MECHANIC_INVULNERABILITY:
return false;
default:
break;
}
break;
}
case SPELL_AURA_ADD_FLAT_MODIFIER: // mods
case SPELL_AURA_ADD_PCT_MODIFIER:
{
// non-positive mods
switch (Effects[effIndex].MiscValue)
{
case SPELLMOD_COST: // dependent from bas point sign (negative -> positive)
if (Effects[effIndex].CalcValue() > 0)
{
if (!deep)
{
bool negative = true;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (i != effIndex)
if (_IsPositiveEffect(i, true))
{
negative = false;
break;
}
}
if (negative)
return false;
}
}
break;
default:
break;
}
break;
}
default:
break;
}
// also check targets
if (!_isPositiveTarget(spellInfo, effIndex))
return false;
break;
case SPELL_EFFECT_DISPEL_MECHANIC:
if (!_isPositiveTarget(spellInfo, effIndex))
{
// non-positive mechanic dispel on negative target
switch (spellInfo->Effects[effIndex].MiscValue)
{
case MECHANIC_BANDAGE:
case MECHANIC_SHIELD:
case MECHANIC_MOUNT:
case MECHANIC_INVULNERABILITY:
return false;
default:
break;
}
}
break;
case SPELL_EFFECT_THREAT:
case SPELL_EFFECT_MODIFY_THREAT_PERCENT:
// check targets AND basepoints
if (!_isPositiveTarget(spellInfo, effIndex) && bp > 0)
return false;
break;
}
default:
break;
}
// non-positive targets
if (!_IsPositiveTarget(Effects[effIndex].TargetA.GetTarget(), Effects[effIndex].TargetB.GetTarget()))
return false;
// negative spell if triggered spell is negative
if (!deep && !Effects[effIndex].ApplyAuraName && Effects[effIndex].TriggerSpell)
if (spellInfo->Effects[effIndex].IsAura())
{
if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(Effects[effIndex].TriggerSpell))
if (!spellTriggeredProto->_IsPositiveSpell())
// non-positive aura use
switch (spellInfo->Effects[effIndex].ApplyAuraName)
{
case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from basepoint sign (negative -> negative)
case SPELL_AURA_MOD_STAT:
case SPELL_AURA_MOD_SKILL:
case SPELL_AURA_MOD_DODGE_PERCENT:
case SPELL_AURA_MOD_HEALING_DONE:
case SPELL_AURA_MOD_DAMAGE_DONE_CREATURE:
case SPELL_AURA_OBS_MOD_HEALTH:
case SPELL_AURA_OBS_MOD_POWER:
case SPELL_AURA_MOD_CRIT_PCT:
case SPELL_AURA_MOD_HIT_CHANCE:
case SPELL_AURA_MOD_SPELL_HIT_CHANCE:
case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
case SPELL_AURA_MOD_RANGED_HASTE:
case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
case SPELL_AURA_HASTE_SPELLS:
case SPELL_AURA_MOD_RESISTANCE:
case SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE:
case SPELL_AURA_MOD_DETECT_RANGE:
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
case SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE:
case SPELL_AURA_MOD_INCREASE_SWIM_SPEED:
if (bp < 0)
return false;
break;
case SPELL_AURA_MOD_ATTACKSPEED: // some buffs have negative bp, check both target and bp
case SPELL_AURA_MOD_MELEE_HASTE:
case SPELL_AURA_MOD_RESISTANCE_PCT:
case SPELL_AURA_MOD_RATING:
case SPELL_AURA_MOD_ATTACK_POWER:
case SPELL_AURA_MOD_RANGED_ATTACK_POWER:
case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
case SPELL_AURA_MOD_SPEED_SLOW_ALL:
case SPELL_AURA_MELEE_SLOW:
case SPELL_AURA_MOD_ATTACK_POWER_PCT:
case SPELL_AURA_MOD_HEALING_DONE_PERCENT:
case SPELL_AURA_MOD_HEALING_PCT:
if (!_isPositiveTarget(spellInfo, effIndex) && bp < 0)
return false;
break;
case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from basepoint sign (positive -> negative)
case SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN:
case SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT:
case SPELL_AURA_MOD_SCHOOL_CRIT_DMG_TAKEN:
case SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE:
case SPELL_AURA_MOD_POWER_COST_SCHOOL:
case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT:
if (bp > 0)
return false;
break;
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: // check targets and basepoints (ex Recklessness)
if (!_isPositiveTarget(spellInfo, effIndex) && bp > 0)
return false;
break;
case SPELL_AURA_ADD_TARGET_TRIGGER:
return true;
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
if (!_isPositiveTarget(spellInfo, effIndex))
{
if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(spellInfo->Effects[effIndex].TriggerSpell))
{
// negative targets of main spell return early
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
// already seen this
if (visited.count({ spellTriggeredProto->Id, i }) > 0)
continue;
if (!spellTriggeredProto->Effects[i].Effect)
continue;
// if non-positive trigger cast targeted to positive target this main cast is non-positive
// this will place this spell auras as debuffs
if (_isPositiveTarget(spellTriggeredProto, i) && !_isPositiveEffectImpl(spellTriggeredProto, i, visited))
return false;
}
}
}
break;
case SPELL_AURA_MOD_STUN:
case SPELL_AURA_TRANSFORM:
case SPELL_AURA_MOD_DECREASE_SPEED:
case SPELL_AURA_MOD_FEAR:
case SPELL_AURA_MOD_TAUNT:
// special auras: they may have non negative target but still need to be marked as debuff
// checked again after all effects (SpellInfo::_InitializeSpellPositivity)
case SPELL_AURA_MOD_PACIFY:
case SPELL_AURA_MOD_PACIFY_SILENCE:
case SPELL_AURA_MOD_DISARM:
case SPELL_AURA_MOD_DISARM_OFFHAND:
case SPELL_AURA_MOD_DISARM_RANGED:
case SPELL_AURA_MOD_CHARM:
case SPELL_AURA_AOE_CHARM:
case SPELL_AURA_MOD_POSSESS:
case SPELL_AURA_MOD_LANGUAGE:
case SPELL_AURA_DAMAGE_SHIELD:
case SPELL_AURA_PROC_TRIGGER_SPELL:
case SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE:
case SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE:
case SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE:
case SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE:
case SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE:
case SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE:
case SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE:
case SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE:
// have positive and negative spells, check target
if (!_isPositiveTarget(spellInfo, effIndex))
return false;
break;
case SPELL_AURA_MOD_CONFUSE:
case SPELL_AURA_MOD_ROOT:
case SPELL_AURA_MOD_SILENCE:
case SPELL_AURA_MOD_DETAUNT:
case SPELL_AURA_GHOST:
case SPELL_AURA_PERIODIC_LEECH:
case SPELL_AURA_PERIODIC_MANA_LEECH:
case SPELL_AURA_MOD_STALKED:
case SPELL_AURA_PREVENT_RESURRECTION:
case SPELL_AURA_PERIODIC_DAMAGE:
case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
case SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS:
case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS:
return false;
case SPELL_AURA_MECHANIC_IMMUNITY:
{
// non-positive immunities
switch (spellInfo->Effects[effIndex].MiscValue)
{
case MECHANIC_BANDAGE:
case MECHANIC_SHIELD:
case MECHANIC_MOUNT:
case MECHANIC_INVULNERABILITY:
return false;
default:
break;
}
break;
}
case SPELL_AURA_ADD_FLAT_MODIFIER: // mods
case SPELL_AURA_ADD_PCT_MODIFIER:
{
switch (spellInfo->Effects[effIndex].MiscValue)
{
case SPELLMOD_CASTING_TIME: // dependent from basepoint sign (positive -> negative)
case SPELLMOD_ACTIVATION_TIME:
case SPELLMOD_SPELL_COST_REFUND_ON_FAIL:
case SPELLMOD_GLOBAL_COOLDOWN:
if (bp > 0)
return false;
break;
case SPELLMOD_COOLDOWN:
case SPELLMOD_COST:
if (!spellInfo->IsPositive() && bp > 0) // dependent on prev effects too (ex Arcane Power)
return false;
break;
case SPELLMOD_EFFECT1: // always positive
case SPELLMOD_EFFECT2:
case SPELLMOD_EFFECT3:
case SPELLMOD_ALL_EFFECTS:
case SPELLMOD_THREAT:
case SPELLMOD_DAMAGE_MULTIPLIER:
case SPELLMOD_VALUE_MULTIPLIER:
return true;
case SPELLMOD_DURATION:
case SPELLMOD_CRITICAL_CHANCE:
case SPELLMOD_DAMAGE:
case SPELLMOD_JUMP_TARGETS:
if (!spellInfo->IsPositive() && bp < 0) // dependent on prev effects too
return false;
break;
default: // dependent from basepoint sign (negative -> negative)
if (bp < 0)
return false;
break;
}
break;
}
default:
break;
}
}
// negative spell if triggered spell is negative
if (!spellInfo->Effects[effIndex].ApplyAuraName && spellInfo->Effects[effIndex].TriggerSpell)
{
if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(spellInfo->Effects[effIndex].TriggerSpell))
{
// spells with at least one negative effect are considered negative
// some self-applied spells have negative effects but in self casting case negative check ignored.
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
// already seen this
if (visited.count({ spellTriggeredProto->Id, i }) > 0)
continue;
if (!spellTriggeredProto->Effects[i].IsEffect())
continue;
if (!_isPositiveEffectImpl(spellTriggeredProto, i, visited))
return false;
}
}
}
// ok, positive
return true;
}
bool SpellInfo::_IsPositiveSpell() const
void SpellInfo::_InitializeSpellPositivity()
{
// spells with at least one negative effect are considered negative
// some self-applied spells have negative effects but in self casting case negative check ignored.
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (!_IsPositiveEffect(i, true))
return false;
return true;
}
std::unordered_set<std::pair<uint32 /*spellId*/, uint8 /*effIndex*/>> visited;
bool SpellInfo::_IsPositiveTarget(uint32 targetA, uint32 targetB)
{
// non-positive targets
switch (targetA)
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (!_isPositiveEffectImpl(this, i, visited))
AttributesCu |= (SPELL_ATTR0_CU_NEGATIVE_EFF0 << i);
// additional checks after effects marked
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
case TARGET_UNIT_NEARBY_ENEMY:
case TARGET_UNIT_TARGET_ENEMY:
case TARGET_UNIT_SRC_AREA_ENEMY:
case TARGET_UNIT_DEST_AREA_ENEMY:
case TARGET_UNIT_CONE_ENEMY_24:
case TARGET_UNIT_CONE_CASTER_TO_DEST_ENEMY:
case TARGET_DEST_DYNOBJ_ENEMY:
case TARGET_DEST_TARGET_ENEMY:
return false;
default:
break;
if (!Effects[i].IsEffect() || !IsPositiveEffect(i))
continue;
switch (Effects[i].ApplyAuraName)
{
// has other non positive effect?
// then it should be marked negative despite of targets (ex 8510, 8511, 8893, 10267)
case SPELL_AURA_DUMMY:
case SPELL_AURA_MOD_STUN:
case SPELL_AURA_MOD_FEAR:
case SPELL_AURA_MOD_TAUNT:
case SPELL_AURA_TRANSFORM:
case SPELL_AURA_MOD_ATTACKSPEED:
case SPELL_AURA_MOD_DECREASE_SPEED:
if (!IsPositive())
AttributesCu |= (SPELL_ATTR0_CU_NEGATIVE_EFF0 << i);
break;
default:
break;
}
}
if (targetB)
return _IsPositiveTarget(targetB, 0);
return true;
}
SpellTargetRestrictionsEntry const* SpellInfo::GetSpellTargetRestrictions() const
+1 -3
View File
@@ -628,9 +628,7 @@ class TC_GAME_API SpellInfo
private:
// loading helpers
void _InitializeExplicitTargetMask();
bool _IsPositiveEffect(uint8 effIndex, bool deep) const;
bool _IsPositiveSpell() const;
static bool _IsPositiveTarget(uint32 targetA, uint32 targetB);
void _InitializeSpellPositivity();
void _LoadSpellSpecific();
void _LoadAuraState();
void _LoadSpellDiminishInfo();
+15 -9
View File
@@ -2649,7 +2649,20 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
continue;
}
// TODO: validate attributes
if ((attributes & SPELL_ATTR0_CU_NEGATIVE) != 0)
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (spellInfo->Effects[i].IsEffect())
continue;
if ((attributes & (SPELL_ATTR0_CU_NEGATIVE_EFF0 << i)) != 0)
{
TC_LOG_ERROR("sql.sql", "Table `spell_custom_attr` has attribute SPELL_ATTR0_CU_NEGATIVE_EFF%u for spell %u with no EFFECT_%u", uint32(i), spellId, uint32(i));
continue;
}
}
}
spellInfo->AttributesCu |= attributes;
++count;
@@ -2849,14 +2862,7 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
spellInfo->AttributesCu |= SPELL_ATTR0_CU_SCHOOLMASK_NORMAL_WITH_MAGIC;
}
if (!spellInfo->_IsPositiveEffect(EFFECT_0, false))
spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF0;
if (!spellInfo->_IsPositiveEffect(EFFECT_1, false))
spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF1;
if (!spellInfo->_IsPositiveEffect(EFFECT_2, false))
spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF2;
spellInfo->_InitializeSpellPositivity();
switch (spellInfo->SpellFamilyName)
{