//healing and buffing aggro int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible) { int32 AggroAmount = 0; auto target_level = target ? target->GetLevel() : 1; bool ignore_default_buff = false; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default for (int o = 0; o < EFFECT_COUNT; o++) { switch (spells[spell_id].effectid[o]) { case SE_CurrentHP: { if (heal_possible == 0) { AggroAmount += 1; break; } // hate based on base healing power of the spell int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id); if (val > 0) { if (heal_possible < val) val = heal_possible; // capped to amount healed val = 2 * val / 3; // 3:2 ratio if (target_level > 50 && val > 1500) val = 1500; // target 51+ seems ~1500 else if (target_level <= 50 && val > 800) val = 800; // per live patch notes, capped to 800 } AggroAmount += std::max(val, 1); break; } case SE_Rune: AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id) * 2; ignore_default_buff = true; break; case SE_HealOverTime: AggroAmount += 10; ignore_default_buff = true; break; default: break; } } if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; if (!ignore_default_buff && IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) AggroAmount = IsBardSong(spell_id) ? 2 : 9; if (AggroAmount > 0) { int HateMod = RuleI(Aggro, SpellAggroMod); HateMod += GetFocusEffect(focusSpellHateMod, spell_id); //Live AA - Spell casting subtlety HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; AggroAmount = (AggroAmount * HateMod) / 100; } return std::max(0, AggroAmount); }
//healing and buffing aggro int32 Mob::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) { uint16 spell_id = spellid; int32 AggroAmount = 0; for (int o = 0; o < EFFECT_COUNT; o++) { switch(spells[spell_id].effectid[o]) { case SE_CurrentHP: { AggroAmount += spells[spell_id].mana; break; } case SE_Rune: { AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[0], spells[spell_id].base[0], spells[spell_id].max[o], this->GetLevel(), spellid) * 2; break; } case SE_HealOverTime:{ AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); break; } default:{ break; } } } if (IsBardSong(spell_id)) AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100; if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; if(AggroAmount > 0) { int HateMod = RuleI(Aggro, SpellAggroMod); if(IsClient()) { HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id); } //Live AA - Spell casting subtlety HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; AggroAmount = (AggroAmount * HateMod) / 100; //made up number probably scales a bit differently on live but it seems like it will be close enough //every time you cast on live you get a certain amount of "this is a spell" aggro //confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected //by hate modifiers either. //AggroAmount += (slevel*slevel/72); // Moved Below } if(AggroAmount < 0) return 0; else return AggroAmount; }
//o-------------------------------------------------------------- //| CalcSpellEffectValue; Yeahlight, Nov 16, 2008 //o-------------------------------------------------------------- //| Adapted from EQEMU 7.0 //o-------------------------------------------------------------- int Mob::CalcSpellEffectValue(Spell* spell, int effect_id, int caster_level, Mob *caster, int ticsremaining) { int formula, base, max, effect_value; int16 spell_id = spell->GetSpellID(); if(!spell->IsValidSpell() || effect_id < 0 || effect_id >= EFFECT_COUNT) return 0; formula = spell->GetSpellFormula(effect_id); base = spell->GetSpellBase(effect_id); max = spell->GetSpellMax(effect_id); if(spell->IsBlankSpellEffect(effect_id)) return 0; effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell, ticsremaining); if(caster && caster->IsClient() && spell->IsBardSong() && (spell->Get().effectid[effect_id] != SE_AttackSpeed) && (spell->Get().effectid[effect_id] != SE_AttackSpeed2) && (spell->Get().effectid[effect_id] != SE_AttackSpeed3)) { int oval = effect_value; int mod = caster->CastToClient()->GetInstrumentModAppliedToSong(spell); effect_value = effect_value * mod / 10; //mlog(SPELLS__BARDS, "Effect value %d altered with bard modifier of %d to yeild %d", oval, mod, effect_value); } return(effect_value); }
//offensive spell aggro int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) { if (NoDetrimentalSpellAggro(spell_id)) return 0; int32 AggroAmount = 0; int32 nonModifiedAggro = 0; uint16 slevel = GetLevel(); bool dispel = false; bool on_hatelist = target ? target->CheckAggro(this) : false; int proc_cap = RuleI(Aggro, MaxScalingProcAggro); int hate_cap = isproc && proc_cap != -1 ? proc_cap : 1200; int32 target_hp = target ? target->GetMaxHP() : 18000; // default to max int32 default_aggro = 25; if (target_hp >= 18000) // max default_aggro = hate_cap; else if (target_hp >= 390) // min, 390 is the first number with int division that is 26 default_aggro = target_hp / 15; for (int o = 0; o < EFFECT_COUNT; o++) { switch (spells[spell_id].effectid[o]) { case SE_CurrentHPOnce: case SE_CurrentHP: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if(val < 0) AggroAmount -= val; break; } case SE_MovementSpeed: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount += default_aggro; break; } case SE_AttackSpeed: case SE_AttackSpeed2: case SE_AttackSpeed3: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 100) AggroAmount += default_aggro; break; } case SE_Stun: case SE_Blind: case SE_Mez: case SE_Charm: case SE_Fear: AggroAmount += default_aggro; break; case SE_Root: AggroAmount += 10; break; case SE_ACv2: case SE_ArmorClass: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount += default_aggro; break; } case SE_ATK: case SE_ResistMagic: case SE_ResistFire: case SE_ResistCold: case SE_ResistPoison: case SE_ResistDisease: case SE_STR: case SE_STA: case SE_DEX: case SE_AGI: case SE_INT: case SE_WIS: case SE_CHA: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount += 10; break; } case SE_ResistAll: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount += 50; break; } case SE_AllStats: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount += 70; break; } case SE_BardAEDot: AggroAmount += 10; break; case SE_SpinTarget: case SE_Amnesia: case SE_Silence: case SE_Destroy: AggroAmount += default_aggro; break; // unsure -- leave them this for now case SE_Harmony: case SE_CastingLevel: case SE_MeleeMitigation: case SE_CriticalHitChance: case SE_AvoidMeleeChance: case SE_RiposteChance: case SE_DodgeChance: case SE_ParryChance: case SE_DualWieldChance: case SE_DoubleAttackChance: case SE_MeleeSkillCheck: case SE_HitChance: case SE_DamageModifier: case SE_MinDamageModifier: case SE_IncreaseBlockChance: case SE_Accuracy: case SE_DamageShield: case SE_SpellDamageShield: case SE_ReverseDS: { AggroAmount += slevel * 2; break; } // unsure -- leave them this for now case SE_CurrentMana: case SE_ManaRegen_v2: case SE_ManaPool: case SE_CurrentEndurance: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 2; break; } case SE_CancelMagic: case SE_DispelDetrimental: dispel = true; break; case SE_ReduceHate: case SE_InstantHate: nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); break; } } if (IsBardSong(spell_id) && AggroAmount > 40) AggroAmount = 40; // bard songs seem to cap to 40 for most of their spells? if (dispel && target && target->GetHateAmount(this) < 100) AggroAmount += 50; if (spells[spell_id].HateAdded > 0) // overrides the hate (ex. tash) AggroAmount = spells[spell_id].HateAdded; if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; // hate focus ignored on first action for some reason if (!on_hatelist && AggroAmount > 0) { int HateMod = RuleI(Aggro, SpellAggroMod); HateMod += GetFocusEffect(focusSpellHateMod, spell_id); AggroAmount = (AggroAmount * HateMod) / 100; } // initial aggro gets a bonus 100 besides for dispel or hate override // We add this 100 in AddToHateList so we need to account for the oddities here if (dispel && spells[spell_id].HateAdded > 0 && !on_hatelist) AggroAmount -= 100; return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro; }
//offensive spell aggro int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) { int32 AggroAmount = 0; int32 nonModifiedAggro = 0; uint16 slevel = GetLevel(); for (int o = 0; o < EFFECT_COUNT; o++) { switch (spells[spell_id].effectid[o]) { case SE_CurrentHPOnce: case SE_CurrentHP: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if(val < 0) AggroAmount -= val; break; } case SE_MovementSpeed: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount += (2 + ((slevel * slevel) / 8)); break; } case SE_AttackSpeed: case SE_AttackSpeed2: case SE_AttackSpeed3: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 100) AggroAmount += (5 + ((slevel * slevel) / 5)); break; } case SE_Stun: { int val = (5 + ((slevel * slevel) / 6)); if (isproc && RuleI(Aggro,MaxStunProcAggro) > -1 && (val > RuleI(Aggro,MaxStunProcAggro))) val = RuleI(Aggro,MaxStunProcAggro); AggroAmount += val; break; } case SE_Blind: { AggroAmount += (5 + ((slevel * slevel) / 6)); break; } case SE_Mez: { AggroAmount += (5 + ((slevel * slevel) / 5)); break; } case SE_Charm: { AggroAmount += (5 + ((slevel * slevel) / 5)); break; } case SE_Root: { AggroAmount += (2 + ((slevel * slevel) / 8)); break; } case SE_Fear: { AggroAmount += (5 + ((slevel * slevel) / 6)); break; } case SE_ATK: case SE_ACv2: case SE_ArmorClass: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 2; break; } case SE_ResistMagic: case SE_ResistFire: case SE_ResistCold: case SE_ResistPoison: case SE_ResistDisease: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 3; break; } case SE_ResistAll: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 6; break; } case SE_STR: case SE_STA: case SE_DEX: case SE_AGI: case SE_INT: case SE_WIS: case SE_CHA: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 2; break; } case SE_AllStats: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 6; break; } case SE_BardAEDot: { AggroAmount += slevel * 2; break; } case SE_SpinTarget: { AggroAmount += (5 + ((slevel * slevel) / 5)); break; } case SE_Amnesia: case SE_Silence: { AggroAmount += slevel * 2; break; } case SE_Destroy: { AggroAmount += slevel * 2; break; } case SE_Harmony: case SE_CastingLevel: case SE_MeleeMitigation: case SE_CriticalHitChance: case SE_AvoidMeleeChance: case SE_RiposteChance: case SE_DodgeChance: case SE_ParryChance: case SE_DualWieldChance: case SE_DoubleAttackChance: case SE_MeleeSkillCheck: case SE_HitChance: case SE_IncreaseArchery: case SE_DamageModifier: case SE_MinDamageModifier: case SE_IncreaseBlockChance: case SE_Accuracy: case SE_DamageShield: case SE_SpellDamageShield: case SE_ReverseDS: { AggroAmount += slevel * 2; break; } case SE_CurrentMana: case SE_ManaRegen_v2: case SE_ManaPool: case SE_CurrentEndurance: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 2; break; } case SE_CancelMagic: case SE_DispelDetrimental: { AggroAmount += slevel; break; } case SE_ReduceHate: case SE_InstantHate: { nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); break; } } } if (IsAEDurationSpell(spell_id)) AggroAmount /= 2; if (spells[spell_id].HateAdded > 0) AggroAmount = spells[spell_id].HateAdded; if (IsBardSong(spell_id)) AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100; if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; if (AggroAmount > 0) { int HateMod = RuleI(Aggro, SpellAggroMod); if (IsClient()) HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id); AggroAmount = (AggroAmount * HateMod) / 100; //made up number probably scales a bit differently on live but it seems like it will be close enough //every time you cast on live you get a certain amount of "this is a spell" aggro //confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected //by hate modifiers either. //AggroAmount += (slevel*slevel/72); // Saved so I can reimplement it; // this should only be on the spell to aggro the npc not every spell } return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro; }