diff --git a/common/spdat.h b/common/spdat.h index fa2bdd369..5d21dff00 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -638,7 +638,7 @@ typedef enum { #define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. //#define SE_ReduceTimerSpecial 295 // not used -#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage +#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage [base1= min dmg base2= max dmg] #define SE_FcDamageAmtIncoming 297 // implemented - debuff that adds points damage to spells cast on target (focus effect). #define SE_ChangeHeight 298 // implemented #define SE_WakeTheDead 299 // implemented @@ -825,7 +825,7 @@ typedef enum { //#define SE_Ff_Value_Max 480 // //#define SE_Fc_Cast_Spell_On_Land 481 // //#define SE_Skill_Base_Damage_Mod 482 // -//#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // +#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // //#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // //#define SE_Ff_CasterClass 485 // //#define SE_Ff_Same_Caster 486 // @@ -856,7 +856,7 @@ typedef enum { //#define SE_Ff_FocusTimerMin 511 // //#define SE_Proc_Timer_Modifier 512 // //#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_Mitigation_Max_Percent 516 // implemented - stackable defense modifier //#define SE_Attack_Offense_Max_Percent 517 // diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index fd635ffee..caad39302 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3736,6 +3736,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return 0; //This is calculated as an actual bonus case SE_FcSpellVulnerability: return focusSpellVulnerability; + case SE_Fc_Spell_Damage_Pct_IncomingPC: + return focusFcSpellDamagePctIncomingPC; case SE_BlockNextSpellFocus: //return focusBlockNextSpell; return 0; //This is calculated as an actual bonus @@ -3775,6 +3777,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusFcHealAmt; case SE_FcHealAmtCrit: return focusFcHealAmtCrit; + } return 0; } diff --git a/zone/common.h b/zone/common.h index 5455162ae..127b07941 100644 --- a/zone/common.h +++ b/zone/common.h @@ -124,6 +124,7 @@ typedef enum { //focus types focusSpellHateMod, focusTriggerOnCast, focusSpellVulnerability, + focusFcSpellDamagePctIncomingPC, focusTwincast, focusSympatheticProc, focusFcDamageAmt, diff --git a/zone/mob.cpp b/zone/mob.cpp index 60037057a..4a7dbb320 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3767,28 +3767,40 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) { + /* + Modifies incoming spell damage by percent, to increase or decrease damage, can be limited to specific resists. + Can be applied through quest function, spell focus or npc_spells_effects table. This function is run on the target of the spell. + */ + if (!IsValidSpell(spell_id)) return 0; if (!caster) return 0; - int32 value = 0; + int32 total_mod = 0; + int32 innate_mod = 0; + int32 fc_spell_vulnerability_mod = 0; + int32 fc_spell_damage_pct_incomingPC_mod = 0; - //Apply innate vulnerabilities + //Apply innate vulnerabilities from quest functions and tables if (Vulnerability_Mod[GetSpellResistType(spell_id)] != 0) - value = Vulnerability_Mod[GetSpellResistType(spell_id)]; - + innate_mod = Vulnerability_Mod[GetSpellResistType(spell_id)]; else if (Vulnerability_Mod[HIGHEST_RESIST+1] != 0) - value = Vulnerability_Mod[HIGHEST_RESIST+1]; + innate_mod = Vulnerability_Mod[HIGHEST_RESIST+1]; - //Apply spell derived vulnerabilities - if (spellbonuses.FocusEffects[focusSpellVulnerability]){ + //[Apply spell derived vulnerabilities] Step 1: Check this focus effect exists on the mob. + if (spellbonuses.FocusEffects[focusSpellVulnerability]){ int32 tmp_focus = 0; int tmp_buffslot = -1; + /* + Find all buffs that may contain SPA 296, then find which slot has the highest possible effect. Since the focus can use + a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the + best is found. Run it again to get the final value randoming between min and max. + */ int buff_count = GetMaxTotalSlots(); for(int i = 0; i < buff_count; i++) { @@ -3808,21 +3820,61 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) tmp_focus = focus; tmp_buffslot = i; } - } } - tmp_focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id); - - if (tmp_focus < -99) - tmp_focus = -99; - - value += tmp_focus; + fc_spell_vulnerability_mod = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id); if (tmp_buffslot >= 0) CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); } - return value; + + if (spellbonuses.FocusEffects[focusFcSpellDamagePctIncomingPC]) { + + int32 tmp_focus = 0; + int tmp_buffslot = -1; + + /* + Find all buffs that may contain SPA 483, then find which slot has the highest possible effect. Since the focus can use + a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the + best is found. Run it again to get the final value randoming between min and max. + */ + int buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) { + + if ((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Spell_Damage_Pct_IncomingPC))) { + + int32 focus = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[i].spellid, spell_id, true); + + if (!focus) + continue; + + if (tmp_focus && focus > tmp_focus) { + tmp_focus = focus; + tmp_buffslot = i; + } + + else if (!tmp_focus) { + tmp_focus = focus; + tmp_buffslot = i; + } + } + } + + fc_spell_damage_pct_incomingPC_mod = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[tmp_buffslot].spellid, spell_id); + + if (tmp_buffslot >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); + } + + total_mod = fc_spell_vulnerability_mod + fc_spell_damage_pct_incomingPC_mod; + + //Don't let focus derived mods reduce past 99% mitigation. Quest related can, and for custom functionality if negative will give a healing affect instead of damage. + if (total_mod < -99) + total_mod = -99; + + total_mod += innate_mod; + return total_mod; } int32 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 08f912189..e96c62380 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3060,6 +3060,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_HealRate: case SE_SkillDamageTaken: case SE_FcSpellVulnerability: + case SE_Fc_Spell_Damage_Pct_IncomingPC: case SE_FcTwincast: case SE_DelayDeath: case SE_CastOnFadeEffect: @@ -4769,6 +4770,11 @@ 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; + break; case SE_BlockNextSpellFocus: if (type == focusBlockNextSpell) { @@ -5284,13 +5290,30 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo if (type == focusSpellVulnerability) { if (best_focus) { if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; + value = focus_spell.base2[i]; //max damage else - value = focus_spell.base[i]; + value = focus_spell.base[i]; //min damage } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; + value = focus_spell.base[i]; //If no max damage set, then default to min damage } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + } + } + break; + + case SE_Fc_Spell_Damage_Pct_IncomingPC: + if (type == focusFcSpellDamagePctIncomingPC) { + if (best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; //max damage + else + value = focus_spell.base[i]; //min damage + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; //If no max damage set, then default to min damage + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value } } break;