From fb66afd5652101a63bd88dcadd6190d294b313d7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 26 Oct 2021 21:36:10 -0400 Subject: [PATCH] [Spells] Implemented SPA 511 SE_Ff_FocusTimerMin (#1645) * update for SPA 511 * remove debugs, AA implemented * update * format update * rename function renamed function only check for buffs value > 0, don't need to check for AA's which are negative ID's * var rename update var name to better represent its function. --- common/spdat.cpp | 2 + common/spdat.h | 6 +- zone/client_process.cpp | 3 - zone/common.h | 2 - zone/mob.cpp | 6 +- zone/mob.h | 10 ++- zone/mob_ai.cpp | 3 - zone/spell_effects.cpp | 180 ++++++++++++++++++---------------------- zone/spells.cpp | 2 - zone/zonedb.cpp | 2 - 10 files changed, 99 insertions(+), 117 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 432b903b5..719af4069 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1255,6 +1255,7 @@ bool IsEffectIgnoredInStacking(int spa) case SE_Ff_ReuseTimeMax: case SE_Ff_Value_Min: case SE_Ff_Value_Max: + case SE_Ff_FocusTimerMin: return true; default: return false; @@ -1296,6 +1297,7 @@ bool IsFocusLimit(int spa) case SE_Ff_ReuseTimeMax: case SE_Ff_Value_Min: case SE_Ff_Value_Max: + case SE_Ff_FocusTimerMin: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index 3f3349186..b4535cf1d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -180,7 +180,7 @@ #define MaxLimitInclude 16 //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_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) - +#define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of proc limiting timers that can be going at same time (This is arbitrary) const int Z_AGGRO=10; @@ -1208,8 +1208,8 @@ typedef enum { #define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor #define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt -//#define SE_Ff_FocusTimerMin 511 // -#define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500). +#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds +#define SE_Proc_Timer_Modifier 512 // not implemented - limits procs per amount of a time based on timer value (ie limit to 1 proc every 55 seconds) //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier diff --git a/zone/client_process.cpp b/zone/client_process.cpp index b7aad858b..ea11764bf 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -509,9 +509,6 @@ bool Client::Process() { } } - if (focus_proc_limit_timer.Check() && !dead) - FocusProcLimitProcess(); - if (client_state == CLIENT_KICKED) { Save(); OnDisconnect(true); diff --git a/zone/common.h b/zone/common.h index 8678aec64..82b17470e 100644 --- a/zone/common.h +++ b/zone/common.h @@ -328,8 +328,6 @@ struct Buffs_Struct { int32 ExtraDIChance; int16 RootBreakChance; //Not saved to dbase uint32 instrument_mod; - int16 focusproclimit_time; //timer to limit number of procs from focus effects - int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set int32 virus_spread_time; //time till next attempted viral spread bool persistant_buff; bool client; //True if the caster is a client diff --git a/zone/mob.cpp b/zone/mob.cpp index a138668eb..a60a9f563 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -103,7 +103,6 @@ Mob::Mob( ranged_timer(2000), tic_timer(6000), mana_timer(2000), - focus_proc_limit_timer(250), spellend_timer(0), rewind_timer(30000), bindwound_timer(10000), @@ -350,6 +349,11 @@ Mob::Mob( ProjectileAtk[i].speed_mod = 0.0f; } + for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { + focusproclimit_spellid[i] = 0; + focusproclimit_timer[i].Disable(); + } + memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); diff --git a/zone/mob.h b/zone/mob.h index fefeafff9..72ccb8447 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -857,8 +857,9 @@ public: inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } void CastSpellOnLand(Mob* caster, int32 spell_id); - void FocusProcLimitProcess(); - bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1); + + bool IsFocusProcLimitTimerActive(int32 focus_spell_id); + void SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time); void VirusEffectProcess(); void SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining); @@ -1463,7 +1464,9 @@ protected: int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; - Timer focus_proc_limit_timer; + + Timer focusproclimit_timer[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 + int32 focusproclimit_spellid[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 Timer shield_timer; uint32 m_shield_target_id; @@ -1560,7 +1563,6 @@ protected: Timer bardsong_timer; Timer gravity_timer; Timer viral_timer; - uint8 viral_timer_counter; // MobAI stuff eStandingPetOrder pStandingPetOrder; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index af74f3510..8e7b2bcd5 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1125,9 +1125,6 @@ void Mob::AI_Process() { ProjectileAttack(); - if (focus_proc_limit_timer.Check()) - FocusProcLimitProcess(); - if (shield_timer.Check()) { ShieldAbilityFinish(); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cefcd2123..23cc70991 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2992,11 +2992,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - case SE_Proc_Timer_Modifier:{ - buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i]; //Set max amount of procs before lockout timer - break; - } - case SE_PetShield: { if (IsPet()) { Mob* petowner = GetOwner(); @@ -3300,6 +3295,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Skill_Base_Damage_Mod: case SE_Worn_Endurance_Regen_Cap: case SE_Buy_AA_Rank: + case SE_Ff_FocusTimerMin: { break; } @@ -4568,8 +4564,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) int32 base1 = 0; int32 base2 = 0; uint32 slot = 0; - + int index_id = -1; + uint32 focus_reuse_time = 0; bool LimitFailure = false; bool LimitInclude[MaxLimitInclude] = {false}; @@ -4917,6 +4914,15 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } break; + case SE_Ff_FocusTimerMin: + if (IsFocusProcLimitTimerActive(-rank.id)) { + LimitFailure = true; + } + else { + focus_reuse_time = base2; + } + 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: @@ -5217,6 +5223,10 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) return 0; } + if (focus_reuse_time) { + SetFocusProcLimitTimer(-rank.id, focus_reuse_time); + } + return (value * lvlModifier / 100); } @@ -5247,6 +5257,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo int lvldiff = 0; 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 LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive @@ -5579,7 +5590,16 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; - // handle effects + case SE_Ff_FocusTimerMin: + if (IsFocusProcLimitTimerActive(focus_spell.id)) { + return 0; + } + else { + focus_reuse_time = focus_spell.base2[i]; + } + break; + + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); @@ -5882,6 +5902,10 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } } + if (focus_reuse_time) { + SetFocusProcLimitTimer(focus_spell.id, focus_reuse_time); + } + return (value * lvlModifier / 100); } @@ -8293,102 +8317,22 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != spell_id)) { - //Step 3: Check if SE_Proc_Time_Modifier is present and if so apply it. - if (ApplyFocusProcLimiter(buffs[i].spellid, i)) { - //Step 4: Cast spells - if (IsBeneficialSpell(trigger_spell_id)) { - SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); - } - else { - Mob* current_target = GetTarget(); - //For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior. - if (current_target && current_target->GetID() != GetID()) - SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); - } + //Step 3: Cast spells + if (IsBeneficialSpell(trigger_spell_id)) { + SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); } - - if (i >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, i); - } - } - } - } -} - -bool Mob::ApplyFocusProcLimiter(int32 spell_id, int buffslot) -{ - if (buffslot < 0) - return false; - - //Do not allow spell cast if timer is active. - if (buffs[buffslot].focusproclimit_time > 0) - return false; - - /* - SE_Proc_Timer_Modifier - base1= amount of total procs allowed until lock out timer is triggered, should be set to at least 1 in any spell for the effect to function. - base2= lock out timer, which prevents any more procs set in ms 1500 = 1.5 seconds - This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie. - */ - - if (IsValidSpell(spell_id)) { - - for (int i = 0; i < EFFECT_COUNT; i++) { - - //Step 1: Find which slot the spell effect is in. - if (spells[spell_id].effectid[i] == SE_Proc_Timer_Modifier) { - - //Step 2: Check if you still have procs left to trigger, and if so reduce available procs - if (buffs[buffslot].focusproclimit_procamt > 0) { - --buffs[buffslot].focusproclimit_procamt; //Reduce total amount of triggers possible. - } - - //Step 3: If you used all the procs in the time frame then set proc amount back to max - if (buffs[buffslot].focusproclimit_procamt == 0 && spells[spell_id].base[i] > 0) { - buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i];//reset to max - - //Step 4: Check if timer exists on this spell, and then set it, and activiate global timer if not active - if (buffs[buffslot].focusproclimit_time ==0 && spells[spell_id].base2[i] > 0) { - buffs[buffslot].focusproclimit_time = spells[spell_id].base2[i];//set time - - //Step 5: If timer is not already running, then start it. - if (!focus_proc_limit_timer.Enabled()) { - focus_proc_limit_timer.Start(250); - } - - return true; + else { + Mob* current_target = GetTarget(); + //For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior. + if (current_target && current_target->GetID() != GetID()) + SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); } } + if (i >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, i); } } } - return true; -} - -void Mob::FocusProcLimitProcess() -{ - /* - Fast 250 ms uinversal timer for checking Focus effects that have a proc rate limiter set in actual time. - */ - bool stop_timer = true; - int buff_count = GetMaxTotalSlots(); - for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i) - { - if (IsValidSpell(buffs[buffs_i].spellid)) - { - if (buffs[buffs_i].focusproclimit_time > 0) { - buffs[buffs_i].focusproclimit_time -= 250; - stop_timer = false; - } - - if (buffs[buffs_i].focusproclimit_time < 0) - buffs[buffs_i].focusproclimit_time = 0; - } - } - - if (stop_timer) { - focus_proc_limit_timer.Disable(); - } } void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) @@ -8668,3 +8612,45 @@ void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_re } } } + +bool Mob::IsFocusProcLimitTimerActive(int32 focus_spell_id) { + /* + Used with SPA SE_Ff_FocusTimerMin to limit how often a focus effect can be applied. + Ie. Can only have a spell trigger once every 15 seconds, or to be more creative can only + have the fire spells received a very high special focused once every 30 seconds. + Note, this stores timers for both spell, item and AA related focuses For AA the focus_spell_id + is saved as the the negative value of the rank.id (to avoid conflicting with spell_ids) + */ + for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { + if (focusproclimit_spellid[i] == focus_spell_id) { + if (focusproclimit_timer[i].Enabled()) { + if (focusproclimit_timer[i].GetRemainingTime() > 0) { + return true; + } + else { + focusproclimit_timer[i].Disable(); + focusproclimit_spellid[i] = 0; + } + } + } + } + return false; +} + +void Mob::SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time) { + + bool is_set = false; + + for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { + if (!focusproclimit_spellid[i] && !is_set) { + focusproclimit_spellid[i] = focus_spell_id; + focusproclimit_timer[i].SetTimer(focus_reuse_time); + is_set = true; + } + //Remove old temporary focus if was from a buff you no longer have. + else if (focusproclimit_spellid[i] > 0 && !FindBuff(focus_spell_id)) { + focusproclimit_spellid[i] = 0; + focusproclimit_timer[i].Disable(); + } + } +} diff --git a/zone/spells.cpp b/zone/spells.cpp index f4d0a0647..2ca332100 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3421,8 +3421,6 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].dot_rune = 0; buffs[emptyslot].ExtraDIChance = 0; buffs[emptyslot].RootBreakChance = 0; - buffs[emptyslot].focusproclimit_time = 0; - buffs[emptyslot].focusproclimit_procamt = 0; buffs[emptyslot].virus_spread_time = 0; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index c5f3ce418..bc463b99b 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3670,8 +3670,6 @@ void ZoneDatabase::LoadBuffs(Client *client) buffs[slot_id].caston_z = caston_z; buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].RootBreakChance = 0; - buffs[slot_id].focusproclimit_time = 0; - buffs[slot_id].focusproclimit_procamt = 0; buffs[slot_id].virus_spread_time = 0; buffs[slot_id].UpdateClient = false; buffs[slot_id].instrument_mod = instrument_mod;