Core/Spells: Implemented SPELL_ATTR5_NOT_ON_PLAYER and SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC
Port From (https://github.com/TrinityCore/TrinityCore/commit/acda6f9dcf411a1406b81e1a100125dbe34f942b)
This commit is contained in:
@@ -1764,8 +1764,8 @@ namespace Framework.Constants
|
|||||||
TriggersChanneling = 0x10, // Triggers Channeling
|
TriggersChanneling = 0x10, // Triggers Channeling
|
||||||
LimitN = 0x20, // Limit N Description Remove Previous Application To Another Unit If Applied
|
LimitN = 0x20, // Limit N Description Remove Previous Application To Another Unit If Applied
|
||||||
IgnoreAreaEffectPvpCheck = 0x40, // Ignore Area Effect Pvp Check
|
IgnoreAreaEffectPvpCheck = 0x40, // Ignore Area Effect Pvp Check
|
||||||
NotOnPlayer = 0x80, /*Nyi*/ // Not On Player
|
NotOnPlayer = 0x80, // Not On Player
|
||||||
NotOnPlayerControlledNpc = 0x100, /*Nyi*/ // Not On Player Controlled Npc
|
NotOnPlayerControlledNpc = 0x100, // Not On Player Controlled Npc
|
||||||
ExtraInitialPeriod = 0x200, // Extra Initial Period Description Immediately Do Periodic Tick On Apply
|
ExtraInitialPeriod = 0x200, // Extra Initial Period Description Immediately Do Periodic Tick On Apply
|
||||||
DoNotDisplayDuration = 0x400, // Do Not Display Duration
|
DoNotDisplayDuration = 0x400, // Do Not Display Duration
|
||||||
ImpliedTargeting = 0x800, // Implied Targeting (Client Only)
|
ImpliedTargeting = 0x800, // Implied Targeting (Client Only)
|
||||||
|
|||||||
@@ -124,11 +124,17 @@ namespace Game.AI
|
|||||||
return SelectTarget(targetType, offset, new DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
|
return SelectTarget(targetType, offset, new DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Unit SelectTarget(SelectTargetMethod targetType, uint offset, ICheck<Unit> selector)
|
||||||
|
{
|
||||||
|
return SelectTarget(targetType, offset, selector.Invoke);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Select the best target (in <targetType> order) satisfying <predicate> from the threat list.
|
/// Select the best target (in <targetType> order) satisfying <predicate> from the threat list.
|
||||||
/// If <offset> is nonzero, the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM) are skipped.
|
/// If <offset> is nonzero, the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM) are skipped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Unit SelectTarget(SelectTargetMethod targetType, uint offset, ICheck<Unit> selector)
|
public delegate bool SelectTargetDelegate(Unit unit);
|
||||||
|
public Unit SelectTarget(SelectTargetMethod targetType, uint offset, SelectTargetDelegate selector)
|
||||||
{
|
{
|
||||||
ThreatManager mgr = GetThreatManager();
|
ThreatManager mgr = GetThreatManager();
|
||||||
// shortcut: if we ignore the first <offset> elements, and there are at most <offset> elements, then we ignore ALL elements
|
// shortcut: if we ignore the first <offset> elements, and there are at most <offset> elements, then we ignore ALL elements
|
||||||
@@ -162,14 +168,14 @@ namespace Game.AI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<Unit> SelectTargetList(uint num, SelectTargetMethod targetType, uint offset = 0, float dist = 0f, bool playerOnly = false, bool withTank = true, int aura = 0)
|
public List<Unit> SelectTargetList(uint num, SelectTargetMethod targetType, uint offset = 0, float dist = 0f, bool playerOnly = false, bool withTank = true, int aura = 0)
|
||||||
{
|
{
|
||||||
return SelectTargetList(num, targetType, offset, new DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
|
return SelectTargetList(num, targetType, offset, new DefaultTargetSelector(me, dist, playerOnly, withTank, aura).Invoke);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Select the best (up to) <num> targets (in <targetType> order) satisfying <predicate> from the threat list and stores them in <targetList> (which is cleared first).
|
/// Select the best (up to) <num> targets (in <targetType> order) satisfying <predicate> from the threat list and stores them in <targetList> (which is cleared first).
|
||||||
/// If <offset> is nonzero, the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM) are skipped.
|
/// If <offset> is nonzero, the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM) are skipped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<Unit> SelectTargetList(uint num, SelectTargetMethod targetType, uint offset, ICheck<Unit> selector)
|
public List<Unit> SelectTargetList(uint num, SelectTargetMethod targetType, uint offset, SelectTargetDelegate selector)
|
||||||
{
|
{
|
||||||
var targetList = new List<Unit>();
|
var targetList = new List<Unit>();
|
||||||
|
|
||||||
@@ -228,7 +234,7 @@ namespace Game.AI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// then finally filter by predicate
|
// then finally filter by predicate
|
||||||
targetList.RemoveAll(unit => !selector.Invoke(unit));
|
targetList.RemoveAll(unit => !selector(unit));
|
||||||
|
|
||||||
if (targetList.Count <= num)
|
if (targetList.Count <= num)
|
||||||
return targetList;
|
return targetList;
|
||||||
@@ -264,8 +270,23 @@ namespace Game.AI
|
|||||||
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
|
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
|
||||||
if (spellInfo != null)
|
if (spellInfo != null)
|
||||||
{
|
{
|
||||||
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer);
|
DefaultTargetSelector targetSelectorInner = new(me, spellInfo.GetMaxRange(false), false, true, 0);
|
||||||
target = SelectTarget(SelectTargetMethod.Random, 0, spellInfo.GetMaxRange(false), playerOnly);
|
bool targetSelector(Unit candidate)
|
||||||
|
{
|
||||||
|
if (!candidate.IsPlayer())
|
||||||
|
{
|
||||||
|
if (spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (spellInfo.HasAttribute(SpellAttr5.NotOnPlayerControlledNpc) && candidate.IsControlledByPlayer())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return targetSelectorInner.Invoke(candidate);
|
||||||
|
};
|
||||||
|
target = SelectTarget(SelectTargetMethod.Random, 0, targetSelector);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -278,12 +299,25 @@ namespace Game.AI
|
|||||||
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
|
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
|
||||||
if (spellInfo != null)
|
if (spellInfo != null)
|
||||||
{
|
{
|
||||||
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer);
|
|
||||||
float range = spellInfo.GetMaxRange(false);
|
float range = spellInfo.GetMaxRange(false);
|
||||||
|
|
||||||
DefaultTargetSelector targetSelector = new(me, range, playerOnly, true, -(int)spellId);
|
DefaultTargetSelector targetSelectorInner = new(me, range, false, true, -(int)spellId);
|
||||||
if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim)
|
bool targetSelector(Unit candidate)
|
||||||
&& targetSelector.Invoke(me.GetVictim()))
|
{
|
||||||
|
if (!candidate.IsPlayer())
|
||||||
|
{
|
||||||
|
if (spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (spellInfo.HasAttribute(SpellAttr5.NotOnPlayerControlledNpc) && candidate.IsControlledByPlayer())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return targetSelectorInner.Invoke(candidate);
|
||||||
|
};
|
||||||
|
if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim) && targetSelector(me.GetVictim()))
|
||||||
target = me.GetVictim();
|
target = me.GetVictim();
|
||||||
else
|
else
|
||||||
target = SelectTarget(SelectTargetMethod.Random, 0, targetSelector);
|
target = SelectTarget(SelectTargetMethod.Random, 0, targetSelector);
|
||||||
|
|||||||
@@ -2173,8 +2173,19 @@ namespace Game.Maps
|
|||||||
if (u.IsTypeId(TypeId.Unit) && u.IsTotem())
|
if (u.IsTypeId(TypeId.Unit) && u.IsTotem())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (_spellInfo != null && _spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer) && !u.IsPlayer())
|
if (_spellInfo != null)
|
||||||
return false;
|
{
|
||||||
|
if (!u.IsPlayer())
|
||||||
|
{
|
||||||
|
if (_spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_spellInfo.HasAttribute(SpellAttr5.NotOnPlayerControlledNpc) && u.IsControlledByPlayer())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (_spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!i_funit.IsValidAttackTarget(u, _spellInfo))
|
if (!i_funit.IsValidAttackTarget(u, _spellInfo))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1594,6 +1594,8 @@ namespace Game.Spells
|
|||||||
retMask &= GridMapTypeMask.Corpse | GridMapTypeMask.Player;
|
retMask &= GridMapTypeMask.Corpse | GridMapTypeMask.Player;
|
||||||
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts))
|
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts))
|
||||||
retMask &= GridMapTypeMask.Player;
|
retMask &= GridMapTypeMask.Player;
|
||||||
|
if (m_spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||||
|
retMask &= ~GridMapTypeMask.Player;
|
||||||
|
|
||||||
if (condList != null)
|
if (condList != null)
|
||||||
retMask &= Global.ConditionMgr.GetSearcherTypeMaskForConditionList(condList);
|
retMask &= Global.ConditionMgr.GetSearcherTypeMaskForConditionList(condList);
|
||||||
|
|||||||
@@ -1080,8 +1080,16 @@ namespace Game.Spells
|
|||||||
return SpellCastResult.SpellCastOk;
|
return SpellCastResult.SpellCastOk;
|
||||||
|
|
||||||
// corpseOwner and unit specific target checks
|
// corpseOwner and unit specific target checks
|
||||||
if (HasAttribute(SpellAttr3.OnlyOnPlayer) && !unitTarget.IsTypeId(TypeId.Player))
|
if (!unitTarget.IsPlayer())
|
||||||
return SpellCastResult.TargetNotPlayer;
|
{
|
||||||
|
if (HasAttribute(SpellAttr3.OnlyOnPlayer))
|
||||||
|
return SpellCastResult.TargetNotPlayer;
|
||||||
|
|
||||||
|
if (HasAttribute(SpellAttr5.NotOnPlayerControlledNpc) && unitTarget.IsControlledByPlayer())
|
||||||
|
return SpellCastResult.TargetIsPlayerControlled;
|
||||||
|
}
|
||||||
|
else if (HasAttribute(SpellAttr5.NotOnPlayer))
|
||||||
|
return SpellCastResult.TargetIsPlayer;
|
||||||
|
|
||||||
if (!IsAllowingDeadTarget() && !unitTarget.IsAlive())
|
if (!IsAllowingDeadTarget() && !unitTarget.IsAlive())
|
||||||
return SpellCastResult.TargetsDead;
|
return SpellCastResult.TargetsDead;
|
||||||
|
|||||||
Reference in New Issue
Block a user