// ***************************************************************************
// Function: LargerEffectTest
// Description: Return boolean true if the spell effect is to be ignored
// for stacking purposes
// ***************************************************************************
BOOL NBLargerEffectTest(PSPELL aSpell, PSPELL bSpell, int i)
{
LONG aAttrib = GetSpellNumEffects(aSpell) > i ? GetSpellAttrib(aSpell, i) : 254;
LONG bAttrib = GetSpellNumEffects(bSpell) > i ? GetSpellAttrib(bSpell, i) : 254;
if (aAttrib == bAttrib
&& (aAttrib == 0 // HP +/-: heals/regen/dd
|| aAttrib == 1 // Ac Mod
|| aAttrib == 55 // Add Effect: Absorb Damage
|| aAttrib == 69 // Max HP Mod
|| aAttrib == 79 // HP Mod
|| aAttrib == 114 // Aggro Multiplier
|| aAttrib == 127)) // Spell Haste
return abs(GetSpellBase(aSpell, i)) >= abs(GetSpellBase(bSpell, i));
return false;
}
// ***************************************************************************
// Function: TriggeringEffectSpell
// Description: Return boolean true if the spell effect is to be ignored
// for stacking purposes
// ***************************************************************************
BOOL NBTriggeringEffectSpell(PSPELL aSpell, int i)
{
LONG aAttrib = GetSpellNumEffects(aSpell) > i ? GetSpellAttrib(aSpell, i) : 254;
return (aAttrib == 85 // Add Proc
|| aAttrib == 374 // Trigger Spell
|| aAttrib == 419 // Add Proc
|| aAttrib == 442 // Trigger Effect
|| aAttrib == 443 // Trigger Effect
|| aAttrib == 453 // Trigger Effect
|| aAttrib == 454 // Trigger Effect
|| aAttrib == 475 // Trigger Spell Non-Item
|| aAttrib == 481); // Trigger Spell
}
// ***************************************************************************
// Function: DurationWindowTest
// Description: Return boolean true if the spell effect is to be ignored
// for stacking purposes
// ***************************************************************************
BOOL NBDurationWindowTest(PSPELL aSpell, PSPELL bSpell, int i)
{
LONG aAttrib = GetSpellNumEffects(aSpell) > i ? GetSpellAttrib(aSpell, i) : 254;
LONG bAttrib = GetSpellNumEffects(bSpell) > i ? GetSpellAttrib(bSpell, i) : 254;
if ((aSpell->SpellType != 1 && aSpell->SpellType != 2) || (bSpell->SpellType != 1 && bSpell->SpellType != 2) || (aSpell->DurationWindow == bSpell->DurationWindow))
return false;
return (!(aAttrib == bAttrib &&
(aAttrib == 2 // Attack Mod
|| aAttrib == 162))); // Mitigate Melee Damage
}
// ***************************************************************************
// Function: SpellEffectTest
// Description: Return boolean true if the spell effect is to be ignored
// for stacking purposes
// ***************************************************************************
BOOL NBSpellEffectTest(PSPELL aSpell, PSPELL bSpell, int i, BOOL bIgnoreTriggeringEffects, BOOL bIgnoreCrossDuration)
{
LONG aAttrib = GetSpellNumEffects(aSpell) > i ? GetSpellAttrib(aSpell, i) : 254;
LONG bAttrib = GetSpellNumEffects(bSpell) > i ? GetSpellAttrib(bSpell, i) : 254;
return ((aAttrib == 57 || bAttrib == 57) // Levitate
|| (aAttrib == 134 || bAttrib == 134) // Limit: Max Level
|| (aAttrib == 135 || bAttrib == 135) // Limit: Resist
|| (aAttrib == 136 || bAttrib == 136) // Limit: Target
|| (aAttrib == 137 || bAttrib == 137) // Limit: Effect
|| (aAttrib == 138 || bAttrib == 138) // Limit: SpellType
|| (aAttrib == 139 || bAttrib == 139) // Limit: Spell
|| (aAttrib == 140 || bAttrib == 140) // Limit: Min Duraction
|| (aAttrib == 141 || bAttrib == 141) // Limit: Instant
|| (aAttrib == 142 || bAttrib == 142) // Limit: Min Level
|| (aAttrib == 143 || bAttrib == 143) // Limit: Min Cast Time
|| (aAttrib == 144 || bAttrib == 144) // Limit: Max Cast Time
|| (aAttrib == 254 || bAttrib == 254) // Placeholder
|| (aAttrib == 311 || bAttrib == 311) // Limit: Combat Skills not Allowed
|| (aAttrib == 339 || bAttrib == 339) // Trigger DoT on cast
|| (aAttrib == 340 || bAttrib == 340) // Trigger DD on cast
|| (aAttrib == 348 || bAttrib == 348) // Limit: Min Mana
|| (aAttrib == 385 || bAttrib == 385) // Limit: SpellGroup
|| (aAttrib == 391 || bAttrib == 391) // Limit: Max Mana
|| (aAttrib == 403 || bAttrib == 403) // Limit: SpellClass
|| (aAttrib == 404 || bAttrib == 404) // Limit: SpellSubclass
|| (aAttrib == 411 || bAttrib == 411) // Limit: PlayerClass
|| (aAttrib == 412 || bAttrib == 412) // Limit: Race
|| (aAttrib == 414 || bAttrib == 414) // Limit: CastingSkill
|| (aAttrib == 415 || bAttrib == 415) // Limit: Item Class
|| (aAttrib == 420 || bAttrib == 420) // Limit: Use
|| (aAttrib == 421 || bAttrib == 421) // Limit: Use Amt
|| (aAttrib == 422 || bAttrib == 422) // Limit: Use Min
|| (aAttrib == 423 || bAttrib == 423) // Limit: Use Type
|| (aAttrib == 428 || bAttrib == 428) // Limit: Skill
|| (aAttrib == 442 || bAttrib == 442) // Trigger Effect
|| (aAttrib == 443 || bAttrib == 443) // Trigger Effect
|| (aAttrib == 453 || bAttrib == 453) // Trigger Effect
|| (aAttrib == 454 || bAttrib == 454) // Trigger Effect
|| (aAttrib == 460 || bAttrib == 460) // Limit: Include Non-Focusable
|| (aAttrib == 475 || bAttrib == 475) // Trigger Spell Non-Item
|| (aAttrib == 479 || bAttrib == 479) // Limit: Value
|| (aAttrib == 480 || bAttrib == 480) // Limit: Value
|| (aAttrib == 481 || bAttrib == 481) // Trigger Spell
|| (aAttrib == 485 || bAttrib == 485) // Limit: Caster Class
|| (aAttrib == 486 || bAttrib == 486) // Limit: Caster
|| (NBLargerEffectTest(aSpell, bSpell, i)) // Ignore if the new effect is greater than the old effect
|| (bIgnoreTriggeringEffects && (NBTriggeringEffectSpell(aSpell, i) || NBTriggeringEffectSpell(bSpell, i))) // Ignore triggering effects validation
|| (bIgnoreCrossDuration && NBDurationWindowTest(aSpell, bSpell, i))); // Ignore if the effects cross Long/Short Buff windows (with exceptions)
}
// ***************************************************************************
// Function: BuffStackTest
// Description: Return boolean true if the two spells will stack
// ***************************************************************************
BOOL NBBuffStackTest(PSPELL aSpell, PSPELL bSpell, BOOL bIgnoreTriggeringEffects, BOOL bIgnoreCrossDuration)
{
if (aSpell->ID == bSpell->ID)
return true;
// We need to loop over the largest of the two, this may seem silly but one could have stacking command blocks
// which we will always need to check.
LONG effects = max(GetSpellNumEffects(aSpell), GetSpellNumEffects(bSpell));
for (int i = 0; i < effects; i++) {
//Compare 1st Buff to 2nd. If Attrib[i]==254 its a place holder. If it is 10 it
//can be 1 of 3 things: PH(Base=0), CHA(Base>0), Lure(Base=-6). If it is Lure or
//Placeholder, exclude it so slots don't match up. Now Check to see if the slots
//have equal attribute values. If the do, they don't stack.
LONG aAttrib = 254, bAttrib = 254; // Default to placeholder ...
LONG aBase = 0, bBase = 0, aBase2 = 0, bBase2 = 0;
if (GetSpellNumEffects(aSpell) > i) {
aAttrib = GetSpellAttrib(aSpell, i);
aBase = GetSpellBase(aSpell, i);
aBase2 = GetSpellBase2(aSpell, i);
}
if (GetSpellNumEffects(bSpell) > i) {
bAttrib = GetSpellAttrib(bSpell, i);
bBase = GetSpellBase(bSpell, i);
bBase2 = GetSpellBase2(bSpell, i);
}
// if (TriggeringEffectSpell(aSpell, i) || TriggeringEffectSpell(bSpell, i)) {
// if (!BuffStackTest(GetSpellByID(TriggeringEffectSpell(aSpell, i) ? aBase2 : aSpell->ID), GetSpellByID(TriggeringEffectSpell(bSpell, i) ? bBase2 : bSpell->ID), bIgnoreTriggeringEffects))
// return false;
// }
if (bAttrib == aAttrib && !NBSpellEffectTest(aSpell, bSpell, i, bIgnoreTriggeringEffects, bIgnoreCrossDuration)) {
if (aAttrib == 55 && bAttrib == 55) {
//WriteChatf("Increase Absorb Damage by %d over %d", aBase, bBase);
return (aBase >= bBase);
}
else if (!((bAttrib == 10 && (bBase == -6 || bBase == 0)) ||
(aAttrib == 10 && (aBase == -6 || aBase == 0)) ||
(bAttrib == 79 && bBase > 0 && bSpell->TargetType == 6) ||
(aAttrib == 79 && aBase > 0 && aSpell->TargetType == 6) ||
(bAttrib == 0 && bBase < 0) ||
(aAttrib == 0 && aBase < 0) ||
(bAttrib == 148 || bAttrib == 149) ||
(aAttrib == 148 || aAttrib == 149))) {
return false;
}
}
//Check to see if second buffs blocks first buff:
//148: Stacking: Block new spell if slot %d is effect
//149: Stacking: Overwrite existing spell if slot %d is effect
if (bAttrib == 148 || bAttrib == 149) {
// in this branch we know bSpell has enough slots
int tmpSlot = GetSpellCalc(bSpell, i) - 200 - 1;
int tmpAttrib = bBase;
if (GetSpellNumEffects(aSpell) > tmpSlot) { // verify aSpell has that slot
if (GetSpellMax(bSpell, i) > 0) {
int tmpVal = abs(GetSpellMax(bSpell, i));
if (GetSpellAttrib(aSpell, tmpSlot) == tmpAttrib && GetSpellBase(aSpell, tmpSlot) < tmpVal) {
return false;
}
}
else if (GetSpellAttrib(aSpell, tmpSlot) == tmpAttrib) {
return false;
}
}
}
//Now Check to see if the first buff blocks second buff. This is necessary
//because only some spells carry the Block Slot. Ex. Brells and Spiritual
//Vigor don't stack Brells has 1 slot total, for HP. Vigor has 4 slots, 2
//of which block Brells.
if (aAttrib == 148 || aAttrib == 149) {
// in this branch we know aSpell has enough slots
int tmpSlot = GetSpellCalc(aSpell, i) - 200 - 1;
int tmpAttrib = aBase;
if (GetSpellNumEffects(bSpell) > tmpSlot) { // verify bSpell has that slot
if (GetSpellMax(aSpell, i) > 0) {
int tmpVal = abs(GetSpellMax(aSpell, i));
if (GetSpellAttrib(bSpell, tmpSlot) == tmpAttrib && GetSpellBase(bSpell, tmpSlot) < tmpVal) {
return false;
}
}
else if (GetSpellAttrib(bSpell, tmpSlot) == tmpAttrib) {
return false;
}
}
}
}
return true;
}