Core/Spells: rework part 2: Split up target selection logic

Port From (https://github.com/TrinityCore/TrinityCore/commit/52ae3c89d1d4dbcff309d940fd41c7212edc77b7)
This commit is contained in:
hondacrx
2021-08-28 14:40:52 -04:00
parent 9123dec4b9
commit 5ff5fb7d3f
6 changed files with 194 additions and 137 deletions
@@ -161,6 +161,7 @@ namespace Framework.Constants
ContributionCollector = 0x400, ContributionCollector = 0x400,
} }
[Flags]
public enum CreatureTypeFlags : uint public enum CreatureTypeFlags : uint
{ {
TameablePet = 0x01, // Makes the mob tameable (must also be a beast and have family set) TameablePet = 0x01, // Makes the mob tameable (must also be a beast and have family set)
@@ -596,9 +596,9 @@ namespace Game.Entities
switch (action.TargetType) switch (action.TargetType)
{ {
case AreaTriggerActionUserTypes.Friend: case AreaTriggerActionUserTypes.Friend:
return caster._IsValidAssistTarget(unit, Global.SpellMgr.GetSpellInfo(action.Param, caster.GetMap().GetDifficultyID())); return caster.IsValidAssistTarget(unit, Global.SpellMgr.GetSpellInfo(action.Param, caster.GetMap().GetDifficultyID()));
case AreaTriggerActionUserTypes.Enemy: case AreaTriggerActionUserTypes.Enemy:
return caster._IsValidAttackTarget(unit, Global.SpellMgr.GetSpellInfo(action.Param, caster.GetMap().GetDifficultyID())); return caster.IsValidAttackTarget(unit, Global.SpellMgr.GetSpellInfo(action.Param, caster.GetMap().GetDifficultyID()));
case AreaTriggerActionUserTypes.Raid: case AreaTriggerActionUserTypes.Raid:
return caster.IsInRaidWith(unit); return caster.IsInRaidWith(unit);
case AreaTriggerActionUserTypes.Party: case AreaTriggerActionUserTypes.Party:
+166 -121
View File
@@ -643,7 +643,7 @@ namespace Game.Entities
Unit magnet = eff.GetBase().GetCaster(); Unit magnet = eff.GetBase().GetCaster();
if (magnet != null) if (magnet != null)
{ {
if (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && _IsValidAttackTarget(magnet, spellInfo)) if (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && IsValidAttackTarget(magnet, spellInfo))
{ {
// @todo handle this charge drop by proc in cast phase on explicit target // @todo handle this charge drop by proc in cast phase on explicit target
if (spellInfo.HasHitDelay()) if (spellInfo.HasHitDelay())
@@ -675,7 +675,7 @@ namespace Game.Entities
{ {
Unit magnet = i.GetCaster(); Unit magnet = i.GetCaster();
if (magnet != null) if (magnet != null)
if (_IsValidAttackTarget(magnet, spellInfo) && magnet.IsWithinLOSInMap(this) if (IsValidAttackTarget(magnet, spellInfo) && magnet.IsWithinLOSInMap(this)
&& (spellInfo == null || (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && (spellInfo == null || (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk
&& spellInfo.CheckTarget(this, magnet, false) == SpellCastResult.SpellCastOk))) && spellInfo.CheckTarget(this, magnet, false) == SpellCastResult.SpellCastOk)))
{ {
@@ -686,11 +686,6 @@ namespace Game.Entities
return victim; return victim;
} }
public bool IsValidAttackTarget(Unit target)
{
return _IsValidAttackTarget(target, null);
}
public void SendAttackStateUpdate(HitInfo HitInfo, Unit target, SpellSchoolMask damageSchoolMask, uint Damage, uint AbsorbDamage, uint Resist, VictimState TargetState, uint BlockedAmount) public void SendAttackStateUpdate(HitInfo HitInfo, Unit target, SpellSchoolMask damageSchoolMask, uint Damage, uint AbsorbDamage, uint Resist, VictimState TargetState, uint BlockedAmount)
{ {
CalcDamageInfo dmgInfo = new(); CalcDamageInfo dmgInfo = new();
@@ -1567,8 +1562,8 @@ namespace Game.Entities
} }
} }
// function based on function Unit.CanAttack from 13850 client // function based on function Unit::CanAttack from 13850 client
public bool _IsValidAttackTarget(Unit target, SpellInfo bySpell, WorldObject obj = null) public bool IsValidAttackTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true)
{ {
Cypher.Assert(target != null); Cypher.Assert(target != null);
@@ -1576,88 +1571,39 @@ namespace Game.Entities
if (this == target) if (this == target)
return false; return false;
// can't attack unattackable units or GMs // can't attack GMs
if (target.HasUnitState(UnitState.Unattackable) if (target.IsPlayer() && target.ToPlayer().IsGameMaster())
|| (target.IsTypeId(TypeId.Player) && target.ToPlayer().IsGameMaster()))
return false; return false;
// visibility checks Unit unit = ToUnit();
// skip visibility check for GO casts, needs removal when go cast is implemented. Also ignore for gameobject and dynauras Unit targetUnit = target.ToUnit();
if (GetEntry() != SharedConst.WorldTrigger && (!obj || !obj.IsTypeMask(TypeMask.GameObject | TypeMask.DynamicObject)))
{
// can't attack invisible
if (bySpell == null || !bySpell.HasAttribute(SpellAttr6.CanTargetInvisible))
{
if (obj && !obj.CanSeeOrDetect(target, bySpell != null && bySpell.IsAffectingArea()))
return false;
else if (!obj)
{
// ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player
bool ignoreStealthCheck = (bySpell != null && bySpell.IsAffectingArea()) ||
(target.GetTypeId() == TypeId.Player && target.HasStealthAura() && IsInCombatWith(target));
if (!CanSeeOrDetect(target, ignoreStealthCheck))
return false;
}
}
}
// can't attack dead
if ((bySpell == null || !bySpell.IsAllowingDeadTarget()) && !target.IsAlive())
return false;
// can't attack untargetable
if ((bySpell == null || !bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable))
&& target.HasUnitFlag(UnitFlags.NotSelectable))
return false;
Player playerAttacker = ToPlayer();
if (playerAttacker != null)
{
if (playerAttacker.HasPlayerFlag(PlayerFlags.Commentator2))
return false;
}
// check flags
if (target.HasUnitFlag(UnitFlags.NonAttackable | UnitFlags.TaxiFlight | UnitFlags.NotAttackable1 | UnitFlags.Unk16)
|| (!HasUnitFlag(UnitFlags.PvpAttackable) && target.IsImmuneToNPC())
|| (!target.HasUnitFlag(UnitFlags.PvpAttackable) && IsImmuneToNPC()))
return false;
if ((bySpell == null || !bySpell.HasAttribute(SpellAttr8.AttackIgnoreImmuneToPCFlag))
&& (HasUnitFlag(UnitFlags.PvpAttackable) && target.IsImmuneToPC())
// check if this is a world trigger cast - GOs are using world triggers to cast their spells, so we need to ignore their immunity flag here, this is a temp workaround, needs removal when go cast is implemented properly
&& GetEntry() != SharedConst.WorldTrigger)
return false;
// CvC case - can attack each other only when one of them is hostile // CvC case - can attack each other only when one of them is hostile
if (!HasUnitFlag(UnitFlags.PvpAttackable) && !target.HasUnitFlag(UnitFlags.PvpAttackable)) if (unit && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && targetUnit && !targetUnit.HasUnitFlag(UnitFlags.PvpAttackable))
return GetReactionTo(target) <= ReputationRank.Hostile || target.GetReactionTo(this) <= ReputationRank.Hostile; return IsHostileTo(target) || target.IsHostileTo(this);
// PvP, PvC, CvP case // PvP, PvC, CvP case
// can't attack friendly targets // can't attack friendly targets
if (GetReactionTo(target) > ReputationRank.Neutral || target.GetReactionTo(this) > ReputationRank.Neutral) if (IsFriendlyTo(target) || target.IsFriendlyTo(this))
return false; return false;
Player playerAffectingAttacker = HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null; Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null;
Player playerAffectingTarget = target.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null; Player playerAffectingTarget = targetUnit && targetUnit.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null;
// Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget)) if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget))
{ {
if (!(target.IsTypeId(TypeId.Player) && IsTypeId(TypeId.Player)) && Player player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
!(target.IsTypeId(TypeId.Unit) && IsTypeId(TypeId.Unit))) Unit creature = playerAffectingAttacker ? targetUnit : unit;
if (creature != null)
{ {
Player player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
Unit creature = playerAffectingAttacker ? target : this;
if (creature.IsContestedGuard() && player.HasPlayerFlag(PlayerFlags.ContestedPVP)) if (creature.IsContestedGuard() && player.HasPlayerFlag(PlayerFlags.ContestedPVP))
return true; return true;
var factionTemplate = creature.GetFactionTemplateEntry(); var factionTemplate = creature.GetFactionTemplateEntry();
if (factionTemplate != null) if (factionTemplate != null)
{ {
if (player.GetReputationMgr().GetForcedRankIfAny(factionTemplate) == ReputationRank.None) if (player.GetReputationMgr().GetForcedRankIfAny(factionTemplate) == 0)
{ {
var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplate.Faction); var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplate.Faction);
if (factionEntry != null) if (factionEntry != null)
@@ -1674,40 +1620,116 @@ namespace Game.Entities
} }
Creature creatureAttacker = ToCreature(); Creature creatureAttacker = ToCreature();
if (creatureAttacker != null && Convert.ToBoolean(creatureAttacker.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.TreatAsRaidUnit)) if (creatureAttacker && creatureAttacker.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit))
return false; return false;
if (bySpell == null)
spellCheck = false;
if (spellCheck && !IsValidSpellAttackTarget(target, bySpell))
return false;
return true;
}
public bool IsValidSpellAttackTarget(WorldObject target, SpellInfo bySpell)
{
Cypher.Assert(target != null);
Cypher.Assert(bySpell != null);
// can't attack unattackable units
Unit unitTarget = target.ToUnit();
if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable))
return false;
Unit unit = ToUnit();
// visibility checks (only units)
if (unit)
{
// can't attack invisible
if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible))
{
if (!unit.CanSeeOrDetect(target, bySpell.IsAffectingArea()))
return false;
/*
else if (!obj)
{
// ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player
bool const ignoreStealthCheck = (bySpell && bySpell.IsAffectingArea()) ||
(target.GetTypeId() == TYPEID_PLAYER && target.HasStealthAura() && IsInCombatWith(target));
if (!CanSeeOrDetect(target, ignoreStealthCheck))
return false;
}
*/
}
}
// can't attack dead
if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive())
return false;
// can't attack untargetable
if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable))
return false;
Player playerAttacker = ToPlayer();
if (playerAttacker != null)
{
if (playerAttacker.HasPlayerFlag(PlayerFlags.Uber))
return false;
}
// check flags
if (unitTarget != null && unitTarget.HasUnitFlag(UnitFlags.NonAttackable | UnitFlags.TaxiFlight | UnitFlags.NotAttackable1 | UnitFlags.Unk16))
return false;
if (unit != null && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToNPC())
return false;
if (unitTarget != null && !unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToNPC())
return false;
if (!bySpell.HasAttribute(SpellAttr8.AttackIgnoreImmuneToPCFlag))
{
if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToPC())
return false;
if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToPC())
return false;
}
// check duel - before sanctuary checks // check duel - before sanctuary checks
if (playerAffectingAttacker != null && playerAffectingTarget != null) Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null;
Player playerAffectingTarget = unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null;
if (playerAffectingAttacker && playerAffectingTarget)
if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.opponent == playerAffectingTarget && playerAffectingAttacker.duel.startTime != 0) if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.opponent == playerAffectingTarget && playerAffectingAttacker.duel.startTime != 0)
return true; return true;
// PvP case - can't attack when attacker or target are in sanctuary // PvP case - can't attack when attacker or target are in sanctuary
// however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
if (target.HasUnitFlag(UnitFlags.PvpAttackable) && HasUnitFlag(UnitFlags.PvpAttackable) && (target.IsInSanctuary() || IsInSanctuary())) if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && (unitTarget.IsInSanctuary() || unit.IsInSanctuary()))
return false; return false;
// additional checks - only PvP case // additional checks - only PvP case
if (playerAffectingAttacker != null && playerAffectingTarget != null) if (playerAffectingAttacker && playerAffectingTarget)
{ {
if (target.IsPvP()) if (unitTarget.IsPvP())
return true; return true;
if (IsFFAPvP() && target.IsFFAPvP()) if (unit.IsFFAPvP() && unitTarget.IsFFAPvP())
return true; return true;
return HasPvpFlag(UnitPVPStateFlags.Unk1) || target.HasPvpFlag(UnitPVPStateFlags.Unk1); return unit.HasPvpFlag(UnitPVPStateFlags.Unk1) ||
unitTarget.HasPvpFlag(UnitPVPStateFlags.Unk1);
} }
return true; return true;
} }
public bool IsValidAssistTarget(Unit target) // function based on function Unit::CanAssist from 13850 client
{ public bool IsValidAssistTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true)
return _IsValidAssistTarget(target, null);
}
// function based on function Unit.CanAssist from 13850 client
public bool _IsValidAssistTarget(Unit target, SpellInfo bySpell)
{ {
Cypher.Assert(target != null); Cypher.Assert(target != null);
@@ -1715,85 +1737,108 @@ namespace Game.Entities
if (this == target) if (this == target)
return true; return true;
// can't assist unattackable units or GMs // can't assist GMs
if (target.HasUnitState(UnitState.Unattackable) if (target.IsPlayer() && target.ToPlayer().IsGameMaster())
|| (target.IsTypeId(TypeId.Player) && target.ToPlayer().IsGameMaster())) return false;
// can't assist non-friendly targets
if (GetReactionTo(target) < ReputationRank.Neutral && target.GetReactionTo(this) < ReputationRank.Neutral && (!ToCreature() || !ToCreature().GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit)))
return false;
if (bySpell == null)
spellCheck = false;
if (spellCheck && !IsValidSpellAssistTarget(target, bySpell))
return false;
return true;
}
public bool IsValidSpellAssistTarget(WorldObject target, SpellInfo bySpell)
{
Cypher.Assert(target != null);
Cypher.Assert(bySpell != null);
// can't assist unattackable units
Unit unitTarget = target.ToUnit();
if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable))
return false; return false;
// can't assist own vehicle or passenger // can't assist own vehicle or passenger
if (m_vehicle != null) Unit unit = ToUnit();
if (IsOnVehicle(target) || m_vehicle.GetBase().IsOnVehicle(target)) if (unit && unitTarget && unit.GetVehicle())
{
if (unit.IsOnVehicle(unitTarget))
return false; return false;
if (unit.GetVehicleBase().IsOnVehicle(unitTarget))
return false;
}
// can't assist invisible // can't assist invisible
if ((bySpell == null || !bySpell.HasAttribute(SpellAttr6.CanTargetInvisible)) && !CanSeeOrDetect(target, bySpell != null && bySpell.IsAffectingArea())) if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible) && !CanSeeOrDetect(target, bySpell.IsAffectingArea()))
return false; return false;
// can't assist dead // can't assist dead
if ((bySpell == null || !bySpell.IsAllowingDeadTarget()) && !target.IsAlive()) if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive())
return false; return false;
// can't assist untargetable // can't assist untargetable
if ((bySpell == null || !bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable)) if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable))
&& target.HasUnitFlag(UnitFlags.NotSelectable))
return false; return false;
if (bySpell == null || !bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag)) if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag))
{ {
if (HasUnitFlag(UnitFlags.PvpAttackable)) if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable))
{ {
if (target.IsImmuneToPC()) if (unitTarget && unitTarget.IsImmuneToPC())
return false; return false;
} }
else else
{ {
if (target.IsImmuneToNPC()) if (unitTarget && unitTarget.IsImmuneToNPC())
return false; return false;
} }
} }
// can't assist non-friendly targets
if (GetReactionTo(target) <= ReputationRank.Neutral
&& target.GetReactionTo(this) <= ReputationRank.Neutral
&& (!IsTypeId(TypeId.Unit) || !Convert.ToBoolean(ToCreature().GetCreatureTemplate().TypeFlags & CreatureTypeFlags.TreatAsRaidUnit)))
return false;
// Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction)
if (IsTypeId(TypeId.Player) && IsCharmed() && GetCharmerGUID().IsCreature())
return true;
// PvP case // PvP case
else if (target.HasUnitFlag(UnitFlags.PvpAttackable)) if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable))
{ {
Player targetPlayerOwner = target.GetAffectingPlayer(); Player targetPlayerOwner = target.GetAffectingPlayer();
if (HasUnitFlag(UnitFlags.PvpAttackable)) if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable))
{ {
Player selfPlayerOwner = GetAffectingPlayer(); Player selfPlayerOwner = GetAffectingPlayer();
if (selfPlayerOwner != null && targetPlayerOwner != null) if (selfPlayerOwner && targetPlayerOwner)
{ {
// can't assist player which is dueling someone // can't assist player which is dueling someone
if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner.duel != null) if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner.duel != null)
return false; return false;
} }
// can't assist player in ffa_pvp zone from outside // can't assist player in ffa_pvp zone from outside
if (target.HasPvpFlag(UnitPVPStateFlags.FFAPvp) && !HasPvpFlag(UnitPVPStateFlags.FFAPvp)) if (unitTarget.IsFFAPvP() && unit && !unit.IsFFAPvP())
return false; return false;
// can't assist player out of sanctuary from sanctuary if has pvp enabled // can't assist player out of sanctuary from sanctuary if has pvp enabled
if (target.HasPvpFlag(UnitPVPStateFlags.PvP)) if (unitTarget.IsPvP())
if (IsInSanctuary() && !target.IsInSanctuary()) if (unit && unit.IsInSanctuary() && !unitTarget.IsInSanctuary())
return false; return false;
} }
} }
// PvC case - player can assist creature only if has specific type flags // PvC case - player can assist creature only if has specific type flags
else if (HasUnitFlag(UnitFlags.PvpAttackable) // !target.HasFlag(UNIT_FIELD_FLAGS, UnitFlags.PvpAttackable) &&
&& (bySpell == null || !bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag)) else if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable))
&& !target.HasPvpFlag(UnitPVPStateFlags.PvP))
{ {
Creature creatureTarget = target.ToCreature(); if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag))
if (creatureTarget != null) {
return Convert.ToBoolean(creatureTarget.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.TreatAsRaidUnit) if (unitTarget && !unitTarget.IsPvP())
|| Convert.ToBoolean(creatureTarget.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.CanAssist); {
Creature creatureTarget = target.ToCreature();
if (creatureTarget != null)
return creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit) || creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist);
}
}
} }
return true; return true;
} }
+2 -2
View File
@@ -1916,7 +1916,7 @@ namespace Game.Maps
if (!u.IsTargetableForAttack(false)) if (!u.IsTargetableForAttack(false))
return false; return false;
if (!i_obj.IsWithinDistInMap(u, i_range) || i_funit._IsValidAttackTarget(u, null, i_obj)) if (!i_obj.IsWithinDistInMap(u, i_range) || i_funit.IsValidAttackTarget(u))
return false; return false;
i_range = i_obj.GetDistance(u); i_range = i_obj.GetDistance(u);
@@ -2088,7 +2088,7 @@ namespace Game.Maps
if (_spellInfo != null && _spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers) && !u.IsPlayer()) if (_spellInfo != null && _spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers) && !u.IsPlayer())
return false; return false;
if (!i_funit._IsValidAttackTarget(u, _spellInfo, i_obj.GetTypeId() == TypeId.DynamicObject ? i_obj : null)) if (!i_funit.IsValidAttackTarget(u, _spellInfo))
return false; return false;
float searchRadius = i_range; float searchRadius = i_range;
+21 -10
View File
@@ -2201,15 +2201,12 @@ namespace Game.Spells
if (m_spellInfo.HasHitDelay() && unit.HasUnitFlag(UnitFlags.NonAttackable) && unit.GetCharmerOrOwnerGUID() != m_caster.GetGUID()) if (m_spellInfo.HasHitDelay() && unit.HasUnitFlag(UnitFlags.NonAttackable) && unit.GetCharmerOrOwnerGUID() != m_caster.GetGUID())
return SpellMissInfo.Evade; return SpellMissInfo.Evade;
if (m_caster._IsValidAttackTarget(unit, m_spellInfo)) if (m_caster.IsValidAttackTarget(unit, m_spellInfo))
unit.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.HostileActionReceived); unit.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.HostileActionReceived);
else if (m_caster.IsFriendlyTo(unit)) else if (m_caster.IsFriendlyTo(unit))
{ {
// for delayed spells ignore negative spells (after duel end) for friendly targets // for delayed spells ignore negative spells (after duel end) for friendly targets
// @todo this cause soul transfer bugged if (m_spellInfo.HasHitDelay() && unit.IsPlayer() && !IsPositive() && !m_caster.IsValidSpellAttackTarget(unit, m_spellInfo))
// 63881 - Malady of the Mind jump spell (Yogg-Saron)
// 45034 - Curse of Boundless Agony jump spell (Kalecgos)
if (m_spellInfo.HasHitDelay() && unit.IsPlayer() && !IsPositive() && m_spellInfo.Id != 63881 && m_spellInfo.Id != 45034)
return SpellMissInfo.Evade; return SpellMissInfo.Evade;
// assisting case, healing and resurrection // assisting case, healing and resurrection
@@ -7936,7 +7933,7 @@ namespace Game.Spells
Corpse corpseTarget = obj.ToCorpse(); Corpse corpseTarget = obj.ToCorpse();
if (corpseTarget != null) if (corpseTarget != null)
{ {
// use ofter for party/assistance checks // use owner for party/assistance checks
Player owner = Global.ObjAccessor.FindPlayer(corpseTarget.GetOwnerGUID()); Player owner = Global.ObjAccessor.FindPlayer(corpseTarget.GetOwnerGUID());
if (owner != null) if (owner != null)
unitTarget = owner; unitTarget = owner;
@@ -7946,24 +7943,25 @@ namespace Game.Spells
if (unitTarget != null) if (unitTarget != null)
{ {
// do only faction checks here
switch (_targetSelectionType) switch (_targetSelectionType)
{ {
case SpellTargetCheckTypes.Enemy: case SpellTargetCheckTypes.Enemy:
if (unitTarget.IsTotem()) if (unitTarget.IsTotem())
return false; return false;
if (!_caster._IsValidAttackTarget(unitTarget, _spellInfo)) if (!_caster.IsValidAttackTarget(unitTarget, _spellInfo, null, false))
return false; return false;
break; break;
case SpellTargetCheckTypes.Ally: case SpellTargetCheckTypes.Ally:
if (unitTarget.IsTotem()) if (unitTarget.IsTotem())
return false; return false;
if (!_caster._IsValidAssistTarget(unitTarget, _spellInfo)) if (!_caster.IsValidAssistTarget(unitTarget, _spellInfo))
return false; return false;
break; break;
case SpellTargetCheckTypes.Party: case SpellTargetCheckTypes.Party:
if (unitTarget.IsTotem()) if (unitTarget.IsTotem())
return false; return false;
if (!_caster._IsValidAssistTarget(unitTarget, _spellInfo)) if (!_caster.IsValidAssistTarget(unitTarget, _spellInfo))
return false; return false;
if (!_referer.IsInPartyWith(unitTarget)) if (!_referer.IsInPartyWith(unitTarget))
return false; return false;
@@ -7974,7 +7972,7 @@ namespace Game.Spells
return false; return false;
if (unitTarget.IsTotem()) if (unitTarget.IsTotem())
return false; return false;
if (!_caster._IsValidAssistTarget(unitTarget, _spellInfo)) if (!_caster.IsValidAssistTarget(unitTarget, _spellInfo))
return false; return false;
if (!_referer.IsInRaidWith(unitTarget)) if (!_referer.IsInRaidWith(unitTarget))
return false; return false;
@@ -8010,6 +8008,19 @@ namespace Game.Spells
default: default:
break; break;
} }
// then check actual spell positivity to determine if the target is valid
// (negative spells may be targeted on allies)
if (_spellInfo.IsPositive())
{
if (!_caster.IsValidSpellAssistTarget(unitTarget, _spellInfo))
return false;
}
else
{
if (!_caster.IsValidSpellAttackTarget(unitTarget, _spellInfo))
return false;
}
} }
if (_condSrcInfo == null) if (_condSrcInfo == null)
return true; return true;
+2 -2
View File
@@ -1186,12 +1186,12 @@ namespace Game.Spells
if (Convert.ToBoolean(neededTargets & (SpellCastTargetFlags.UnitEnemy | SpellCastTargetFlags.UnitAlly | SpellCastTargetFlags.UnitRaid | SpellCastTargetFlags.UnitParty | SpellCastTargetFlags.UnitMinipet | SpellCastTargetFlags.UnitPassenger))) if (Convert.ToBoolean(neededTargets & (SpellCastTargetFlags.UnitEnemy | SpellCastTargetFlags.UnitAlly | SpellCastTargetFlags.UnitRaid | SpellCastTargetFlags.UnitParty | SpellCastTargetFlags.UnitMinipet | SpellCastTargetFlags.UnitPassenger)))
{ {
if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitEnemy)) if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitEnemy))
if (caster._IsValidAttackTarget(unitTarget, this)) if (caster.IsValidAttackTarget(unitTarget, this))
return SpellCastResult.SpellCastOk; return SpellCastResult.SpellCastOk;
if (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitAlly) if (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitAlly)
|| (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitParty) && caster.IsInPartyWith(unitTarget)) || (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitParty) && caster.IsInPartyWith(unitTarget))
|| (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitRaid) && caster.IsInRaidWith(unitTarget))) || (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitRaid) && caster.IsInRaidWith(unitTarget)))
if (caster._IsValidAssistTarget(unitTarget, this)) if (caster.IsValidAssistTarget(unitTarget, this))
return SpellCastResult.SpellCastOk; return SpellCastResult.SpellCastOk;
if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitMinipet)) if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitMinipet))
if (unitTarget.GetGUID() == caster.GetCritterGUID()) if (unitTarget.GetGUID() == caster.GetCritterGUID())