diff --git a/common/ruletypes.h b/common/ruletypes.h index 400c5f30f..8d4a53daa 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -414,6 +414,7 @@ RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.") RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.") RULE_BOOL(Spells, FixBeaconHeading, false, "Beacon spells use casters heading to fix live bug. False: Live like heading always 0.") +RULE_BOOL(Spells, UseSpellImpliedTargeting, false, "Replicates EQ2-style targeting behavior for spells. Spells will 'pass through' inappropriate targets to target's target if it is appropriate.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/spells.cpp b/zone/spells.cpp index 08c77b737..96d2b74ce 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -179,6 +179,24 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, return false; } + //Goal of Spells:UseSpellImpliedTargeting is to replicate the EQ2 feature where spells will 'pass through' invalid targets to target's target to try to find a valid target. + if (RuleB(Spells,UseSpellImpliedTargeting) && IsClient()) { + Mob* spell_target = entity_list.GetMobID(target_id); + if (spell_target && spell_target->GetTarget()) { + // If either this is beneficial and the target is not a player or player's pet or vis versa + if ((IsBeneficialSpell(spell_id) && (!(spell_target->IsClient() || (spell_target->HasOwner() && spell_target->GetOwner()->IsClient())))) + || (IsDetrimentalSpell(spell_id) && (spell_target->IsClient() || (spell_target->HasOwner() && spell_target->GetOwner()->IsClient())))) { + //Check if the target's target is a valid target; we can use DoCastingChecksOnTarget() here because we can let it handle the failure as vanilla would + if (DoCastingChecksOnTarget(true, spell_id, spell_target->GetTarget())) { + target_id = spell_target->GetTarget()->GetID(); + } else { + //Just return false here because we are going to fail the next check block anyway if we reach this point. + return false; + } + } + } + } + if (!DoCastingChecksOnCaster(spell_id, slot) || !DoCastingChecksZoneRestrictions(true, spell_id) || !DoCastingChecksOnTarget(true, spell_id, entity_list.GetMobID(target_id))) {