diff --git a/common/ruletypes.h b/common/ruletypes.h index 7f463e733..40682abcd 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -319,7 +319,7 @@ RULE_INT(Spells, MaxDiscSlotsNPC, 0, "Maximum number of NPC disc slots. NPC don' RULE_INT(Spells, MaxTotalSlotsNPC, 60, "Maximum total of NPC slots. The default value is the limit of the Titanium client") RULE_INT(Spells, MaxTotalSlotsPET, 30, "Maximum total of pet slots. The default value is the limit of the Titanium client") RULE_BOOL (Spells, EnableBlockedBuffs, true, "Allow blocked spells") -RULE_INT(Spells, ReflectType, 3, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells") +RULE_INT(Spells, ReflectType, 4, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells") RULE_BOOL(Spells, ReflectMessagesClose, true, "True (Live functionality) is for Reflect messages to show to players within close proximity. False shows just player reflecting") RULE_INT(Spells, VirusSpreadDistance, 30, "The distance a viral spell will jump to its next victim") RULE_BOOL(Spells, LiveLikeFocusEffects, true, "Determines whether specific healing, dmg and mana reduction focuses are randomized") diff --git a/common/spdat.h b/common/spdat.h index 509170000..ebd39eed4 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -511,6 +511,15 @@ enum NegateSpellEffectType NEGATE_SPA_SPELLBONUS_AND_AABONUS = 5, NEGATE_SPA_ITEMBONUS_AND_AABONUS = 6, }; +//Used for rule RuleI(Spells, ReflectType)) +enum ReflectSpellType +{ + REFLECT_DISABLED = 0, + REFLECT_SINGLE_TARGET_SPELLS_ONLY = 1, + REFLECT_ALL_PLAYER_SPELLS = 2, + RELFECT_ALL_SINGLE_TARGET_SPELLS = 3, + REFLECT_ALL_SPELLS = 4, +}; enum SpellTypes : uint32 { @@ -843,7 +852,7 @@ typedef enum { #define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently #define SE_IllusionCopy 156 // implemented - Deception #define SE_SpellDamageShield 157 // implemented, @DS, causes non-melee damage on caster of a spell, base: Amt DS (negative), limit: none, max: unknown (same as base but +) -#define SE_Reflect 158 // implemented +#define SE_Reflect 158 // implemented, @SpellMisc, reflect casted detrimental spell back at caster, base: chance pct, limit: resist modifier (positive value reduces resists), max: pct of base dmg mod (50=50pct of base) #define SE_AllStats 159 // implemented //#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance) #define SE_MitigateSpellDamage 161 // implemented - rune with max value diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 9a640196a..e56de4daf 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1672,6 +1672,17 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->ZoneSuspendMinion = base1; break; + + case SE_Reflect: + + if (newbon->reflect[SBIndex::REFLECT_CHANCE] < base1) { + newbon->reflect[SBIndex::REFLECT_CHANCE] = base1; + } + if (newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] < base2) { + newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = base2; + } + break; + case SE_SpellDamageShield: newbon->SpellDamageShield += base1; break; @@ -2152,7 +2163,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_Reflect: - new_bonus->reflect_chance += effect_value; + + if (AdditiveWornBonus) { + new_bonus->reflect[SBIndex::REFLECT_CHANCE] += effect_value; + } + + else if (new_bonus->reflect[SBIndex::REFLECT_CHANCE] < effect_value) { + new_bonus->reflect[SBIndex::REFLECT_CHANCE] = effect_value; + new_bonus->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = base2; + new_bonus->reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] = max; + } break; case SE_Amplification: @@ -4381,9 +4401,9 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) break; case SE_Reflect: - if (negate_spellbonus) { spellbonuses.reflect_chance = effect_value; } - if (negate_aabonus) { aabonuses.reflect_chance = effect_value; } - if (negate_itembonus) { itembonuses.reflect_chance = effect_value; } + if (negate_spellbonus) { spellbonuses.reflect[SBIndex::REFLECT_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.reflect[SBIndex::REFLECT_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.reflect[SBIndex::REFLECT_CHANCE] = effect_value; } break; case SE_Amplification: diff --git a/zone/common.h b/zone/common.h index 4c5bfa4df..937cffc51 100644 --- a/zone/common.h +++ b/zone/common.h @@ -404,7 +404,7 @@ struct StatBonuses { int32 skillmodmax[EQ::skills::HIGHEST_SKILL + 1]; int effective_casting_level; int adjusted_casting_skill; // SPA 112 for fizzles - int reflect_chance; // chance to reflect incoming spell + int reflect[3]; // chance to reflect incoming spell [0]=Chance [1]=Resist Mod [2]= % of Base Dmg uint32 singingMod; uint32 Amplification; // stacks with singingMod uint32 brassMod; @@ -680,6 +680,9 @@ namespace SBIndex { constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 440, 345, 346 constexpr uint16 DOUBLE_MELEE_ROUND_CHANCE = 0; // SPA 471 constexpr uint16 DOUBLE_MELEE_ROUND_DMG_BONUS = 1; // SPA 471 + constexpr uint16 REFLECT_CHANCE = 0; // SPA 158 + constexpr uint16 REFLECT_RESISTANCE_MOD = 1; // SPA 158 + constexpr uint16 REFLECT_DMG_EFFECTIVENESS = 2; // SPA 158 }; diff --git a/zone/effects.cpp b/zone/effects.cpp index b7319e793..4180d774c 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -117,10 +117,10 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio / 100; + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect)*ratio / 100; else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect)*ratio/100; else if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); @@ -156,10 +156,10 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect); else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect); if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); @@ -167,6 +167,33 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { return value; } +int32 Mob::GetActReflectedSpellDamage(int32 spell_id, int32 value, int effectiveness) { + /* + Reflected spells use the spells base damage before any modifiers or formulas applied. + That value can then be modifier by the reflect spells 'max' value, defined here as effectiveness + Default effectiveness is set at 100. + Extra Spell Damage stat from the with the reflect effect will be applied to reflected damage + with no level limitation, this was confirmed with extensive parsing ~Kayen + */ + if (IsNPC()) { + value += value * CastToNPC()->GetSpellFocusDMG() / 100; + + if (CastToNPC()->GetSpellScale()) { + value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); + } + } + + int32 base_spell_dmg = value; + + value = value * effectiveness / 100; + + if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_spell_dmg); + } + + return value; +} + int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (target == nullptr) @@ -259,8 +286,9 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s else extra_spell_amt = extra_spell_amt * total_cast_time / 7000; + //Confirmed with parsing 10/9/21 ~Kayen if (extra_spell_amt * 2 > abs(base_spell_dmg)) { - return 0; + extra_spell_amt = abs(base_spell_dmg) / 2; } return extra_spell_amt; @@ -324,7 +352,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { 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 + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value_BaseEffect); //Item Heal Amt Add before critical } if (target) { @@ -1020,7 +1048,7 @@ void EntityList::AESpell( } current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + caster_mob->SpellOnTarget(spell_id, current_mob, 0, true, resist_adjust); /** * Increment hit count if max targets diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index 40805d648..156a320f9 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -342,7 +342,7 @@ int Lua_StatBonuses::Getadjusted_casting_skill() const { int Lua_StatBonuses::Getreflect_chance() const { Lua_Safe_Call_Int(); - return self->reflect_chance; + return self->reflect[SBIndex::REFLECT_CHANCE]; } uint32 Lua_StatBonuses::GetsingingMod() const { diff --git a/zone/mob.cpp b/zone/mob.cpp index d1c6ced3d..f0982115c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3214,12 +3214,12 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override); if(twinproc) - SpellOnTarget(spell_id, this, false, false, 0, true, level_override); + SpellOnTarget(spell_id, this, 0, false, 0, true, level_override); } else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override); if(twinproc) - SpellOnTarget(spell_id, on, false, false, 0, true, level_override); + SpellOnTarget(spell_id, on, 0, false, 0, true, level_override); } return; } @@ -4596,19 +4596,6 @@ bool Mob::TryDoubleMeleeRoundEffect() { return false; } -bool Mob::TryReflectSpell(uint32 spell_id) -{ - if (!spells[spell_id].reflectable) - return false; - - int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance; - - if(chance && zone->random.Roll(chance)) - return true; - - return false; -} - void Mob::DoGravityEffect() { Mob *caster = nullptr; diff --git a/zone/mob.h b/zone/mob.h index b9e3bfd85..3d207ee26 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -306,6 +306,7 @@ public: virtual int32 GetActSpellCost(uint16 spell_id, int32 cost){ return cost;} virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration); virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); + virtual int32 GetActReflectedSpellDamage(int32 spell_id, int32 value, int effectiveness); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false, int level_override = -1); @@ -331,9 +332,9 @@ public: bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); void SendBeginCast(uint16 spell_id, uint32 casttime); - virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false, + virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); - virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1); + virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false); virtual bool CheckFizzle(uint16 spell_id); @@ -827,7 +828,6 @@ public: int GetCriticalChanceBonus(uint16 skill); int16 GetSkillDmgAmt(uint16 skill); int16 GetPositionalDmgAmt(Mob* defender); - bool TryReflectSpell(uint32 spell_id); inline bool CanBlockSpell() const { return(spellbonuses.FocusEffects[focusBlockNextSpell]); } bool DoHPToManaCovert(uint16 mana_cost = 0); int32 ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard = false, uint16 caster_id=0); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 4d614731a..1aa87288c 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -2043,7 +2043,7 @@ void Mob::InstillDoubt(Mob *who) { //temporary hack... //cast fear on them... should prolly be a different spell //and should be un-resistable. - SpellOnTarget(229, who, false, true, -2000); + SpellOnTarget(229, who, 0, true, -2000); //is there a success message? } else { MessageString(Chat::LightBlue,NOT_SCARING); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 088d4b2a2..5caab6d0d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -46,7 +46,7 @@ extern WorldServer worldserver; // the spell can still fail here, if the buff can't stack // in this case false will be returned, true otherwise -bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override) +bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness) { int caster_level, buffslot, effect, effect_value, i; EQ::ItemInstance *SummonedItem=nullptr; @@ -259,7 +259,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove //handles AAs and what not... if(caster) { - dmg = caster->GetActSpellDamage(spell_id, dmg, this); + if (reflect_effectiveness) { + dmg = caster->GetActReflectedSpellDamage(spell_id, (int32)(spells[spell_id].base[i] * partial / 100), reflect_effectiveness); + } + else { + dmg = caster->GetActSpellDamage(spell_id, dmg, this); + } caster->ResourceTap(-dmg, spell_id); } diff --git a/zone/spells.cpp b/zone/spells.cpp index 97caa49c7..b7affc35f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2274,14 +2274,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui return(false); } if (isproc) { - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true, level_override); + SpellOnTarget(spell_id, spell_target, 0, true, resist_adjust, true, level_override); } else { if (spells[spell_id].targettype == ST_TargetOptional){ if (!TrySpellProjectile(spell_target, spell_id)) return false; } - else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false, level_override)) { + else if(!SpellOnTarget(spell_id, spell_target, 0, true, resist_adjust, false, level_override)) { if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) { // Prevent mana usage/timers being set for beneficial buffs if(casting_spell_aa_id) @@ -3513,7 +3513,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) // and if you don't want effects just return false. interrupting here will // break stuff // -bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, +bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust, bool isproc, int level_override) { @@ -3641,7 +3641,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(),temp1, 0); } - mod_spell_cast(spell_id, spelltar, reflect, use_resist_adjust, resist_adjust, isproc); + mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); // now check if the spell is allowed to land if (RuleB(Spells, EnableBlockedBuffs)) { @@ -3860,69 +3860,101 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } } } - // Reflect - if(spelltar && spelltar->TryReflectSpell(spell_id) && !reflect && IsDetrimentalSpell(spell_id) && this != spelltar) { - int reflect_chance = 0; + /* + Reflect + base= % Chance to Reflect + Limit= Resist Modifier (+Value for decrease chance to resist) + Max= % of base spell damage (this is the base before any formula or focus is applied) + On live any type of detrimental spell can be reflected as long as the Reflectable spell field is set, this includes AOE. + The 'caster' of the reflected spell is owner of the reflect effect. Caster's focus effects are NOT applied to reflected spell. + + reflect_effectiveness is applied to damage spells, a value of 100 is no change to base damage. Other values change by percent. (50=50% of damage) + we this variable to both check if a spell being applied is from a reflection and for the damage modifier. + + There are a few spells in database that are not detrimental that have Reflectable field set, however from testing, they do not actually reflect. + */ + if(spells[spell_id].reflectable && !reflect_effectiveness && spelltar && this != spelltar && IsDetrimentalSpell(spell_id) && + (spelltar->spellbonuses.reflect[SBIndex::REFLECT_CHANCE] || spelltar->aabonuses.reflect[SBIndex::REFLECT_CHANCE] || spelltar->itembonuses.reflect[SBIndex::REFLECT_CHANCE])) { + bool can_spell_reflect = false; switch(RuleI(Spells, ReflectType)) { - case 0: + case REFLECT_DISABLED: break; - case 1: + case REFLECT_SINGLE_TARGET_SPELLS_ONLY: { if(spells[spell_id].targettype == ST_Target) { for(int y = 0; y < 16; y++) { - if(spells[spell_id].classes[y] < 255) - reflect_chance = 1; + if (spells[spell_id].classes[y] < 255) { + can_spell_reflect = true; + } } } break; } - case 2: + case REFLECT_ALL_PLAYER_SPELLS: { for(int y = 0; y < 16; y++) { - if(spells[spell_id].classes[y] < 255) - reflect_chance = 1; + if (spells[spell_id].classes[y] < 255) { + can_spell_reflect = true; + } } break; } - case 3: + case RELFECT_ALL_SINGLE_TARGET_SPELLS: { - if(spells[spell_id].targettype == ST_Target) - reflect_chance = 1; - + if (spells[spell_id].targettype == ST_Target) { + can_spell_reflect = true; + } break; } - case 4: - reflect_chance = 1; + case REFLECT_ALL_SPELLS: //This is live like behavior + can_spell_reflect = true; default: break; } - if (reflect_chance) { + if (can_spell_reflect) { - if (RuleB(Spells, ReflectMessagesClose)) { - entity_list.MessageCloseString( - this, /* Sender */ - false, /* Skip Sender */ - RuleI(Range, SpellMessages), /* Range */ - Chat::Spells, /* Type */ - SPELL_REFLECT, /* String ID */ - GetCleanName(), /* Message 1 */ - spelltar->GetCleanName() /* Message 2 */ - ); + int reflect_resist_adjust = 0; + int reflect_effectiveness_mod = 0; //Need value of 100 to do baseline unmodified damage. + + if (spelltar->spellbonuses.reflect[SBIndex::REFLECT_CHANCE] && zone->random.Roll(spelltar->spellbonuses.reflect[SBIndex::REFLECT_CHANCE])) { + reflect_resist_adjust = spelltar->spellbonuses.reflect[SBIndex::REFLECT_RESISTANCE_MOD]; + reflect_effectiveness_mod = spelltar->spellbonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] ? spelltar->spellbonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] : 100; } - else { - MessageString(Chat::Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); + else if (spelltar->aabonuses.reflect[SBIndex::REFLECT_CHANCE] && zone->random.Roll(spelltar->aabonuses.reflect[SBIndex::REFLECT_CHANCE])) { + reflect_effectiveness_mod = 100; + reflect_resist_adjust = spelltar->aabonuses.reflect[SBIndex::REFLECT_RESISTANCE_MOD]; + } + else if (spelltar->itembonuses.reflect[SBIndex::REFLECT_CHANCE] && zone->random.Roll(spelltar->itembonuses.reflect[SBIndex::REFLECT_CHANCE])) { + reflect_resist_adjust = spelltar->itembonuses.reflect[SBIndex::REFLECT_RESISTANCE_MOD]; + reflect_effectiveness_mod = spelltar->itembonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] ? spelltar->itembonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] : 100; } - CheckNumHitsRemaining(NumHit::ReflectSpell); - // caster actually appears to change - // ex. During OMM fight you click your reflect mask and you get the recourse from the reflected - // spell - spelltar->SpellOnTarget(spell_id, this, true, use_resist_adjust, resist_adjust); - safe_delete(action_packet); - return false; + if (reflect_effectiveness_mod) { + + if (RuleB(Spells, ReflectMessagesClose)) { + entity_list.MessageCloseString( + this, /* Sender */ + false, /* Skip Sender */ + RuleI(Range, SpellMessages), /* Range */ + Chat::Spells, /* Type */ + SPELL_REFLECT, /* String ID */ + GetCleanName(), /* Message 1 */ + spelltar->GetCleanName() /* Message 2 */ + ); + } + else { + MessageString(Chat::Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); + } + + CheckNumHitsRemaining(NumHit::ReflectSpell); + + spelltar->SpellOnTarget(spell_id, this, reflect_effectiveness_mod, use_resist_adjust, (resist_adjust - reflect_resist_adjust)); + safe_delete(action_packet); + return false; + } } } @@ -4022,7 +4054,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } // cause the effects to the target - if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override)) + if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness)) { // if SpellEffect returned false there's a problem applying the // spell. It's most likely a buff that can't stack. @@ -6071,7 +6103,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) if (d <= spells[spell_id].aoerange) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); maxtarget_count++; } @@ -6145,7 +6177,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) (heading_to_target >= 0.0f && heading_to_target <= angle_end)) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); maxtarget_count++; } } @@ -6153,7 +6185,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) if (heading_to_target >= angle_start && heading_to_target <= angle_end) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); maxtarget_count++; } }