diff --git a/common/spdat.cpp b/common/spdat.cpp index 42b2fe26d..525c70c1c 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1230,7 +1230,7 @@ bool IsEffectIgnoredInStacking(int spa) case SE_LimitClass: case SE_LimitRace: case SE_FcBaseEffects: - case 415: + case SE_FFItemClass: case SE_SkillDamageAmount2: case SE_FcLimitUse: case SE_FcIncreaseNumHits: @@ -1298,6 +1298,7 @@ bool IsFocusLimit(int spa) case SE_Ff_Value_Min: case SE_Ff_Value_Max: case SE_Ff_FocusTimerMin: + case SE_FFItemClass: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index fd9413444..2e6d59d5e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -177,7 +177,7 @@ #define EFFECT_COUNT 12 #define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2) #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. -#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects +#define MaxLimitInclude 18 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects #define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks] #define MAX_AA_PROCS 16 //(Actual Proc Amount is MAX_AA_PROCS/4) Number of spells to check AA procs from. (This is arbitrary) #define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) @@ -206,7 +206,9 @@ enum FocusLimitIncludes { IncludeExistsSELimitSpellClass = 12, IncludeFoundSELimitSpellClass = 13, IncludeExistsSELimitSpellSubclass = 14, - IncludeFoundSELimitSpellSubclass = 15 + IncludeFoundSELimitSpellSubclass = 15, + IncludeExistsSEFFItemClass = 16, + IncludeFoundSEFFItemClass = 17 }; /* The id's correspond to 'type' 39 in live(2021) dbstr_us gives the message for target and caster restricted effects. These are not present in the ROF2 dbstr_us. @@ -1115,7 +1117,7 @@ typedef enum { #define SE_LimitRace 412 // implemented, @Ff, Race that can use the spell focus, base1: race, Note: not used in any known live spells. Use only single race at a time. #define SE_FcBaseEffects 413 // implemented, @Fc, On Caster, base spell effectiveness mod pct, base: pct #define SE_LimitCastingSkill 414 // implemented, @Ff, Spell and singing skills(s) that a spell focus can require or exclude, base1: skill id, Include: Positive Exclude: Negative -//#define SE_FFItemClass 415 // not used - base1 matches ItemType, base2 matches SubType, -1 ignored, max is bitmask of valid slots +#define SE_FFItemClass 415 // implemented, @Ff, Limits focuses to be applied only from item click. base1: item ItemType (-1 to include for all ItemTypes,-1000 to exclude clicks from getting the focus, or exclude specific SubTypes or Slots if set), limit: item SubType (-1 for all SubTypes), max: item Slots (bitmask of valid slots, -1 ALL slots), Note: not used on live. See comments in Mob::CalcFocusEffect for more details. #define SE_ACv2 416 // implemented - New AC spell effect #define SE_ManaRegen_v2 417 // implemented - New mana regen effect #define SE_SkillDamageAmount2 418 // implemented - adds skill damage directly to certain attacks diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 163aa18c6..19320af8f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3266,6 +3266,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Buy_AA_Rank: case SE_Ff_FocusTimerMin: case SE_Proc_Timer_Modifier: + case SE_FFItemClass: { break; } @@ -4548,6 +4549,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) 10/11 SE_LimitCastingSkill: 12/13 SE_LimitSpellClass: 14/15 SE_LimitSpellSubClass: + 16/17 SE_FFItemCLass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ @@ -4891,6 +4893,59 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } break; + case SE_FFItemClass: + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + if (IsClient() && casting_spell_slot == EQ::spells::CastingSlot::Item && casting_spell_inventory_slot != 0xFFFFFFFF) { + auto item = CastToClient()->GetInv().GetItem(casting_spell_inventory_slot); + if (item && item->GetItem()) { + //If ItemType set to < -1, then we will exclude either all Subtypes (-1000), or specific items by ItemType, SubType or Slot. See above for rules. + if (base_value < -1) { //Excludes + bool exclude_this_item = true; + int tmp_itemtype = (item->GetItem()->ItemType + 100) * -1; + //ItemType (if set to -1000, ignore and exclude any ItemType) + if (base_value < -1 && base_value != -1000) { + if (base_value != tmp_itemtype) { + exclude_this_item = false; + } + } + //SubType (if set to -1, ignore and exclude all SubTypes) + if (limit_value >= 0) { + if (limit_value != item->GetItem()->SubType) { + exclude_this_item = false; + } + } + if (exclude_this_item) { + LimitFailure = true; + } + } + else {//Includes + LimitInclude[IncludeExistsSEFFItemClass] = true; + bool include_this_item = true; + //ItemType (if set to -1, ignore and include any ItemType) + if (base_value >= 0) { + if (base_value != item->GetItem()->ItemType) { + include_this_item = false; + } + } + //SubType (if set to -1, ignore and include any SubType) + if (limit_value >= 0) { + if (limit_value != item->GetItem()->SubType) { + include_this_item = false; + } + } + if (include_this_item) { + LimitInclude[IncludeFoundSEFFItemClass] = true; + } + } + } + } + } + //If this is checking that focus can only be cast from an item, then if its not cast from item fail. + else if (base_value >= -1) { + LimitFailure = true; + } + //If we are checking to exclude items from a focus then do not fail unless the above check fails. + break; /* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect. case SE_Ff_Same_Caster: @@ -5239,13 +5294,14 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 10/11 SE_LimitCastingSkill: 12/13 SE_LimitSpellClass: 14/15 SE_LimitSpellSubClass: + 16/17 SE_FFItemCLass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ for (int i = 0; i < EFFECT_COUNT; i++) { switch (focus_spell.effect_id[i]) { - + case SE_Blank: break; @@ -5567,6 +5623,96 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_FFItemClass: + + /* + Limits focuses to check if cast from item clicks. Can be used to INCLUDE or EXCLUDE items by ItemType and/or SubType and/or Slots + Not used on live, going on information we have plus implemented as broadly as possible to allow all possible options. + base = item table field 'ItemType' Limit = item table field 'SubType' Max = item table field 'Slots' (this is slot bitmask) + + When including: Setting base, limit, max respectively to -1 will cause it to ignore that check, letting any type or slot ect be used. + + Special rules for excluding. base value needs to be negative < -1, if excluding all ItemTypes set to -1000. + For SubType and Slots set using same rules above as for includes. Ie. -1 for all, positive for specifics + To exclude a specific ItemType we have to do some math. The exclude value will be the negative value of (ItemType + 100). + If ItemType = 10, then SET ItemType= -110 to exclude. If its ItemType 0, then SET ItemType= -100 to exclude ect. Not ideal but it works. + + Usage example: [INCLUDE] Only focus spell if from click cast and is a 'defense armor' item type=10 [base= 10, limit= -1, max= -1] + Usage example: [INCLUDE] Only focus spell if from click cast and is from helmet slot' slots= 4 [base= -1, limit= -1, max= 4] + Usage example: [EXCLUDE] Do not focus spell if it is from an item click. [base= -1000, limit= -1, max= -1] + Usage example: [EXCLUDE] Do not focus spell if it is from an item click from a helmet slot. [base= -1000, limit= -1, max= 4] + Usage example: [EXCLUDE] Do not focus spell if it is from an item click and is a 'defense armor' item type=10. [base= -110, limit= -1, max= -1] + + Note: You can apply multiple includes or excludes to a single focus spell, using multiple SPA 415 limits in the spell. Ie. Check for clicks from ItemType 10 or 11. + + */ + + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + if (IsClient() && casting_spell_slot == EQ::spells::CastingSlot::Item && casting_spell_inventory_slot != 0xFFFFFFFF) { + auto item = CastToClient()->GetInv().GetItem(casting_spell_inventory_slot); + if (item && item->GetItem()) { + //If ItemType set to < -1, then we will exclude either all Subtypes (-1000), or specific items by ItemType, SubType or Slot. See above for rules. + if (focus_spell.base_value[i] < -1) { //Excludes + bool exclude_this_item = true; + int tmp_itemtype = (item->GetItem()->ItemType + 100) * -1; + //ItemType (if set to -1000, ignore and exclude any ItemType) + if (focus_spell.base_value[i] < -1 && focus_spell.base_value[i] != -1000) { + if (focus_spell.base_value[i] != tmp_itemtype) { + exclude_this_item = false; + } + } + //SubType (if set to -1, ignore and exclude all SubTypes) + if (focus_spell.limit_value[i] >= 0) { + if (focus_spell.limit_value[i] != item->GetItem()->SubType) { + exclude_this_item = false; + } + } + //item slot bitmask (if set to -1, ignore and exclude all SubTypes) + if (focus_spell.max_value[i] >= 0) { + if (focus_spell.max_value[i] != item->GetItem()->Slots) { + exclude_this_item = false; + } + } + if (exclude_this_item) { + return 0; + } + } + else {//Includes + LimitInclude[IncludeExistsSEFFItemClass] = true; + bool include_this_item = true; + //ItemType (if set to -1, ignore and include any ItemType) + if (focus_spell.base_value[i] >= 0) { + if (focus_spell.base_value[i] != item->GetItem()->ItemType) { + include_this_item = false; + } + } + //SubType (if set to -1, ignore and include any SubType) + if (focus_spell.limit_value[i] >= 0) { + if (focus_spell.limit_value[i] != item->GetItem()->SubType) { + include_this_item = false; + } + } + //item slot bitmask (if set to -1, ignore and include any slot) + if (focus_spell.max_value[i] >= 0) { + if (focus_spell.max_value[i] != item->GetItem()->Slots) { + include_this_item = false; + } + } + + if (include_this_item) { + LimitInclude[IncludeFoundSEFFItemClass] = true; + } + } + } + } + } + //If this is checking that focus can only be cast from an item, then if its not cast from item fail. + else if (focus_spell.base_value[i] >= -1) { + return 0; + } + //If we are checking to exclude items from a focus then do not fail unless the above check fails. + break; + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) {