From d2706701458dd58c928949886d4fe32f28cd27bd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 14:47:17 -0400 Subject: [PATCH] [Spells] Update for SPA 403 and 404 (#1482) * Update for SPA403 and 404 Update SPA SE_LimitSpellClass: 403 SPA SE_LimitSpellSubclass: 404 Now use spell table values from column 221 and 222 respectively. Unknown what the values mean in these fields, but at least live spells work properly. Added FocusLImitInclude Enum to improved focus effect function readability. * Formatting Co-authored-by: Akkadius --- common/shareddb.cpp | 2 + common/spdat.h | 29 +- zone/mob.h | 1 - zone/spell_effects.cpp | 1250 +++++++++++++++++++++------------------- 4 files changed, 681 insertions(+), 601 deletions(-) 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)){