diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 33deb7fe8..258450200 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1883,6 +1883,8 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].override_crit_chance = atoi(row[217]); sp[tempid].aemaxtargets = atoi(row[218]); sp[tempid].no_heal_damage_item_mod = atoi(row[219]); + sp[tempid].spell_class = atoi(row[221]); + sp[tempid].spell_subclass = atoi(row[222]); sp[tempid].persistdeath = atoi(row[224]) != 0; sp[tempid].min_dist = atof(row[227]); sp[tempid].min_dist_mod = atof(row[228]); diff --git a/common/spdat.h b/common/spdat.h index ea8524f82..089d593d5 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -170,6 +170,25 @@ const int Z_AGGRO=10; const uint32 MobAISpellRange=100; // max range of buffs +enum FocusLimitIncludes { + IncludeExistsSELimitResist = 0, + IncludeFoundSELimitResist = 1, + IncludeExistsSELimitSpell = 2, + IncludeFoundSELimitSpell = 3, + IncludeExistsSELimitEffect = 4, + IncludeFoundSELimitEffect = 5, + IncludeExistsSELimitTarget = 6, + IncludeFoundSELimitTarget = 7, + IncludeExistsSELimitSpellGroup = 8, + IncludeFoundSELimitSpellGroup = 9, + IncludeExistsSELimitCastingSkill = 10, + IncludeFoundSELimitCastingSkill = 11, + IncludeExistsSELimitSpellClass = 12, + IncludeFoundSELimitSpellClass = 13, + IncludeExistsSELimitSpellSubclass = 14, + IncludeFoundSELimitSpellSubclass = 15 +}; + enum SpellTypes : uint32 { SpellType_Nuke = (1 << 0), @@ -746,8 +765,8 @@ typedef enum { #define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained #define SE_ManaDrainWithDmg 401 // implemented - Deals damage based on the amount of mana drained #define SE_EndDrainWithDmg 402 // implemented - Deals damage for the amount of endurance drained -#define SE_LimitSpellClass 403 // implemented - Limits to specific types of spells (see CheckSpellCategory) -#define SE_LimitSpellSubclass 404 // *not implemented - Limits to specific types of spells (see CheckSpellCategory) [Categories NOT defined yet] +#define SE_LimitSpellClass 403 // implemented, @Ff, 'Spell Category' using table field 'spell_class' that a spell focus can require or exclude, base1: category type, Include: Positive Exclude: Negative +#define SE_LimitSpellSubclass 404 // implemented, @Ff, 'Spell Category Subclass' using table field 'spell_subclass' that a spell focus can require or exclude, base1: category type, Include: Positive Exclude: Negative #define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block) #define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted #define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied) @@ -860,7 +879,7 @@ typedef enum { //#define SE_Endurance_Max_Percent 514 // #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier #define SE_AC_Mitigation_Max_Percent 516 // implemented - stackable defense modifier -//#define SE_Attack_Offense_Max_Percent 517 // +//#define SE_Attack_Offense_Max_Percent 517 // #define SE_Attack_Accuracy_Max_Percent 518 // implemented - stackable accurary modifer //#define SE_Luck_Amount 519 // //#define SE_Luck_Percent 520 // @@ -1018,8 +1037,8 @@ struct SPDat_Spell_Struct /* 218 */ int aemaxtargets; //Is used for various AE effects -- MAX_TARGETS /* 219 */ int no_heal_damage_item_mod; // -- NO_HEAL_DAMAGE_ITEM_MOD /* 220 */ //int caster_requirement_id; // -- CASTER_REQUIREMENT_ID -/* 221 */ //int spell_class; // -- SPELL_CLASS -/* 222 */ //int spell_subclass; // -- SPELL_SUBCLASS +/* 221 */ int spell_class; // -- SPELL_CLASS +/* 222 */ int spell_subclass; // -- SPELL_SUBCLASS /* 223 */ //int ai_valid_targets; // -- AI_VALID_TARGETS /* 224 */ bool persistdeath; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH /* 225 */ //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE diff --git a/zone/mob.h b/zone/mob.h index 128c99f55..5a548dae3 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -349,7 +349,6 @@ public: bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); - bool CheckSpellCategory(uint16 spell_id, int category_id, int effect_id); void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); void BeamDirectional(uint16 spell_id, int16 resist_adjust); void ConeDirectional(uint16 spell_id, int16 resist_adjust); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d947d6845..4813f3b8f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4500,9 +4500,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[0] = true; + LimitInclude[IncludeExistsSELimitResist] = true; if (spell.resisttype == base1) // Include - LimitInclude[1] = true; + LimitInclude[IncludeFoundSELimitResist] = true; } break; @@ -4552,9 +4552,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[2] = true; + LimitInclude[IncludeExistsSELimitSpell] = true; if (spell_id == base1) // Include - LimitInclude[3] = true; + LimitInclude[IncludeFoundSELimitSpell] = true; } break; @@ -4570,15 +4570,15 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[4] = true; + LimitInclude[IncludeExistsSELimitEffect] = true; // they use 33 here for all classes ... unsure if the type check is really needed if (base1 == SE_SummonPet && type == focusReagentCost) { if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id)) - LimitInclude[5] = true; + LimitInclude[IncludeFoundSELimitEffect] = true; } else { if (IsEffectInSpell(spell_id, base1)) // Include - LimitInclude[5] = true; + LimitInclude[IncludeFoundSELimitEffect] = true; } } break; @@ -4612,9 +4612,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[6] = true; + LimitInclude[IncludeExistsSELimitTarget] = true; if (base1 == spell.targettype) // Include - LimitInclude[7] = true; + LimitInclude[IncludeFoundSELimitTarget] = true; } break; @@ -4644,33 +4644,33 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[10] = true; + LimitInclude[IncludeExistsSELimitCastingSkill] = true; if (base1 == spell.skill) - LimitInclude[11] = true; + LimitInclude[IncludeFoundSELimitCastingSkill] = true; } break; case SE_LimitSpellClass: if (base1 < 0) { // Exclude - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) - return (0); + if (-base1 == spell.spell_class); + LimitFailure = true; } else { - LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include - LimitInclude[13] = true; + LimitInclude[IncludeExistsSELimitSpellClass] = true; + if (base1 == spell.spell_class) // Include + LimitInclude[IncludeFoundSELimitSpellClass] = true; } break; case SE_LimitSpellSubclass: if (base1 < 0) { // Exclude - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) - return (0); + if (-base1 == spell.spell_subclass); + LimitFailure = true; } else { - LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include - LimitInclude[15] = true; + LimitInclude[IncludeExistsSELimitSpellSubclass] = true; + if (base1 == spell.spell_subclass) // Include + LimitInclude[IncludeFoundSELimitSpellSubclass] = true; } break; @@ -4934,17 +4934,19 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 'casterid' is the casterid of the caster of spell_id, used when spell_id is cast on a target with a focus effect that is checked by incoming spell. */ - if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) + if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) { return 0; + } const SPDat_Spell_Struct &focus_spell = spells[focus_id]; - const SPDat_Spell_Struct &spell = spells[spell_id]; + const SPDat_Spell_Struct &spell = spells[spell_id]; - int32 value = 0; - int lvlModifier = 100; - int spell_level = 0; - int lvldiff = 0; - int32 Caston_spell_id = 0; + + int16 value = 0; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + uint32 Caston_spell_id = 0; bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice @@ -4964,541 +4966,650 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo switch (focus_spell.effectid[i]) { - case SE_Blank: - break; - - case SE_LimitResist: - if (focus_spell.base[i] < 0) { - if (spell.resisttype == -focus_spell.base[i]) // Exclude - return 0; - } else { - LimitInclude[0] = true; - if (spell.resisttype == focus_spell.base[i]) // Include - LimitInclude[1] = true; - } - break; - - case SE_LimitInstant: - if (focus_spell.base[i] == 1 && spell.buffduration) // Fail if not instant - return 0; - if (focus_spell.base[i] == 0 && (spell.buffduration == 0)) // Fail if instant - return 0; - - break; - - case SE_LimitMaxLevel: - if (IsNPC()) + case SE_Blank: break; - spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = spell_level - focus_spell.base[i]; - // every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky - // when ItemCastsUseFocus is true - if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || - RuleB(Character, ItemCastsUseFocus) == false)) { - if (focus_spell.base2[i] > 0) { - lvlModifier -= focus_spell.base2[i] * lvldiff; - if (lvlModifier < 1) + + case SE_LimitResist: + if (focus_spell.base[i] < 0) { + if (spell.resisttype == -focus_spell.base[i]) { // Exclude return 0; - } else - return 0; - } - break; - - case SE_LimitMinLevel: - if (IsNPC()) - break; - if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base[i]) - return (0); - break; - - case SE_LimitCastTimeMin: - if (spells[spell_id].cast_time < (uint16)focus_spell.base[i]) - return (0); - break; - - case SE_LimitCastTimeMax: - if (spells[spell_id].cast_time > (uint16)focus_spell.base[i]) - return (0); - break; - - case SE_LimitSpell: - if (focus_spell.base[i] < 0) { // Exclude - if (spell_id == -focus_spell.base[i]) - return (0); - } else { - LimitInclude[2] = true; - if (spell_id == focus_spell.base[i]) // Include - LimitInclude[3] = true; - } - break; - - case SE_LimitMinDur: - if (focus_spell.base[i] > - CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) - return (0); - break; - - case SE_LimitEffect: - if (focus_spell.base[i] < 0) { - if (IsEffectInSpell(spell_id, -focus_spell.base[i])) // Exclude - return 0; - } else { - LimitInclude[4] = true; - if (IsEffectInSpell(spell_id, focus_spell.base[i])) // Include - LimitInclude[5] = true; - } - break; - - case SE_LimitSpellType: - switch (focus_spell.base[i]) { - case 0: - if (!IsDetrimentalSpell(spell_id)) - return 0; - break; - case 1: - if (!IsBeneficialSpell(spell_id)) - return 0; - break; - default: - LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", - focus_spell.base[i]); - } - break; - - case SE_LimitManaMin: - if (spell.mana < focus_spell.base[i]) - return 0; - break; - - case SE_LimitManaMax: - if (spell.mana > focus_spell.base[i]) - return 0; - break; - - case SE_LimitTarget: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.targettype) // Exclude - return 0; - } else { - LimitInclude[6] = true; - if (focus_spell.base[i] == spell.targettype) // Include - LimitInclude[7] = true; - } - break; - - case SE_LimitCombatSkills: - if (focus_spell.base[i] == 0 && - (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) // Exclude Discs / Procs - return 0; - else if (focus_spell.base[i] == 1 && - (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) // Exclude Spells - return 0; - - break; - - case SE_LimitSpellGroup: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.spellgroup) // Exclude - return 0; - } else { - LimitInclude[8] = true; - if (focus_spell.base[i] == spell.spellgroup) // Include - LimitInclude[9] = true; - } - break; - - case SE_LimitCastingSkill: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.skill) - return 0; - } else { - LimitInclude[10] = true; - if (focus_spell.base[i] == spell.skill) - LimitInclude[11] = true; - } - break; - - case SE_LimitClass: - // Do not use this limit more then once per spell. If multiple class, treat value like items - // would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) - return 0; - break; - - case SE_LimitRace: - if (focus_spell.base[i] != GetRace()) - return 0; - break; - - case SE_LimitUseMin: - if (focus_spell.base[i] > spell.numhits) - return 0; - break; - - case SE_LimitUseType: - if (focus_spell.base[i] != spell.numhitstype) - return 0; - break; - - case SE_CastonFocusEffect: - if (focus_spell.base[i] > 0) - Caston_spell_id = focus_spell.base[i]; - break; - - case SE_LimitSpellClass: - if (focus_spell.base[i] < 0) { // Exclude - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) - return (0); - } else { - LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) // Include - LimitInclude[13] = true; - } - break; - - case SE_LimitSpellSubclass: - if (focus_spell.base[i] < 0) { // Exclude - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) - return (0); - } else { - LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) // Include - LimitInclude[15] = true; - } - break; - - case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here - if (focus_spell.base[i] == 0) { - if (casterid == GetID()) - return 0;//Mob casting is same as target, fail if you are casting on yourself. - } - else if (focus_spell.base[i] == 1) { - if (casterid != GetID()) - return 0;//Mob casting is not same as target, fail if you are not casting on yourself. - } - break; - - case SE_Ff_CasterClass: - // Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) - return 0; - break; - - // handle effects - case SE_ImprovedDamage: - if (type == focusImprovedDamage) { - // This is used to determine which focus should be used for the random calculation - if (best_focus) { - // If the spell contains a value in the base2 field then that is the max value - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; } - // If the spell does not contain a base2 value, then its a straight non random - // value - else { - value = focus_spell.base[i]; - } - } - // Actual focus calculation starts here - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_ImprovedDamage2: - if (type == focusImprovedDamage2) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_ImprovedHeal: - if (type == focusImprovedHeal) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } else { - value = focus_spell.base[i]; - } - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_ReduceManaCost: - if (type == focusManaCost) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } else { - value = focus_spell.base[i]; - } - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_IncreaseSpellHaste: - if (type == focusSpellHaste && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_IncreaseSpellDuration: - if (type == focusSpellDuration && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_SpellDurationIncByTic: - if (type == focusSpellDurByTic && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_SwarmPetDuration: - if (type == focusSwarmPetDuration && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_IncreaseRange: - if (type == focusRange && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_ReduceReagentCost: - if (type == focusReagentCost && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_PetPowerIncrease: - if (type == focusPetPower && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_SpellResistReduction: - if (type == focusResistRate) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } else { - value = focus_spell.base[i]; - } - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_SpellHateMod: - if (type == focusSpellHateMod) { - if (value != 0) { - if (value > 0) { - if (focus_spell.base[i] > value) - value = focus_spell.base[i]; - } else { - if (focus_spell.base[i] < value) - value = focus_spell.base[i]; - } - } else - value = focus_spell.base[i]; - } - break; - - case SE_ReduceReuseTimer: - if (type == focusReduceRecastTime) - value = focus_spell.base[i] / 1000; - break; - - case SE_TriggerOnCast: - if (type == focusTriggerOnCast) { - if (zone->random.Roll(focus_spell.base[i])) - value = focus_spell.base2[i]; - else - value = 0; - } - break; - - case SE_BlockNextSpellFocus: - if (type == focusBlockNextSpell) { - if (zone->random.Roll(focus_spell.base[i])) - value = 1; - } - break; - - case SE_SympatheticProc: - if (type == focusSympatheticProc) { - value = focus_id; - } - break; - - case SE_FcSpellVulnerability: - if (type == focusSpellVulnerability) { - if (best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; //max damage - else - value = focus_spell.base[i]; //min damage - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; //If no max damage set, then default to min damage - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value - } - } - break; - - case SE_Fc_Spell_Damage_Pct_IncomingPC: - if (type == focusFcSpellDamagePctIncomingPC) { - if (best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; //max damage - else - value = focus_spell.base[i]; //min damage - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; //If no max damage set, then default to min damage } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + LimitInclude[IncludeExistsSELimitResist] = true; + if (spell.resisttype == focus_spell.base[i]) { // Include + LimitInclude[IncludeFoundSELimitResist] = true; + } } - } - break; + break; - case SE_FcTwincast: - if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmt: - if (type == focusFcDamageAmt) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmt2: - if (type == focusFcDamageAmt2) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmtCrit: - if (type == focusFcDamageAmtCrit) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmtIncoming: - if (type == focusFcDamageAmtIncoming) - value = focus_spell.base[i]; - break; - - case SE_Fc_Spell_Damage_Amt_IncomingPC: - if (type == focusFcSpellDamageAmtIncomingPC) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmtIncoming: - if (type == focusFcHealAmtIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcDamagePctCrit: - if (type == focusFcDamagePctCrit) - value = focus_spell.base[i]; - break; - - case SE_FcHealPctCritIncoming: - if (type == focusFcHealPctCritIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmtCrit: - if (type == focusFcHealAmtCrit) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmt: - if (type == focusFcHealAmt) - value = focus_spell.base[i]; - break; - - case SE_FcHealPctIncoming: - if (type == focusFcHealPctIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcBaseEffects: - if (type == focusFcBaseEffects) - value = focus_spell.base[i]; - break; - - case SE_FcIncreaseNumHits: - if (type == focusIncreaseNumHits) - value = focus_spell.base[i]; - break; - - case SE_FcLimitUse: - if (type == focusFcLimitUse) - value = focus_spell.base[i]; - break; - - case SE_FcMute: - if (type == focusFcMute) - value = focus_spell.base[i]; - break; - - case SE_FcStunTimeMod: - if (type == focusFcStunTimeMod) - value = focus_spell.base[i]; - break; - - case SE_FcTimerRefresh: - if (type == focusFcTimerRefresh) - value = focus_spell.base[i]; - break; - - case SE_Fc_Cast_Spell_On_Land: - if (type == focusFcCastSpellOnLand) { - if (zone->random.Roll(focus_spell.base[i])) { - value = focus_spell.base2[i]; + case SE_LimitInstant: + if (focus_spell.base[i] == 1 && spell.buffduration) { // Fail if not instant + return 0; + } + if (focus_spell.base[i] == 0 && (spell.buffduration == 0)) { // Fail if instant + return 0; + } + + break; + + case SE_LimitMaxLevel: + if (IsNPC()) { + break; + } + spell_level = spell.classes[(GetClass() % 17) - 1]; + lvldiff = spell_level - focus_spell.base[i]; + // every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky + // when ItemCastsUseFocus is true + if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || + RuleB(Character, ItemCastsUseFocus) == false)) { + if (focus_spell.base2[i] > 0) { + lvlModifier -= focus_spell.base2[i] * lvldiff; + if (lvlModifier < 1) { + return 0; + } + } + else { + return 0; + } + } + break; + + case SE_LimitMinLevel: + if (IsNPC()) { + break; + } + if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base[i]) { + return (0); + } + break; + + case SE_LimitCastTimeMin: + if (spells[spell_id].cast_time < (uint16) focus_spell.base[i]) { + return (0); + } + break; + + case SE_LimitCastTimeMax: + if (spells[spell_id].cast_time > (uint16) focus_spell.base[i]) { + return (0); + } + break; + + case SE_LimitSpell: + if (focus_spell.base[i] < 0) { // Exclude + if (spell_id == -focus_spell.base[i]) { + return (0); + } + } + else { + LimitInclude[IncludeExistsSELimitSpell] = true; + if (spell_id == focus_spell.base[i]) { // Include + LimitInclude[IncludeFoundSELimitSpell] = true; + } + } + break; + + case SE_LimitMinDur: + if (focus_spell.base[i] > + CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) { + return (0); + } + break; + + case SE_LimitEffect: + if (focus_spell.base[i] < 0) { + if (IsEffectInSpell(spell_id, -focus_spell.base[i])) { // Exclude + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitEffect] = true; + if (IsEffectInSpell(spell_id, focus_spell.base[i])) { // Include + LimitInclude[IncludeFoundSELimitEffect] = true; + } + } + break; + + case SE_LimitSpellType: + switch (focus_spell.base[i]) { + case 0: + if (!IsDetrimentalSpell(spell_id)) { + return 0; + } + break; + case 1: + if (!IsBeneficialSpell(spell_id)) { + return 0; + } + break; + default: + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", + focus_spell.base[i]); + } + break; + + case SE_LimitManaMin: + if (spell.mana < focus_spell.base[i]) { + return 0; + } + break; + + case SE_LimitManaMax: + if (spell.mana > focus_spell.base[i]) { + return 0; + } + break; + + case SE_LimitTarget: + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.targettype) { // Exclude + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitTarget] = true; + if (focus_spell.base[i] == spell.targettype) { // Include + LimitInclude[IncludeFoundSELimitTarget] = true; + } + } + break; + + case SE_LimitCombatSkills: + if (focus_spell.base[i] == 0 && + (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs + return 0; + } + else if (focus_spell.base[i] == 1 && + (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells + return 0; + } + + break; + + case SE_LimitSpellGroup: + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.spellgroup) { // Exclude + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitSpellGroup] = true; + if (focus_spell.base[i] == spell.spellgroup) { // Include + LimitInclude[IncludeFoundSELimitSpellGroup] = true; + } + } + break; + + case SE_LimitCastingSkill: + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.skill) { + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitCastingSkill] = true; + if (focus_spell.base[i] == spell.skill) { + LimitInclude[IncludeFoundSELimitCastingSkill] = true; + } + } + break; + + case SE_LimitClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items + // would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) { + return 0; + } + break; + + case SE_LimitRace: + if (focus_spell.base[i] != GetRace()) { + return 0; + } + break; + + case SE_LimitUseMin: + if (focus_spell.base[i] > spell.numhits) { + return 0; + } + break; + + case SE_LimitUseType: + if (focus_spell.base[i] != spell.numhitstype) { + return 0; + } + break; + + case SE_CastonFocusEffect: + if (focus_spell.base[i] > 0) { + Caston_spell_id = focus_spell.base[i]; + } + break; + + case SE_LimitSpellClass: + if (focus_spell.base[i] < 0) { // Exclude + if (-focus_spell.base[i] == spell.spell_class) { + return (0); + } + } + else { + LimitInclude[IncludeExistsSELimitSpellClass] = true; + if (focus_spell.base[i] == spell.spell_class) { // Include + LimitInclude[IncludeFoundSELimitSpellClass] = true; + } + } + break; + + case SE_LimitSpellSubclass: + if (focus_spell.base[i] < 0) { // Exclude + if (-focus_spell.base[i] == spell.spell_subclass) { + return (0); + } + } + else { + LimitInclude[IncludeExistsSELimitSpellSubclass] = true; + if (focus_spell.base[i] == spell.spell_subclass) { // Include + LimitInclude[IncludeFoundSELimitSpellSubclass] = true; + } + } + break; + + case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here + if (focus_spell.base[i] == 0) { + if (casterid == GetID()) { + return 0; + }//Mob casting is same as target, fail if you are casting on yourself. + } + else if (focus_spell.base[i] == 1) { + if (casterid != GetID()) { + return 0; + }//Mob casting is not same as target, fail if you are not casting on yourself. + } + break; + + case SE_Ff_CasterClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) { + return 0; + } + break; + + // handle effects + case SE_ImprovedDamage: + if (type == focusImprovedDamage) { + // This is used to determine which focus should be used for the random calculation + if (best_focus) { + // If the spell contains a value in the base2 field then that is the max value + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + // If the spell does not contain a base2 value, then its a straight non random + // value + else { + value = focus_spell.base[i]; + } + } + // Actual focus calculation starts here + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_ImprovedDamage2: + if (type == focusImprovedDamage2) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_ImprovedHeal: + if (type == focusImprovedHeal) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_ReduceManaCost: + if (type == focusManaCost) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_IncreaseRange: + if (type == focusRange && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_ReduceReagentCost: + if (type == focusReagentCost && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_PetPowerIncrease: + if (type == focusPetPower && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_SpellResistReduction: + if (type == focusResistRate) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_SpellHateMod: + if (type == focusSpellHateMod) { + if (value != 0) { + if (value > 0) { + if (focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + } + else { + if (focus_spell.base[i] < value) { + value = focus_spell.base[i]; + } + } + } + else { + value = focus_spell.base[i]; + } + } + break; + + case SE_ReduceReuseTimer: + if (type == focusReduceRecastTime) { + value = focus_spell.base[i] / 1000; + } + break; + + case SE_TriggerOnCast: + if (type == focusTriggerOnCast) { + if (zone->random.Roll(focus_spell.base[i])) { + value = focus_spell.base2[i]; + } + else { + value = 0; + } + } + break; + + case SE_BlockNextSpellFocus: + if (type == focusBlockNextSpell) { + if (zone->random.Roll(focus_spell.base[i])) { + value = 1; + } + } + break; + + case SE_SympatheticProc: + if (type == focusSympatheticProc) { + value = focus_id; + } + break; + + case SE_FcSpellVulnerability: + if (type == focusSpellVulnerability) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; //max damage + } + else { + value = focus_spell.base[i]; + } //min damage + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; //If no max damage set, then default to min damage + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + } + } + break; + + case SE_Fc_Spell_Damage_Pct_IncomingPC: + if (type == focusFcSpellDamagePctIncomingPC) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; //max damage + } + else { + value = focus_spell.base[i]; + } //min damage + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; //If no max damage set, then default to min damage + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + } + } + break; + + case SE_FcTwincast: + if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmt: + if (type == focusFcDamageAmt) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmt2: + if (type == focusFcDamageAmt2) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmtCrit: + if (type == focusFcDamageAmtCrit) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmtIncoming: + if (type == focusFcDamageAmtIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_Fc_Spell_Damage_Amt_IncomingPC: + if (type == focusFcSpellDamageAmtIncomingPC) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealAmtIncoming: + if (type == focusFcHealAmtIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamagePctCrit: + if (type == focusFcDamagePctCrit) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealPctCritIncoming: + if (type == focusFcHealPctCritIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealAmtCrit: + if (type == focusFcHealAmtCrit) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealAmt: + if (type == focusFcHealAmt) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealPctIncoming: + if (type == focusFcHealPctIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_FcBaseEffects: + if (type == focusFcBaseEffects) { + value = focus_spell.base[i]; + } + break; + + case SE_FcIncreaseNumHits: + if (type == focusIncreaseNumHits) { + value = focus_spell.base[i]; + } + break; + + case SE_FcLimitUse: + if (type == focusFcLimitUse) { + value = focus_spell.base[i]; + } + break; + + case SE_FcMute: + if (type == focusFcMute) { + value = focus_spell.base[i]; + } + break; + + case SE_FcStunTimeMod: + if (type == focusFcStunTimeMod) { + value = focus_spell.base[i]; + } + break; + + case SE_FcTimerRefresh: + if (type == focusFcTimerRefresh) { + value = focus_spell.base[i]; + } + break; + + case SE_Fc_Cast_Spell_On_Land: + if (type == focusFcCastSpellOnLand) { + if (zone->random.Roll(focus_spell.base[i])) { + value = focus_spell.base2[i]; + } + break; } - break; - } #if EQDEBUG >= 6 - // this spits up a lot of garbage when calculating spell focuses - // since they have all kinds of extra effects on them. - default: - LogInfo("CalcFocusEffect: unknown effectid [{}]", - focus_spell.effectid[i]); + // this spits up a lot of garbage when calculating spell focuses + // since they have all kinds of extra effects on them. + default: + LogInfo("CalcFocusEffect: unknown effectid [{}]", + focus_spell.effectid[i]); #endif } } for (int e = 0; e < MaxLimitInclude; e += 2) { - if (LimitInclude[e] && !LimitInclude[e + 1]) + if (LimitInclude[e] && !LimitInclude[e + 1]) { return 0; + } } if (Caston_spell_id) { - if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) - SpellFinished(Caston_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[Caston_spell_id].ResistDiff); + if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) { + SpellFinished( + Caston_spell_id, + this, + EQ::spells::CastingSlot::Item, + 0, + -1, + spells[Caston_spell_id].ResistDiff + ); + } } return (value * lvlModifier / 100); @@ -7425,57 +7536,6 @@ void Mob::FocusProcLimitProcess() } } -bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ - - if (!IsValidSpell(spell_id) || !category_id) - return false; - - int effectid = 0; - int category = 0; - - /*Category ID SE_LimitSpellClass [(+) Include (-) Exclude] - 1 = UNK - 2 = Cures - 3 = Offensive Spells - 4 = UNK - 5 = UNK - 6 = Lifetap - */ - - /*Category ID SE_LimitSpellSubClass [(+) Include (-) Exclude] - 5 = UNK - 8 = UNK - */ - - if (effect_id == SE_LimitSpellClass) { - - switch(category_id) - { - case 2: - if (IsCureSpell(spell_id)) - return true; - break; - - case 3: - if (IsDetrimentalSpell(spell_id)) - return true; - break; - - case 6: - if (spells[spell_id].targettype == ST_Tap || spells[spell_id].targettype == ST_TargetAETap) - return true; - break; - } - } - - else if (effect_id == SE_LimitSpellSubclass) { - //Pending Implementation when category types are figured out. - return false; - } - - return false; -} - void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) { if (IsPowerDistModSpell(spell_id)){