diff --git a/common/ruletypes.h b/common/ruletypes.h index b0c8074b1..533567b0e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -380,6 +380,7 @@ RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.") +RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/common/spdat.h b/common/spdat.h index 95016bd6d..863281e4b 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1078,9 +1078,9 @@ typedef enum { #define SE_FcTimerLockout 390 // implemented, @Fc, On Caster, set a spell to be on recast timer, base: recast duration milliseconds, Note: Applied from casted spells only #define SE_LimitManaMax 391 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt #define SE_FcHealAmt 392 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt -#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received critical chance mod, base: chance pct +#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct #define SE_FcHealAmtIncoming 394 // implemented, @Fc, On Target, heal received mod flat amt, base: amt -#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct +#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct #define SE_FcHealAmtCrit 396 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt #define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC #define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets diff --git a/zone/bot.cpp b/zone/bot.cpp index 0cb352a18..9f9f723b9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6776,7 +6776,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += (GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier); - value += (value * target->GetHealRate(spell_id, this) / 100); + value += (value * target->GetHealRate() / 100); if (Critical) entity_list.MessageClose(this, false, 100, Chat::SpellCrit, "%s performs an exceptional heal! (%d)", GetName(), value); diff --git a/zone/common.h b/zone/common.h index ba2f1eac9..4c5bfa4df 100644 --- a/zone/common.h +++ b/zone/common.h @@ -134,7 +134,7 @@ typedef enum { //focus types focusSwarmPetDuration, //@Fc, SPA: 398, SE_SwarmPetDuration, On Caster, swarm pet duration mod, base: milliseconds focusReduceRecastTime, //@Fc, SPA: 310, SE_ReduceReuseTimer, On Caster, disc reuse time mod, base: milliseconds focusBlockNextSpell, //@Fc, SPA: 335, SE_BlockNextSpellFocus, On Caster, chance to block next spell, base: chance - focusFcHealPctIncoming, //@Fc, SPA: 395, SE_FcHealPctCritIncoming, On Target, heal received mod pct, base: pct + focusFcHealPctIncoming, //@Fc, SPA: 393, SE_FcHealPctIncoming, On Target, heal received mod pct, base: pct focusFcDamageAmtIncoming, //@Fc, SPA: 297, SE_FcDamageAmtIncoming, On Target, damage taken flat amt, base: amt focusFcSpellDamageAmtIncomingPC, //@Fc, SPA: 484, SE_Fc_Spell_Damage_Amt_IncomingPC, On Target, damage taken flat amt, base: amt focusFcCastSpellOnLand, //@Fc, SPA: 481, SE_Fc_Cast_Spell_On_Land, On Target, cast spell if hit by spell, base: chance pct, limit: spellid @@ -151,7 +151,7 @@ typedef enum { //focus types focusFcAmplifyAmt, //@Fc, SPA: 508, SE_Fc_Amplify_Amt, On Caster, damage-heal-dot mod flat amt, base: amt focusFcCastTimeMod2, //@Fc, SPA: 500, SE_Fc_CastTimeMod2, On Caster, cast time mod pct, base: pct focusFcCastTimeAmt, //@Fc, SPA: 501, SE_Fc_CastTimeAmt, On Caster, cast time mod flat amt, base: milliseconds - focusFcHealPctCritIncoming, //@Fc, SPA: 393, SE_FcHealPctCritIncoming, On Target, heal received critical chance mod, base: chance pct + focusFcHealPctCritIncoming, //@Fc, SPA: 395, SE_FcHealPctCritIncoming, On Target, spell healing mod pct, base: pct focusFcHealAmt, //@Fc, SPA: 392, SE_FcHealAmt, On Caster, spell healing mod flat amt, base: amt focusFcHealAmtCrit, //@Fc, SPA: 396, SE_FcHealAmtCrit, On Caster, spell healing mod flat amt, base: amt } focusType; //Any new FocusType needs to be added to the Mob::IsFocus function diff --git a/zone/effects.cpp b/zone/effects.cpp index 37726df12..571abe6da 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -267,63 +267,93 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - if (target == nullptr) - target = this; - if (IsNPC()) - value += value*CastToNPC()->GetSpellFocusHeal()/100; + if (IsNPC()) { + value += value * CastToNPC()->GetSpellFocusHeal() / 100; + } int32 value_BaseEffect = 0; - int16 chance = 0; - int8 modifier = 1; - bool Critical = false; + int16 critical_chance = 0; + int8 critical_modifier = 1; - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); + if (spells[spell_id].buffduration < 1) { + critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + if (spellbonuses.CriticalHealDecay) { + critical_chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + } + } + else { + critical_chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + if (spellbonuses.CriticalRegenDecay) { + critical_chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); + } + } + + if (critical_chance) { + + if (spells[spell_id].override_crit_chance > 0 && critical_chance > spells[spell_id].override_crit_chance) { + critical_chance = spells[spell_id].override_crit_chance; + } + + if (zone->random.Roll(critical_chance)) { + critical_modifier = 2; //At present time no critical heal amount modifier SPA exists. + } + } + + value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id) / 100); value = value_BaseEffect; - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); + if (GetClass() == CLERIC) { + value += int(value_BaseEffect*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus + } + value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id) / 100); value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); // Instant Heals - if(spells[spell_id].buffduration < 1) { + if (spells[spell_id].buffduration < 1) { - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; - - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - - if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance) - chance = spells[spell_id].override_crit_chance; - - if(chance && (zone->random.Roll(chance))) { - Critical = true; - modifier = 2; //At present time no critical heal amount modifier SPA exists. + if (target) { + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) } - value *= modifier; - value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; - value += GetFocusEffect(focusFcHealAmt, spell_id); - value += GetFocusEffect(focusFcAmplifyAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical + + if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value); //Item Heal Amt Add before critical + } - if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%17) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; + if (target) { + value += value * target->GetHealRate() / 100; //SPA 120 modifies value after Focus Applied but before critical + } + + /* + Apply critical hit modifier + */ - value += value*target->GetHealRate(spell_id, this)/100; + value *= critical_modifier; + value += GetFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical + value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical - if (IsNPC() && CastToNPC()->GetHealScale()) + if (target) { + value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical + } + + if (IsNPC() && CastToNPC()->GetHealScale()) { value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); + } - if (Critical) { + if (critical_modifier > 1) { entity_list.MessageCloseString( this, true, 100, Chat::SpellCrit, OTHER_CRIT_HEAL, GetName(), itoa(value)); - if (IsClient()) + if (IsClient()) { MessageString(Chat::SpellCrit, YOU_CRIT_HEAL, itoa(value)); + } } return value; @@ -331,20 +361,13 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - - if (spellbonuses.CriticalRegenDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - - if(chance && zone->random.Roll(chance)) - value *= 2; + if (critical_chance && zone->random.Roll(critical_chance)) + value *= critical_modifier; } - if (IsNPC() && CastToNPC()->GetHealScale()) + if (IsNPC() && CastToNPC()->GetHealScale()) { value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); + } return value; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 69bf30531..96591b9e1 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3878,20 +3878,6 @@ int32 Mob::GetPositionalDmgTakenAmt(Mob *attacker) return total_amt; } - -int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { - - int16 heal_rate = 0; - - heal_rate += itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; - heal_rate += GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, caster, spell_id); - - if(heal_rate < -99) - heal_rate = -99; - - return heal_rate; -} - void Mob::SetBottomRampageList() { auto &mob_list = entity_list.GetCloseMobList(this); diff --git a/zone/mob.h b/zone/mob.h index dcdb66d3e..ac222a3dd 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -803,10 +803,9 @@ public: void TrySympatheticProc(Mob *target, uint32 spell_id); bool TryFadeEffect(int slot); uint16 GetSpellEffectResistChance(uint16 spell_id); - int16 GetHealRate(uint16 spell_id, Mob* caster = nullptr); int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); - int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); + int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); //**** This can be removed when bot healing focus code is updated **** int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); int32 GetPositionalDmgTakenAmt(Mob *attacker); @@ -846,8 +845,10 @@ public: bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); bool CanFocusUseRandomEffectivenessByType(focusType type); int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); + int GetHealRate() const { return itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; } int GetMemoryBlurChance(int base_chance); + bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 22a45bda0..e634e70d3 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5743,7 +5743,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_FcHealPctCritIncoming: if (type == focusFcHealPctCritIncoming) { - value = focus_spell.base[i]; + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5761,7 +5761,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_FcHealPctIncoming: if (type == focusFcHealPctIncoming) { - value = focus_spell.base[i]; + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5925,7 +5925,7 @@ void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id) } } - // Only use of this focus per AA effect. + // Only use one of this focus per AA effect. if (IsClient() && aabonuses.FocusEffects[type]) { for (const auto &aa : aa_ranks) { auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); @@ -6372,8 +6372,9 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2)) + if (RuleB(Spells, LiveLikeFocusEffects) && CanFocusUseRandomEffectivenessByType(type)) { rand_effectiveness = true; + } if (RuleB(Spells, NPC_UseFocusFromItems) && itembonuses.FocusEffects[type]){ @@ -6933,50 +6934,54 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id) { + //**** This can be removed when bot healing focus code is updated **** + /* This is a general function for calculating best focus effect values for focus effects that exist on targets but modify incoming spells. Should be used when checking for foci that can exist on clients or npcs ect. Example: When your target has a focus limited buff that increases amount of healing on them. */ - if (!caster) + if (!caster) { return 0; + } int value = 0; if (spellbonuses.FocusEffects[type]){ - int32 tmp_focus = 0; - int tmp_buffslot = -1; + int32 tmp_focus = 0; + int tmp_buffslot = -1; - int buff_count = GetMaxTotalSlots(); - for(int i = 0; i < buff_count; i++) { + int buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++) { - if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, effect))){ + if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, effect))){ - int32 focus = caster->CalcFocusEffect(type, buffs[i].spellid, spell_id); + int32 focus = caster->CalcFocusEffect(type, buffs[i].spellid, spell_id); - if (!focus) - continue; + if (!focus) { + continue; + } - if (tmp_focus && focus > tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } + if (tmp_focus && focus > tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; + } - else if (!tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } + else if (!tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; } } - - value = tmp_focus; - - if (tmp_buffslot >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); } + value = tmp_focus; + + if (tmp_buffslot >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); + } + return value; } @@ -8498,6 +8503,8 @@ bool Mob::CanFocusUseRandomEffectivenessByType(focusType type) case focusSpellHateMod: case focusSpellVulnerability: case focusFcSpellDamagePctIncomingPC: + case focusFcHealPctIncoming: + case focusFcHealPctCritIncoming: return true; }