Implemented SPA 481, 485,486,512

Implemented

SE_Fc_Cast_Spell_On_Land 481
Focus effect that is checked when a spell is cast on a target, if target has this focus effect and all limiting criteria are met, then the target will cast a spell as specified by the focus. Can be given a roll chance for success. Base1=Chance, Base2=Spellid

Note: This spell has a huge amount of potential applications. See 'Alliance' type spells on live. (ie live spell 50247)

Implemented associated focus limits seen in live spells.

SE_Ff_CasterClass	485
- Caster of spell on target with a focus effect that is checked by incoming spells must be specified class or classes.

SE_Ff_Same_Caster	 486 -Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster

The following is an associated effect seen with SPA 481

SE_Proc_Timer_Modifier 			512
This provides a way to rate limit the amount of spell triggers generated by SPA 481. For example after 1 successful spell trigger no additional spells can be triggered for 1.5 seconds. Ie. Base=1 and Base2 1500.
Written in a flexible format to allow scaling of multiple different buffs with this effect at same time.
This commit is contained in:
KayenEQ
2021-07-16 00:11:06 -04:00
parent 581b21109e
commit bafd5c4cb2
11 changed files with 244 additions and 32 deletions
+208 -23
View File
@@ -2934,17 +2934,22 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
}
if (GetLevel() <= spells[spell_id].max[i]) {
if (IsClient())
if (spells[spell_id].max[i] == 0 || GetLevel() <= spells[spell_id].max[i]) {
if (IsClient() && spells[spell_id].base2[i])
Stun(spells[spell_id].base2[i]);
else
Stun(effect_value);
Stun(spells[spell_id].base[i]);
}
else
caster->MessageString(Chat::SpellFailure, FEAR_TOO_HIGH);
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_PersistentEffect:
MakeAura(spell_id);
break;
@@ -3206,6 +3211,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_Pet_Crit_Melee_Damage_Pct_Owner:
case SE_Pet_Add_Atk:
case SE_TwinCastBlocker:
case SE_Fc_Cast_Spell_On_Land:
case SE_Ff_CasterClass:
case SE_Ff_Same_Caster:
{
break;
}
@@ -4481,13 +4489,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
case SE_Blank:
break;
// Handle Focus Limits
// Handle Focus Limits
case SE_LimitResist:
if (base1 < 0) {
if (spell.resisttype == -base1) // Exclude
LimitFailure = true;
} else {
}
else {
LimitInclude[0] = true;
if (spell.resisttype == base1) // Include
LimitInclude[1] = true;
@@ -4508,12 +4517,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
// every level over cap reduces the effect by base2 percent unless from a clicky when
// ItemCastsUseFocus is true
if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) ||
RuleB(Character, ItemCastsUseFocus) == false)) {
RuleB(Character, ItemCastsUseFocus) == false)) {
if (base2 > 0) {
lvlModifier -= base2 * lvldiff;
if (lvlModifier < 1)
LimitFailure = true;
} else
}
else
LimitFailure = true;
}
break;
@@ -4537,7 +4547,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) { // Exclude
if (spell_id == -base1)
LimitFailure = true;
} else {
}
else {
LimitInclude[2] = true;
if (spell_id == base1) // Include
LimitInclude[3] = true;
@@ -4554,13 +4565,15 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) {
if (IsEffectInSpell(spell_id, -base1)) // Exclude
LimitFailure = true;
} else {
}
else {
LimitInclude[4] = 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;
} else {
}
else {
if (IsEffectInSpell(spell_id, base1)) // Include
LimitInclude[5] = true;
}
@@ -4594,7 +4607,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) {
if (-base1 == spell.targettype) // Exclude
LimitFailure = true;
} else {
}
else {
LimitInclude[6] = true;
if (base1 == spell.targettype) // Include
LimitInclude[7] = true;
@@ -4613,7 +4627,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) {
if (-base1 == spell.spellgroup) // Exclude
LimitFailure = true;
} else {
}
else {
LimitInclude[8] = true;
if (base1 == spell.spellgroup) // Include
LimitInclude[9] = true;
@@ -4624,7 +4639,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) {
if (-base1 == spell.skill)
LimitFailure = true;
} else {
}
else {
LimitInclude[10] = true;
if (base1 == spell.skill)
LimitInclude[11] = true;
@@ -4635,7 +4651,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) { // Exclude
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass))
return (0);
} else {
}
else {
LimitInclude[12] = true;
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include
LimitInclude[13] = true;
@@ -4646,7 +4663,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (base1 < 0) { // Exclude
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass))
return (0);
} else {
}
else {
LimitInclude[14] = true;
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include
LimitInclude[15] = true;
@@ -4675,7 +4693,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
LimitFailure = true;
break;
// Handle Focus Effects
// Handle Focus Effects
case SE_ImprovedDamage:
if (type == focusImprovedDamage && base1 > value)
value = base1;
@@ -4742,11 +4760,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (value > 0) {
if (base1 > value)
value = base1;
} else {
}
else {
if (base1 < value)
value = base1;
}
} else
}
else
value = base1;
}
break;
@@ -4760,7 +4780,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (type == focusTriggerOnCast) {
if (zone->random.Roll(base1)) {
value = base2;
} else {
}
else {
value = 0;
LimitFailure = true;
}
@@ -4771,7 +4792,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (type == focusSpellVulnerability)
value = base1;
break;
case SE_Fc_Spell_Damage_Pct_IncomingPC:
if (type == focusFcSpellDamagePctIncomingPC)
value = base1;
@@ -4789,7 +4810,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
value = base1;
break;
// Note if using these as AA, make sure this is first focus used.
// Note if using these as AA, make sure this is first focus used.
case SE_SympatheticProc:
if (type == focusSympatheticProc)
value = base2;
@@ -4874,6 +4895,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
if (type == focusFcStunTimeMod)
value = base1;
break;
case SE_Fc_Cast_Spell_On_Land:
if (type == focusFcCastSpellOnLand) {
if (zone->random.Roll(base1)) {
value = base2;
}
break;
}
}
}
@@ -4890,8 +4919,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
//given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any
//assumes that spell_id is not a bard spell and that both ids are valid spell ids
int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus)
int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid)
{
/*
'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target.
'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))
return 0;
@@ -5125,6 +5159,23 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
}
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) {
@@ -5414,6 +5465,14 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
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;
}
#if EQDEBUG >= 6
// this spits up a lot of garbage when calculating spell focuses
// since they have all kinds of extra effects on them.
@@ -7089,6 +7148,132 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
}
}
void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id)
{
/*
This function checks for incoming spells on a mob, if they meet the criteria for focus SE_Fc_Cast_Spell_on_Land then
a new spell will be cast by THIS mob as specified by the focus effect. Note: Chance to cast the spell is determined in
the CalcFocusEffect function if not 100pct.
ApplyFocusProcLimiter() function checks for SE_Proc_Timer_Modifier which allows for limiting how often a spell from effect can be triggered
for example, if set to base=1 and base2= 1500, then for everyone 1 successful trigger, you will be unable to trigger again for 1.5 seconds.
*/
if (!caster)
return;
uint32 trigger_spell_id = 0;
//Step 1: Check this focus effect exists on the mob.
if (spellbonuses.FocusEffects[focusFcCastSpellOnLand]) {
int buff_count = GetMaxTotalSlots();
for (int i = 0; i < buff_count; i++) {
if ((IsValidSpell(buffs[i].spellid) && (buffs[i].spellid != spell_id) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Cast_Spell_On_Land))) {
//Step 2: Check if we pass all focus limiters and focus chance roll
trigger_spell_id = caster->CalcFocusEffect(focusFcCastSpellOnLand, buffs[i].spellid, spell_id, false, buffs[i].casterid);
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);
}
}
if (i >= 0)
CheckNumHitsRemaining(NumHit::MatchingSpells, i);
}
}
}
}
}
bool Mob::ApplyFocusProcLimiter(uint32 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
base2= lock out timer, which prevents any more procs
This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks.
*/
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();
}
}
bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){
if (!IsValidSpell(spell_id) || !category_id)