[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.
This commit is contained in:
KayenEQ 2021-10-26 21:36:10 -04:00 committed by GitHub
parent ef5124d756
commit fb66afd565
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 99 additions and 117 deletions

View File

@ -1255,6 +1255,7 @@ bool IsEffectIgnoredInStacking(int spa)
case SE_Ff_ReuseTimeMax: case SE_Ff_ReuseTimeMax:
case SE_Ff_Value_Min: case SE_Ff_Value_Min:
case SE_Ff_Value_Max: case SE_Ff_Value_Max:
case SE_Ff_FocusTimerMin:
return true; return true;
default: default:
return false; return false;
@ -1296,6 +1297,7 @@ bool IsFocusLimit(int spa)
case SE_Ff_ReuseTimeMax: case SE_Ff_ReuseTimeMax:
case SE_Ff_Value_Min: case SE_Ff_Value_Min:
case SE_Ff_Value_Max: case SE_Ff_Value_Max:
case SE_Ff_FocusTimerMin:
return true; return true;
default: default:
return false; return false;

View File

@ -180,7 +180,7 @@
#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects #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_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_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; 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_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_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_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt
//#define SE_Ff_FocusTimerMin 511 // #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 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500). #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_Mana_Max_Percent 513 //
//#define SE_Endurance_Max_Percent 514 // //#define SE_Endurance_Max_Percent 514 //
#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier

View File

@ -509,9 +509,6 @@ bool Client::Process() {
} }
} }
if (focus_proc_limit_timer.Check() && !dead)
FocusProcLimitProcess();
if (client_state == CLIENT_KICKED) { if (client_state == CLIENT_KICKED) {
Save(); Save();
OnDisconnect(true); OnDisconnect(true);

View File

@ -328,8 +328,6 @@ struct Buffs_Struct {
int32 ExtraDIChance; int32 ExtraDIChance;
int16 RootBreakChance; //Not saved to dbase int16 RootBreakChance; //Not saved to dbase
uint32 instrument_mod; 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 int32 virus_spread_time; //time till next attempted viral spread
bool persistant_buff; bool persistant_buff;
bool client; //True if the caster is a client bool client; //True if the caster is a client

View File

@ -103,7 +103,6 @@ Mob::Mob(
ranged_timer(2000), ranged_timer(2000),
tic_timer(6000), tic_timer(6000),
mana_timer(2000), mana_timer(2000),
focus_proc_limit_timer(250),
spellend_timer(0), spellend_timer(0),
rewind_timer(30000), rewind_timer(30000),
bindwound_timer(10000), bindwound_timer(10000),
@ -350,6 +349,11 @@ Mob::Mob(
ProjectileAtk[i].speed_mod = 0.0f; 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(&itembonuses, 0, sizeof(StatBonuses));
memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses));
memset(&aabonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses));

View File

@ -857,8 +857,9 @@ public:
inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; }
void CastSpellOnLand(Mob* caster, int32 spell_id); 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 VirusEffectProcess();
void SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining); 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%) int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
Timer tic_timer; Timer tic_timer;
Timer mana_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; Timer shield_timer;
uint32 m_shield_target_id; uint32 m_shield_target_id;
@ -1560,7 +1563,6 @@ protected:
Timer bardsong_timer; Timer bardsong_timer;
Timer gravity_timer; Timer gravity_timer;
Timer viral_timer; Timer viral_timer;
uint8 viral_timer_counter;
// MobAI stuff // MobAI stuff
eStandingPetOrder pStandingPetOrder; eStandingPetOrder pStandingPetOrder;

View File

@ -1125,9 +1125,6 @@ void Mob::AI_Process() {
ProjectileAttack(); ProjectileAttack();
if (focus_proc_limit_timer.Check())
FocusProcLimitProcess();
if (shield_timer.Check()) { if (shield_timer.Check()) {
ShieldAbilityFinish(); ShieldAbilityFinish();
} }

View File

@ -2992,11 +2992,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break; 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: { case SE_PetShield: {
if (IsPet()) { if (IsPet()) {
Mob* petowner = GetOwner(); 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_Skill_Base_Damage_Mod:
case SE_Worn_Endurance_Regen_Cap: case SE_Worn_Endurance_Regen_Cap:
case SE_Buy_AA_Rank: case SE_Buy_AA_Rank:
case SE_Ff_FocusTimerMin:
{ {
break; break;
} }
@ -4570,6 +4566,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
uint32 slot = 0; uint32 slot = 0;
int index_id = -1; int index_id = -1;
uint32 focus_reuse_time = 0;
bool LimitFailure = false; bool LimitFailure = false;
bool LimitInclude[MaxLimitInclude] = {false}; bool LimitInclude[MaxLimitInclude] = {false};
@ -4917,6 +4914,15 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
} }
break; 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. /* 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: case SE_Ff_Same_Caster:
@ -5217,6 +5223,10 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
return 0; return 0;
} }
if (focus_reuse_time) {
SetFocusProcLimitTimer(-rank.id, focus_reuse_time);
}
return (value * lvlModifier / 100); return (value * lvlModifier / 100);
} }
@ -5247,6 +5257,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
int lvldiff = 0; int lvldiff = 0;
uint32 Caston_spell_id = 0; uint32 Caston_spell_id = 0;
int index_id = -1; 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}; bool LimitInclude[MaxLimitInclude] = {false};
/* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive
@ -5579,6 +5590,15 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
} }
break; break;
case SE_Ff_FocusTimerMin:
if (IsFocusProcLimitTimerActive(focus_spell.id)) {
return 0;
}
else {
focus_reuse_time = focus_spell.base2[i];
}
break;
// handle effects // handle effects
case SE_ImprovedDamage: case SE_ImprovedDamage:
if (type == focusImprovedDamage) { if (type == focusImprovedDamage) {
@ -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); return (value * lvlModifier / 100);
} }
@ -8293,9 +8317,7 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id)
if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != 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. //Step 3: Cast spells
if (ApplyFocusProcLimiter(buffs[i].spellid, i)) {
//Step 4: Cast spells
if (IsBeneficialSpell(trigger_spell_id)) { if (IsBeneficialSpell(trigger_spell_id)) {
SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff);
} }
@ -8306,89 +8328,11 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id)
SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff);
} }
} }
if (i >= 0) if (i >= 0)
CheckNumHitsRemaining(NumHit::MatchingSpells, i); 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;
}
}
}
}
}
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) 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();
}
}
}

View File

@ -3421,8 +3421,6 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
buffs[emptyslot].dot_rune = 0; buffs[emptyslot].dot_rune = 0;
buffs[emptyslot].ExtraDIChance = 0; buffs[emptyslot].ExtraDIChance = 0;
buffs[emptyslot].RootBreakChance = 0; buffs[emptyslot].RootBreakChance = 0;
buffs[emptyslot].focusproclimit_time = 0;
buffs[emptyslot].focusproclimit_procamt = 0;
buffs[emptyslot].virus_spread_time = 0; buffs[emptyslot].virus_spread_time = 0;
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;

View File

@ -3670,8 +3670,6 @@ void ZoneDatabase::LoadBuffs(Client *client)
buffs[slot_id].caston_z = caston_z; buffs[slot_id].caston_z = caston_z;
buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].ExtraDIChance = ExtraDIChance;
buffs[slot_id].RootBreakChance = 0; 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].virus_spread_time = 0;
buffs[slot_id].UpdateClient = false; buffs[slot_id].UpdateClient = false;
buffs[slot_id].instrument_mod = instrument_mod; buffs[slot_id].instrument_mod = instrument_mod;