diff --git a/changelog.txt b/changelog.txt index eabbad6fd..c4fa6cecf 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,22 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 06/17/2014 == +Kayen: Implemented SE_AStacker, SE_BStacker, SE_CStacker, SE_DStacker. +These effects when present in buffs prevent each other from stacking, +Any effect with B prevents A, C prevents B, D prevents C. +Kayen: Implemented SE_DamageModifier2 (Stacks with SE_DamageModifier, mods damage by skill type) +Kayen: Implemented SE_AddHatePct (Modifies +/- your total hate on NPC by percent) +Kayen: Implemented SE_AddHateOverTimePct (Modifies +/- your total hate on NPC by percent over time) +Kayen: Implemented SE_DoubleRiposte (Modifies +/- your double riposte chance) *Not used in any live effects +Kayen: Implemented SE_Berserk (Sets client as 'Berserk' giving chance to crippling blow) *Not used in any live effects +Kayen: Implemented SE_Vampirsm (Stackable lifetap from melee effect) *Not used in any live effects +Kayen: Minor fixes to how lifetap from melee effects are calced. Removed arbitrary hard cap of 100%, Negative value will now dmg client. +Kayen: Fix to issue that prevented NPC's from receiving HP Regeneration derived from spell buffs. +Kayen: Fixes and Updates for melee and spell mitigation runes. +Kayen: Update to SE_NegateAttack, 'max' value can now set upper limit of damage absorbed. DOT ticks will no longer be absorbed. +Kayen: Implemented SE_Metabolism - Modifies food/drink consumption rates. [Data for AA is already in database] +Kayen: Update to SE_BalanaceMana, SE_BalanceHP to support limit value which caps max mana/hp that can be taken per player. + == 06/13/2014 == Kayen: For table 'npc_spell_effects_entries' setting se_max for damage shield effects (59) will now determine the DS Type (ie burning) Setting se_max to 1 for SkillDamageTaken effects (127) will allow for stackable mitigation/weakness same as quest function ModSkillDmgTaken. diff --git a/common/spdat.cpp b/common/spdat.cpp index 013a917f1..3c4989a67 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1009,6 +1009,19 @@ uint32 GetMorphTrigger(uint32 spell_id) return 0; } +bool IsCastonFadeDurationSpell(uint16 spell_id) +{ + for (int i = 0; i < EFFECT_COUNT; ++i) { + if (spells[spell_id].effectid[i] == SE_ImprovedSpellEffect + || spells[spell_id].effectid[i] == SE_BossSpellTrigger + || spells[spell_id].effectid[i] == SE_CastOnWearoff){ + + return true; + } + } + return false; +} + uint32 GetPartialMeleeRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) diff --git a/common/spdat.h b/common/spdat.h index ada9e3249..b302e8314 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -190,9 +190,9 @@ typedef enum { #define SE_DivineAura 40 // implemented #define SE_Destroy 41 // implemented - Disintegrate, Banishment of Shadows #define SE_ShadowStep 42 // implemented -//#define SE_Berserk 43 // not used -#define SE_Lycanthropy 44 // implemented -//#define SE_Vampirism 45 // not used +#define SE_Berserk 43 // implemented (*not used in any known live spell) Makes client 'Berserk' giving crip blow chance. +#define SE_Lycanthropy 44 // implemented +#define SE_Vampirism 45 // implemented (*not used in any known live spell) Stackable lifetap from melee. #define SE_ResistFire 46 // implemented #define SE_ResistCold 47 // implemented #define SE_ResistPoison 48 // implemented @@ -307,7 +307,7 @@ typedef enum { #define SE_SpellDamageShield 157 // implemented - Petrad's Protection #define SE_Reflect 158 // implemented #define SE_AllStats 159 // implemented -#define SE_MakeDrunk 160 // implemented - poorly though, should check against tolerance +//#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance) #define SE_MitigateSpellDamage 161 // implemented - rune with max value #define SE_MitigateMeleeDamage 162 // implemented - rune with max value #define SE_NegateAttacks 163 // implemented @@ -370,7 +370,7 @@ typedef enum { #define SE_SkillDamageAmount 220 // implemented #define SE_Packrat 221 // implemented as bonus #define SE_BlockBehind 222 // implemented - Chance to block from behind (with our without Shield) -//#define SE_DoubleRiposte 223 // not used +#define SE_DoubleRiposte 223 // implemented - Chance to double riposte [not used on live] #define SE_GiveDoubleRiposte 224 // implemented[AA] #define SE_GiveDoubleAttack 225 // implemented[AA] - Allow any class to double attack with set chance. #define SE_TwoHandBash 226 // *not implemented as bonus @@ -380,7 +380,7 @@ typedef enum { //#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability #define SE_StunBashChance 231 // implemented - increase chance to stun from bash. #define SE_DivineSave 232 // implemented (base1 == % chance on death to insta-res) (base2 == spell cast on save) -//#define SE_Metabolism 233 // *not implemented - (Crown of Feathers) Increase metabolism? +#define SE_Metabolism 233 // implemented - Modifies food/drink consumption rates. //#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison #define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live. //#define SE_FreePet 236 // not used @@ -593,20 +593,20 @@ typedef enum { #define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) #define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y //#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. -//#define SE_AStacker 446 // *not implementet - bufff stacking blocker ? (26219 | Qirik's Watch) -//#define SE_BStacker 447 // *not implemented -//#define SE_CStacker 448 // *not implemented -//#define SE_DStacker 449 // *not implemented +#define SE_AStacker 446 // implementet - bufff stacking blocker (26219 | Qirik's Watch) +#define SE_BStacker 447 // implemented +#define SE_CStacker 448 // implemented +#define SE_DStacker 449 // implemented #define SE_MitigateDotDamage 450 // implemented DOT spell mitigation rune with max value #define SE_MeleeThresholdGuard 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage #define SE_SpellThresholdGuard 452 // implemented Partial Spell Rune that only is lowered if spell hits are over X amount of damage #define SE_TriggerMeleeThreshold 453 // implemented Trigger effect on X amount of melee damage taken #define SE_TriggerSpellThreshold 454 // implemented Trigger effect on X amount of spell damage taken -//#define SE_AddHatePct 455 // not used -//#define SE_AddHateOverTimePct 456 // not used +#define SE_AddHatePct 455 // implemented Modify total hate by % +#define SE_AddHateOverTimePct 456 // implemented Modify total hate by % over time. //#define SE_ResourceTap 457 // not used //#define SE_FactionModPct 458 // not used -//#define SE_DamageModifier2 459 // *not implemented - Modifies melee damage by skill type +#define SE_DamageModifier2 459 // implemented - Modifies melee damage by skill type // LAST @@ -833,6 +833,7 @@ bool IsBuffSpell(uint16 spell_id); bool IsPersistDeathSpell(uint16 spell_id); bool IsSuspendableSpell(uint16 spell_id); uint32 GetMorphTrigger(uint32 spell_id); +bool IsCastonFadeDurationSpell(uint16 spell_id); uint32 GetPartialMeleeRuneReduction(uint32 spell_id); uint32 GetPartialMagicRuneReduction(uint32 spell_id); uint32 GetPartialMeleeRuneAmount(uint32 spell_id); diff --git a/zone/attack.cpp b/zone/attack.cpp index 34a1de47d..99682eee5 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3176,7 +3176,11 @@ int32 Mob::ReduceDamage(int32 damage) if(!TryFadeEffect(slot)) BuffFadeBySlot(slot , true); } - return -6; + + if (spellbonuses.NegateAttacks[2] && (damage > spellbonuses.NegateAttacks[2])) + damage -= spellbonuses.NegateAttacks[2]; + else + return -6; } } @@ -3188,11 +3192,11 @@ int32 Mob::ReduceDamage(int32 damage) { DisableMeleeRune = true; int damage_to_reduce = damage * spellbonuses.MeleeThresholdGuard[0] / 100; - if(damage_to_reduce > buffs[slot].melee_rune) + if(damage_to_reduce >= buffs[slot].melee_rune) { mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); - damage -= damage_to_reduce; + damage -= buffs[slot].melee_rune; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); } @@ -3212,11 +3216,15 @@ int32 Mob::ReduceDamage(int32 damage) if(slot >= 0) { int damage_to_reduce = damage * spellbonuses.MitigateMeleeRune[0] / 100; - if(damage_to_reduce > buffs[slot].melee_rune) + + if (spellbonuses.MitigateMeleeRune[2] && (damage_to_reduce > spellbonuses.MitigateMeleeRune[2])) + damage_to_reduce = spellbonuses.MitigateMeleeRune[2]; + + if(spellbonuses.MitigateMeleeRune[3] && (damage_to_reduce >= buffs[slot].melee_rune)) { mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); - damage -= damage_to_reduce; + damage -= buffs[slot].melee_rune; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); } @@ -3224,7 +3232,10 @@ int32 Mob::ReduceDamage(int32 damage) { mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); + + if (spellbonuses.MitigateMeleeRune[3]) + buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); + damage -= damage_to_reduce; } } @@ -3265,7 +3276,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi int32 slot = -1; // See if we block the spell outright first - if (spellbonuses.NegateAttacks[0]){ + if (!iBuffTic && spellbonuses.NegateAttacks[0]){ slot = spellbonuses.NegateAttacks[1]; if(slot >= 0) { if(--buffs[slot].numhits == 0) { @@ -3273,7 +3284,11 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if(!TryFadeEffect(slot)) BuffFadeBySlot(slot , true); } - return 0; + + if (spellbonuses.NegateAttacks[2] && (damage > spellbonuses.NegateAttacks[2])) + damage -= spellbonuses.NegateAttacks[2]; + else + return 0; } } @@ -3286,15 +3301,21 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if(slot >= 0) { int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100; - if(damage_to_reduce > buffs[slot].dot_rune) + + if (spellbonuses.MitigateDotRune[2] && (damage_to_reduce > spellbonuses.MitigateDotRune[2])) + damage_to_reduce = spellbonuses.MitigateDotRune[2]; + + if(spellbonuses.MitigateDotRune[3] && (damage_to_reduce >= buffs[slot].dot_rune)) { - damage -= damage_to_reduce; + damage -= buffs[slot].dot_rune; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); } else { - buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce); + if (spellbonuses.MitigateDotRune[3]) + buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce); + damage -= damage_to_reduce; } } @@ -3316,9 +3337,9 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi { DisableSpellRune = true; int damage_to_reduce = damage * spellbonuses.SpellThresholdGuard[0] / 100; - if(damage_to_reduce > buffs[slot].magic_rune) + if(damage_to_reduce >= buffs[slot].magic_rune) { - damage -= damage_to_reduce; + damage -= buffs[slot].magic_rune; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); } @@ -3337,11 +3358,15 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if(slot >= 0) { int damage_to_reduce = damage * spellbonuses.MitigateSpellRune[0] / 100; - if(damage_to_reduce > buffs[slot].magic_rune) + + if (spellbonuses.MitigateSpellRune[2] && (damage_to_reduce > spellbonuses.MitigateSpellRune[2])) + damage_to_reduce = spellbonuses.MitigateSpellRune[2]; + + if(spellbonuses.MitigateSpellRune[3] && (damage_to_reduce >= buffs[slot].magic_rune)) { mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateSpellDamage %d damage negated, %d" " damage remaining, fading buff.", damage_to_reduce, buffs[slot].magic_rune); - damage -= damage_to_reduce; + damage -= buffs[slot].magic_rune; if(!TryFadeEffect(slot)) BuffFadeBySlot(slot); } @@ -3349,7 +3374,10 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi { mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); - buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); + + if (spellbonuses.MitigateSpellRune[3]) + buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); + damage -= damage_to_reduce; } } @@ -4283,6 +4311,7 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack float critChance = 0.0f; + bool IsBerskerSPA = false; //1: Try Slay Undead if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){ @@ -4310,12 +4339,15 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack //are defined you will have an innate chance to hit at Level 1 regardless of bonuses. //Warning: Do not define these rules if you want live like critical hits. critChance += RuleI(Combat, MeleeBaseCritChance); - + if (IsClient()) { - critChance += RuleI(Combat, ClientBaseCritChance); + critChance += RuleI(Combat, ClientBaseCritChance); - if ((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12) { - if (IsBerserk()) + if (spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA) + IsBerskerSPA = true; + + if (((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12) || IsBerskerSPA) { + if (IsBerserk() || IsBerskerSPA) critChance += RuleI(Combat, BerserkBaseCritChance); else critChance += RuleI(Combat, WarBerBaseCritChance); @@ -4360,15 +4392,15 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack uint16 critMod = 200; bool crip_success = false; int16 CripplingBlowChance = GetCrippBlowChance(); - + //Crippling Blow Chance: The percent value of the effect is applied //to the your Chance to Critical. (ie You have 10% chance to critical and you //have a 200% Chance to Critical Blow effect, therefore you have a 20% Chance to Critical Blow. - if (CripplingBlowChance || IsBerserk()) { - if (!IsBerserk()) + if (CripplingBlowChance || (IsBerserk() || IsBerskerSPA)) { + if (!IsBerserk() && !IsBerskerSPA) critChance *= float(CripplingBlowChance)/100.0f; - if (IsBerserk() || MakeRandomFloat(0, 1) < critChance) { + if ((IsBerserk() || IsBerskerSPA) || MakeRandomFloat(0, 1) < critChance) { critMod = 400; crip_success = true; } @@ -4449,6 +4481,10 @@ void Mob::DoRiposte(Mob* defender) { defender->spellbonuses.GiveDoubleRiposte[0] + defender->itembonuses.GiveDoubleRiposte[0]; + DoubleRipChance = defender->aabonuses.DoubleRiposte + + defender->spellbonuses.DoubleRiposte + + defender->itembonuses.DoubleRiposte; + //Live AA - Double Riposte if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { mlog(COMBAT__ATTACKS, "Preforming a double riposed (%d percent chance)", DoubleRipChance); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index d699cdde2..3ce6becca 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1099,6 +1099,15 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_DamageModifier2: + { + if(base2 == -1) + newbon->DamageModifier2[HIGHEST_SKILL+1] += base1; + else + newbon->DamageModifier2[base2] += base1; + break; + } + case SE_SlayUndead: { if(newbon->SlayUndead[1] < base1) @@ -1107,6 +1116,11 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_DoubleRiposte: + { + newbon->DoubleRiposte += base1; + } + case SE_GiveDoubleRiposte: { //0=Regular Riposte 1=Skill Attack Riposte 2=Skill @@ -1234,6 +1248,10 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_Vampirism: + newbon->Vampirism += base1; + break; + case SE_FrenziedDevastation: newbon->FrenziedDevastation += base2; break; @@ -1242,6 +1260,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) newbon->SpellProcChance += base1; break; + case SE_Berserk: + newbon->BerserkSPA = true; + break; + + case SE_Metabolism: + newbon->Metabolism += base1; + break; + } } } @@ -1813,6 +1839,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + case SE_Vampirism: + newbon->Vampirism += effect_value; + break; + case SE_AllInstrumentMod: { if(effect_value > newbon->singingMod) @@ -1909,6 +1939,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + case SE_DamageModifier2: + { + if(base2 == -1) + newbon->DamageModifier2[HIGHEST_SKILL+1] += effect_value; + else + newbon->DamageModifier2[base2] += effect_value; + break; + } + case SE_MinDamageModifier: { if(base2 == -1) @@ -2256,9 +2295,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_NegateAttacks: { - if (!newbon->NegateAttacks[0]){ + if (!newbon->NegateAttacks[0] || + ((newbon->NegateAttacks[0] && newbon->NegateAttacks[2]) && (newbon->NegateAttacks[2] < max))){ newbon->NegateAttacks[0] = 1; newbon->NegateAttacks[1] = buffslot; + newbon->NegateAttacks[2] = max; } break; } @@ -2268,6 +2309,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (newbon->MitigateMeleeRune[0] < effect_value){ newbon->MitigateMeleeRune[0] = effect_value; newbon->MitigateMeleeRune[1] = buffslot; + newbon->MitigateMeleeRune[2] = base2; + newbon->MitigateMeleeRune[3] = max; } break; } @@ -2298,6 +2341,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (newbon->MitigateSpellRune[0] < effect_value){ newbon->MitigateSpellRune[0] = effect_value; newbon->MitigateSpellRune[1] = buffslot; + newbon->MitigateSpellRune[2] = base2; + newbon->MitigateSpellRune[3] = max; } break; } @@ -2307,6 +2352,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (newbon->MitigateDotRune[0] < effect_value){ newbon->MitigateDotRune[0] = effect_value; newbon->MitigateDotRune[1] = buffslot; + newbon->MitigateDotRune[2] = base2; + newbon->MitigateDotRune[3] = max; } break; } @@ -2553,6 +2600,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + case SE_DoubleRiposte: + { + newbon->DoubleRiposte += effect_value; + } + case SE_GiveDoubleRiposte: { //Only allow for regular double riposte chance. @@ -2661,6 +2713,31 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } break; + case SE_AStacker: + newbon->AStacker = true; + break; + + case SE_BStacker: + newbon->BStacker = true; + break; + + case SE_CStacker: + newbon->CStacker = true; + break; + + case SE_DStacker: + newbon->DStacker = true; + break; + + case SE_Berserk: + newbon->BerserkSPA = true; + break; + + + case SE_Metabolism: + newbon->Metabolism += effect_value; + break; + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -3438,6 +3515,17 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; } + case SE_DamageModifier2: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.DamageModifier2[e] = effect_value; + aabonuses.DamageModifier2[e] = effect_value; + itembonuses.DamageModifier2[e] = effect_value; + } + break; + } + case SE_MinDamageModifier: { for(int e = 0; e < HIGHEST_SKILL+1; e++) @@ -3933,6 +4021,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.MasteryofPast = effect_value; break; + case SE_DoubleRiposte: + spellbonuses.DoubleRiposte = effect_value; + itembonuses.DoubleRiposte = effect_value; + aabonuses.DoubleRiposte = effect_value; + break; + case SE_GiveDoubleRiposte: spellbonuses.GiveDoubleRiposte[0] = effect_value; itembonuses.GiveDoubleRiposte[0] = effect_value; @@ -4033,6 +4127,24 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) spellbonuses.AbsorbMagicAtt[0] = effect_value; spellbonuses.AbsorbMagicAtt[1] = -1; break; + + case SE_Berserk: + spellbonuses.BerserkSPA = false; + aabonuses.BerserkSPA = false; + itembonuses.BerserkSPA = false; + break; + + case SE_Vampirism: + spellbonuses.Vampirism = effect_value; + aabonuses.Vampirism = effect_value; + itembonuses.Vampirism = effect_value; + break; + + case SE_Metabolism: + spellbonuses.Metabolism = effect_value; + aabonuses.Metabolism = effect_value; + itembonuses.Metabolism = effect_value; + break; } } diff --git a/zone/client.cpp b/zone/client.cpp index 7aa7bd748..4ec3ce2d7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8134,20 +8134,12 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_ uint16 cons_mod = 180; - switch(GetAA(aaInnateMetabolism)){ - case 1: - cons_mod = cons_mod * 110 * RuleI(Character, ConsumptionMultiplier) / 10000; - break; - case 2: - cons_mod = cons_mod * 125 * RuleI(Character, ConsumptionMultiplier) / 10000; - break; - case 3: - cons_mod = cons_mod * 150 * RuleI(Character, ConsumptionMultiplier) / 10000; - break; - default: - cons_mod = cons_mod * RuleI(Character, ConsumptionMultiplier) / 100; - break; - } + int16 metabolism_bonus = spellbonuses.Metabolism + itembonuses.Metabolism + aabonuses.Metabolism; + + if (metabolism_bonus) + cons_mod = cons_mod * metabolism_bonus * RuleI(Character, ConsumptionMultiplier) / 10000; + else + cons_mod = cons_mod * RuleI(Character, ConsumptionMultiplier) / 100; if(type == ItemTypeFood) { diff --git a/zone/common.h b/zone/common.h index d5f516f63..f94962ee1 100644 --- a/zone/common.h +++ b/zone/common.h @@ -279,6 +279,7 @@ struct StatBonuses { int16 HitChance; //HitChance/15 == % increase i = Accuracy (Item: Accuracy) int16 HitChanceEffect[HIGHEST_SKILL+2]; //Spell effect Chance to Hit, straight percent increase int16 DamageModifier[HIGHEST_SKILL+2]; //i + int16 DamageModifier2[HIGHEST_SKILL+2]; //i int16 MinDamageModifier[HIGHEST_SKILL+2]; //i int16 ProcChance; // ProcChance/10 == % increase i = CombatEffects int16 ProcChanceSPA; // ProcChance from spell effects @@ -289,7 +290,8 @@ struct StatBonuses { int16 FlurryChance; int16 Accuracy[HIGHEST_SKILL+2]; //Accuracy/15 == % increase [Spell Effect: Accuracy) int16 HundredHands; //extra haste, stacks with all other haste i - int8 MeleeLifetap; //i + int16 MeleeLifetap; //i + int16 Vampirism; //i int16 HealRate; // Spell effect that influences effectiveness of heals int32 MaxHPChange; // Spell Effect int16 SkillDmgTaken[HIGHEST_SKILL+2]; // All Skills + -1 @@ -326,12 +328,12 @@ struct StatBonuses { uint16 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have. bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses) int16 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage - uint16 NegateAttacks[2]; // 0 = bool HasEffect 1 = Buff Slot - uint16 MitigateMeleeRune[2]; // 0 = Mitigation value 1 = Buff Slot + uint16 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit + uint16 MitigateMeleeRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per hit 3 = Rune Amt uint16 MeleeThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. uint16 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. - uint16 MitigateSpellRune[2]; // 0 = Mitigation value 1 = Buff Slot - uint16 MitigateDotRune[2]; // 0 = Mitigation value 1 = Buff Slot + uint16 MitigateSpellRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per spell 3 = Rune Amt + uint16 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt uint32 TriggerMeleeThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger uint32 TriggerSpellThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot @@ -350,6 +352,12 @@ struct StatBonuses { bool NegateIfCombat; // Bool Drop buff if cast or melee int8 Screech; // -1 = Will be blocked if another Screech is +(1) int16 AlterNPCLevel; // amount of lvls +/- + bool AStacker; // For buff stack blocking + bool BStacker; // For buff stack blocking + bool CStacker; // For buff stack blocking + bool DStacker; // For buff stack blocking + bool BerserkSPA; // berserk effect + int16 Metabolism; // Food/drink consumption rates. // AAs int8 Packrat; //weight reduction for items, 1 point = 10% @@ -378,6 +386,7 @@ struct StatBonuses { int16 PetCriticalHit; // Allow pets to critical hit with % value. int16 PetAvoidance; // Pet avoidance chance. int16 CombatStability; // Melee damage mitigation. + int16 DoubleRiposte; // Chance to double riposte int16 GiveDoubleRiposte[3]; // 0=Regular Chance, 1=Skill Attack Chance, 2=Skill uint16 RaiseSkillCap[2]; // Raise a specific skill cap (1 = value, 2=skill) int16 Ambidexterity; // Increase chance to duel wield by adding bonus 'skill'. diff --git a/zone/groups.cpp b/zone/groups.cpp index 3f5b376ee..6aee5f38b 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1095,7 +1095,7 @@ void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range) } -void Group::BalanceHP(int32 penalty, int32 range, Mob* caster) +void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit) { if (!caster) return; @@ -1103,7 +1103,7 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster) if (!range) range = 200; - int dmgtaken = 0, numMem = 0; + int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; float distance; float range2 = range*range; @@ -1114,7 +1114,12 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster) if(members[gi]){ distance = caster->DistNoRoot(*members[gi]); if(distance <= range2){ - dmgtaken += (members[gi]->GetMaxHP() - members[gi]->GetHP()); + + dmgtaken_tmp = members[gi]->GetMaxHP() - members[gi]->GetHP(); + if (limit && (dmgtaken_tmp > limit)) + dmgtaken_tmp = limit; + + dmgtaken += (dmgtaken_tmp); numMem += 1; } } @@ -1140,7 +1145,7 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster) } } -void Group::BalanceMana(int32 penalty, int32 range, Mob* caster) +void Group::BalanceMana(int32 penalty, int32 range, Mob* caster, int32 limit) { if (!caster) return; @@ -1151,14 +1156,19 @@ void Group::BalanceMana(int32 penalty, int32 range, Mob* caster) float distance; float range2 = range*range; - int manataken = 0, numMem = 0; + int manataken = 0, numMem = 0, manataken_tmp = 0; unsigned int gi = 0; for(; gi < MAX_GROUP_MEMBERS; gi++) { - if(members[gi]){ + if(members[gi] && (members[gi]->GetMaxMana() > 0)){ distance = caster->DistNoRoot(*members[gi]); if(distance <= range2){ - manataken += (members[gi]->GetMaxMana() - members[gi]->GetMana()); + + manataken_tmp = members[gi]->GetMaxMana() - members[gi]->GetMana(); + if (limit && (manataken_tmp > limit)) + manataken_tmp = limit; + + manataken += (manataken_tmp); numMem += 1; } } @@ -1166,6 +1176,10 @@ void Group::BalanceMana(int32 penalty, int32 range, Mob* caster) manataken += manataken * penalty / 100; manataken /= numMem; + + if (limit && (manataken > limit)) + manataken = limit; + for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) { if(members[gi]){ diff --git a/zone/groups.h b/zone/groups.h index c5a3f8ab1..fe21ff3a4 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -86,8 +86,8 @@ public: uint16 GetAvgLevel(); bool LearnMembers(); void VerifyGroup(); - void BalanceHP(int32 penalty, int32 range = 0, Mob* caster = nullptr); - void BalanceMana(int32 penalty, int32 range = 0, Mob* caster = nullptr); + void BalanceHP(int32 penalty, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); + void BalanceMana(int32 penalty, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); void HealGroup(uint32 heal_amt, Mob* caster, int32 range = 0); inline void SetGroupAAs(GroupLeadershipAA_Struct *From) { memcpy(&LeaderAbilities, From, sizeof(GroupLeadershipAA_Struct)); } inline void GetGroupAAs(GroupLeadershipAA_Struct *Into) { memcpy(Into, &LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); } diff --git a/zone/mob.cpp b/zone/mob.cpp index d5ff23a7d..06dbe4519 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4277,6 +4277,9 @@ int16 Mob::GetMeleeDamageMod_SE(uint16 skill) dmg_mod += itembonuses.DamageModifier[HIGHEST_SKILL+1] + spellbonuses.DamageModifier[HIGHEST_SKILL+1] + aabonuses.DamageModifier[HIGHEST_SKILL+1] + itembonuses.DamageModifier[skill] + spellbonuses.DamageModifier[skill] + aabonuses.DamageModifier[skill]; + dmg_mod += itembonuses.DamageModifier2[HIGHEST_SKILL+1] + spellbonuses.DamageModifier2[HIGHEST_SKILL+1] + aabonuses.DamageModifier2[HIGHEST_SKILL+1] + + itembonuses.DamageModifier2[skill] + spellbonuses.DamageModifier2[skill] + aabonuses.DamageModifier2[skill]; + if (HasShieldEquiped() && !IsOffHandAtk()) dmg_mod += itembonuses.ShieldEquipDmgMod[0] + spellbonuses.ShieldEquipDmgMod[0] + aabonuses.ShieldEquipDmgMod[0]; @@ -4334,22 +4337,19 @@ int16 Mob::GetSkillDmgAmt(uint16 skill) void Mob::MeleeLifeTap(int32 damage) { - if(damage > 0 && (spellbonuses.MeleeLifetap || itembonuses.MeleeLifetap || aabonuses.MeleeLifetap )) - { - int lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap; - - if(lifetap_amt > 100) - lifetap_amt = 100; - - else if (lifetap_amt < -99) - lifetap_amt = -99; + int16 lifetap_amt = 0; + lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap + + spellbonuses.Vampirism + itembonuses.Vampirism + aabonuses.Vampirism; + if(lifetap_amt && damage > 0){ lifetap_amt = damage * lifetap_amt / 100; - mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage); - //heal self for damage done.. - HealDamage(lifetap_amt); + + if (lifetap_amt > 0) + HealDamage(lifetap_amt); //Heal self for modified damage amount. + else + Damage(this, -lifetap_amt,0, SkillEvocation,false); //Dmg self for modified damage amount. } } diff --git a/zone/mob.h b/zone/mob.h index c3de2b576..4ab9d8a84 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -800,7 +800,7 @@ public: uint16 GetInstrumentMod(uint16 spell_id) const; int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, Mob *caster = nullptr, int ticsremaining = 0); int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); - virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr); + virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; } // HP Event diff --git a/zone/npc.cpp b/zone/npc.cpp index 3886d7c66..38989bccb 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -599,19 +599,19 @@ bool NPC::Process() //Lieka Edit:Fixing NPC regen.NPCs should regen to full during a set duration, not based on their HPs.Increase NPC's HPs by % of total HPs / tick. if((GetHP() < GetMaxHP()) && !IsPet()) { if(!IsEngaged()) {//NPC out of combat - if(hp_regen > OOCRegen) - SetHP(GetHP() + hp_regen); + if(GetNPCHPRegen() > OOCRegen) + SetHP(GetHP() + GetNPCHPRegen()); else SetHP(GetHP() + OOCRegen); } else - SetHP(GetHP()+hp_regen); + SetHP(GetHP()+GetNPCHPRegen()); } else if(GetHP() < GetMaxHP() && GetOwnerID() !=0) { if(!IsEngaged()) //pet - SetHP(GetHP()+hp_regen+bonus+(GetLevel()/5)); + SetHP(GetHP()+GetNPCHPRegen()+bonus+(GetLevel()/5)); else - SetHP(GetHP()+hp_regen+bonus); + SetHP(GetHP()+GetNPCHPRegen()+bonus); } else - SetHP(GetHP()+hp_regen); + SetHP(GetHP()+GetNPCHPRegen()); if(GetMana() < GetMaxMana()) { SetMana(GetMana()+mana_regen+bonus); diff --git a/zone/npc.h b/zone/npc.h index ae444c099..07c81e79b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -256,6 +256,7 @@ public: virtual void DoClassAttacks(Mob *target); void CheckSignal(); inline bool IsTargetableWithHotkey() const { return no_target_hotkey; } + int32 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; } //waypoint crap int GetMaxWp() const { return max_wp; } diff --git a/zone/raids.cpp b/zone/raids.cpp index bf6f76d5c..c712fc4bd 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -504,7 +504,7 @@ void Raid::HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, int32 range) } -void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster) +void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 limit) { if (!caster) return; @@ -512,7 +512,7 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster) if (!range) range = 200; - int dmgtaken = 0, numMem = 0; + int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; int gi = 0; float distance; @@ -525,7 +525,12 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster) { distance = caster->DistNoRoot(*members[gi].member); if(distance <= range2){ - dmgtaken += (members[gi].member->GetMaxHP() - members[gi].member->GetHP()); + + dmgtaken_tmp = members[gi].member->GetMaxHP() - members[gi].member->GetHP(); + if (limit && (dmgtaken_tmp > limit)) + dmgtaken_tmp = limit; + + dmgtaken += (dmgtaken_tmp); numMem += 1; } } @@ -555,7 +560,7 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster) } } -void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster) +void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 limit) { if (!caster) return; @@ -566,17 +571,24 @@ void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster) float distance; float range2 = range*range; - int manataken = 0, numMem = 0; + int manataken = 0, numMem = 0, manataken_tmp = 0; int gi = 0; for(; gi < MAX_RAID_MEMBERS; gi++) { if(members[gi].member){ if(members[gi].GroupNumber == gid) { - distance = caster->DistNoRoot(*members[gi].member); - if(distance <= range2){ - manataken += (members[gi].member->GetMaxMana() - members[gi].member->GetMana()); - numMem += 1; + if (members[gi].member->GetMaxMana() > 0) { + distance = caster->DistNoRoot(*members[gi].member); + if(distance <= range2){ + + manataken_tmp = members[gi].member->GetMaxMana() - members[gi].member->GetMana(); + if (limit && (manataken_tmp > limit)) + manataken_tmp = limit; + + manataken += (manataken_tmp); + numMem += 1; + } } } } @@ -584,6 +596,7 @@ void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster) manataken += manataken * penalty / 100; manataken /= numMem; + for(gi = 0; gi < MAX_RAID_MEMBERS; gi++) { if(members[gi].member){ diff --git a/zone/raids.h b/zone/raids.h index c8c2f61e4..be12788e6 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -147,8 +147,8 @@ public: void CastGroupSpell(Mob* caster,uint16 spellid, uint32 gid); void SplitExp(uint32 exp, Mob* other); uint32 GetTotalRaidDamage(Mob* other); - void BalanceHP(int32 penalty, uint32 gid, int32 range = 0, Mob* caster = nullptr); - void BalanceMana(int32 penalty, uint32 gid, int32 range = 0, Mob* caster = nullptr); + void BalanceHP(int32 penalty, uint32 gid, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); + void BalanceMana(int32 penalty, uint32 gid, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); void HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, int32 range = 0); void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr); void GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4e10160ed..b846e0058 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2393,7 +2393,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) gid = r->GetGroup(caster->GetName()); if(gid < 11) { - r->BalanceHP(spell.base[i], gid, spell.range, caster); + r->BalanceHP(spell.base[i], gid, spell.range, caster, spell.base2[i]); break; } } @@ -2403,7 +2403,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(!g) break; - g->BalanceHP(spell.base[i], spell.range, caster); + g->BalanceHP(spell.base[i], spell.range, caster, spell.base2[i]); break; } @@ -2421,7 +2421,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) gid = r->GetGroup(caster->GetName()); if(gid < 11) { - r->BalanceMana(spell.base[i], gid, spell.range, caster); + r->BalanceMana(spell.base[i], gid, spell.range, caster, spell.base2[i]); break; } } @@ -2431,7 +2431,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(!g) break; - g->BalanceMana(spell.base[i], spell.range, caster); + g->BalanceMana(spell.base[i], spell.range, caster, spell.base2[i]); break; } @@ -2668,6 +2668,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) SlowMitigation(caster); break; + case SE_AddHatePct: + { + if (IsNPC()) + CastToNPC()->SetHate(caster, (CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100)); + + break; + } + // Handled Elsewhere case SE_ImmuneFleeing: case SE_NegateSpellEffect: @@ -2886,6 +2894,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_LimitCastTimeMax: case SE_TriggerOnReqCaster: case SE_FrenziedDevastation: + case SE_AStacker: + case SE_BStacker: + case SE_CStacker: + case SE_DStacker: + case SE_DoubleRiposte: + case SE_Berserk: + case SE_Vampirism: + case SE_Metabolism: { break; } @@ -3518,6 +3534,14 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste } } + case SE_AddHateOverTimePct: + { + if (IsNPC()) + CastToNPC()->SetHate(caster, (CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100)); + + break; + } + default: { // do we need to do anyting here? diff --git a/zone/spells.cpp b/zone/spells.cpp index 54e98613e..21a2fe749 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2542,7 +2542,7 @@ int CalcBuffDuration_formula(int level, int formula, int duration) // -1 if they can't stack and spellid2 should be stopped //currently, a spell will not land if it would overwrite a better spell on any effect //if all effects are better or the same, we overwrite, else we do nothing -int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1, Mob* caster2) +int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1, Mob* caster2, int buffslot) { const SPDat_Spell_Struct &sp1 = spells[spellid1]; const SPDat_Spell_Struct &sp2 = spells[spellid2]; @@ -2621,6 +2621,24 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, } } + /*Buff stacking prevention spell effects (446 - 449) works as follows... If B prevent A, if C prevent B, if D prevent C. + Special check is added to make sure the buffs stack properly when applied from fade on duration effect, since the buff + is not fully removed at the time of the trgger*/ + if (spellbonuses.BStacker) { + if ((effect2 == SE_AStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_BStacker))) + return -1; + } + + if (spellbonuses.CStacker) { + if ((effect2 == SE_BStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_CStacker))) + return -1; + } + + if (spellbonuses.DStacker) { + if ((effect2 == SE_CStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_DStacker))) + return -1; + } + if(effect2 == SE_StackingCommand_Overwrite) { overwrite_effect = sp2.base[i]; @@ -2901,7 +2919,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid if (curbuf.spellid != SPELL_UNKNOWN) { // there's a buff in this slot ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spell_id, - caster_level, entity_list.GetMobID(curbuf.casterid), caster); + caster_level, entity_list.GetMobID(curbuf.casterid), caster, buffslot); if (ret == -1) { // stop the spell mlog(SPELLS__BUFFS, "Adding buff %d failed: stacking prevented by spell %d in slot %d with caster level %d", spell_id, curbuf.spellid, buffslot, curbuf.casterlevel); @@ -3047,7 +3065,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs // there's a buff in this slot - ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spellid, caster_level); + ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spellid, caster_level, nullptr, nullptr, i); if(ret == 1) { // should overwrite current slot if(iFailIfOverwrite) {