diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2fbbcb58c..f79515198 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4539,13 +4539,21 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) int spell_level = 0; int lvldiff = 0; uint32 effect = 0; - int32 base_value = 0; - int32 limit_value = 0; + int32 base_value = 0; + int32 limit_value = 0; uint32 slot = 0; int index_id = -1; uint32 focus_reuse_time = 0; + bool is_from_item_click = false; + bool try_apply_to_item_click = false; + bool has_item_limit_check = false; + + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + is_from_item_click = true; + } + bool LimitFailure = false; bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice @@ -4903,6 +4911,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) break; case SE_FFItemClass: + has_item_limit_check = true; 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); @@ -4995,18 +5004,21 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_IncreaseSpellHaste: if (type == focusSpellHaste && base_value > value) { value = base_value; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeMod2: if (type == focusFcCastTimeMod2 && base_value > value) { value = base_value; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeAmt: if (type == focusFcCastTimeAmt && base_value > value) { value = base_value; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -5081,6 +5093,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_ReduceReuseTimer: if (type == focusReduceRecastTime) { value = base_value / 1000; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -5247,6 +5260,10 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } } + if (try_apply_to_item_click && !has_item_limit_check) { + return 0; + } + if (LimitFailure) { return 0; } @@ -5290,6 +5307,14 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo uint32 Caston_spell_id = 0; int index_id = -1; uint32 focus_reuse_time = 0; //If this is set and all limits pass, start timer at end of script. + + bool is_from_item_click = false; + bool try_apply_to_item_click = false; + bool has_item_limit_check = false; + + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + is_from_item_click = true; + } bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive @@ -5638,14 +5663,14 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 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. + + 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] @@ -5653,9 +5678,9 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 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. - + */ - + has_item_limit_check = true; 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); @@ -5756,18 +5781,21 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_IncreaseSpellHaste: if (type == focusSpellHaste && focus_spell.base_value[i] > value) { value = focus_spell.base_value[i]; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeMod2: if (type == focusFcCastTimeMod2 && focus_spell.base_value[i] > value) { value = focus_spell.base_value[i]; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeAmt: if (type == focusFcCastTimeAmt && focus_spell.base_value[i] > value) { value = focus_spell.base_value[i]; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -5828,6 +5856,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_ReduceReuseTimer: if (type == focusReduceRecastTime) { value = focus_spell.base_value[i] / 1000; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -6011,6 +6040,15 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; } } + + /* + For item click cast/recast focus modifiers. Only use if SPA 415 exists. + This is an item click but does not have SPA 415 limiter. Fail here. + */ + + if (try_apply_to_item_click && !has_item_limit_check) { + return 0; + } if (Caston_spell_id) { if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 4d6446fc5..386963949 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -417,14 +417,21 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, target_id = GetID(); } - if(cast_time <= -1) { + if (cast_time <= -1) { // save the non-reduced cast time to use in the packet cast_time = orgcasttime = spell.cast_time; // if there's a cast time, check if they have a modifier for it - if(cast_time) { + if (cast_time) { cast_time = GetActSpellCasttime(spell_id, cast_time); } } + //must use SPA 415 with focus (SPA 127/500/501) to reduce item recast + else if (cast_time && IsClient() && slot == CastingSlot::Item && item_slot != 0xFFFFFFFF) { + orgcasttime = cast_time; + if (cast_time) { + cast_time = GetActSpellCasttime(spell_id, cast_time); + } + } else orgcasttime = cast_time; @@ -2543,6 +2550,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui recast -= GetAA(aaTouchoftheWicked) * 420; } int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);//Client only + if(reduction) recast -= reduction; @@ -2556,20 +2564,32 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot); if(itm && itm->GetItem()->RecastDelay > 0){ auto recast_type = itm->GetItem()->RecastType; - CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), itm->GetItem()->RecastDelay); - if (recast_type != -1) { - database.UpdateItemRecastTimestamps( - CastToClient()->CharacterID(), - recast_type, - CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() - ); + int recast_delay = itm->GetItem()->RecastDelay; + //must use SPA 415 with focus (SPA 310) to reduce item recast + int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); + if (reduction) { + recast_delay -= reduction; + } + recast_delay = std::max(recast_delay, 0); + + if (recast_delay > 0) { + + CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), recast_delay); + if (recast_type != -1) { + database.UpdateItemRecastTimestamps( + CastToClient()->CharacterID(), + recast_type, + CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() + ); + } + + auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = static_cast(recast_delay); + ird->recast_type = recast_type; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); } - auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); - ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; - ird->recast_delay = itm->GetItem()->RecastDelay; - ird->recast_type = recast_type; - CastToClient()->QueuePacket(outapp); - safe_delete(outapp); } }