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
|
||||
LimitN = 0x20, // Limit N Description Remove Previous Application To Another Unit If Applied
|
||||
IgnoreAreaEffectPvpCheck = 0x40, // Ignore Area Effect Pvp Check
|
||||
NotOnPlayer = 0x80, /*Nyi*/ // Not On Player
|
||||
NotOnPlayerControlledNpc = 0x100, /*Nyi*/ // Not On Player Controlled Npc
|
||||
NotOnPlayer = 0x80, // Not On Player
|
||||
NotOnPlayerControlledNpc = 0x100, // Not On Player Controlled Npc
|
||||
ExtraInitialPeriod = 0x200, // Extra Initial Period Description Immediately Do Periodic Tick On Apply
|
||||
DoNotDisplayDuration = 0x400, // Do Not Display Duration
|
||||
ImpliedTargeting = 0x800, // Implied Targeting (Client Only)
|
||||
|
||||
@@ -124,11 +124,17 @@ namespace Game.AI
|
||||
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>
|
||||
/// 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.
|
||||
/// </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();
|
||||
// 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>
|
||||
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>
|
||||
/// 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.
|
||||
/// </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>();
|
||||
|
||||
@@ -228,7 +234,7 @@ namespace Game.AI
|
||||
}
|
||||
|
||||
// then finally filter by predicate
|
||||
targetList.RemoveAll(unit => !selector.Invoke(unit));
|
||||
targetList.RemoveAll(unit => !selector(unit));
|
||||
|
||||
if (targetList.Count <= num)
|
||||
return targetList;
|
||||
@@ -264,8 +270,23 @@ namespace Game.AI
|
||||
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
|
||||
if (spellInfo != null)
|
||||
{
|
||||
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer);
|
||||
target = SelectTarget(SelectTargetMethod.Random, 0, spellInfo.GetMaxRange(false), playerOnly);
|
||||
DefaultTargetSelector targetSelectorInner = new(me, spellInfo.GetMaxRange(false), false, true, 0);
|
||||
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;
|
||||
}
|
||||
@@ -278,12 +299,25 @@ namespace Game.AI
|
||||
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
|
||||
if (spellInfo != null)
|
||||
{
|
||||
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer);
|
||||
float range = spellInfo.GetMaxRange(false);
|
||||
|
||||
DefaultTargetSelector targetSelector = new(me, range, playerOnly, true, -(int)spellId);
|
||||
if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim)
|
||||
&& targetSelector.Invoke(me.GetVictim()))
|
||||
DefaultTargetSelector targetSelectorInner = new(me, range, false, true, -(int)spellId);
|
||||
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);
|
||||
};
|
||||
if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim) && targetSelector(me.GetVictim()))
|
||||
target = me.GetVictim();
|
||||
else
|
||||
target = SelectTarget(SelectTargetMethod.Random, 0, targetSelector);
|
||||
|
||||
@@ -2173,8 +2173,19 @@ namespace Game.Maps
|
||||
if (u.IsTypeId(TypeId.Unit) && u.IsTotem())
|
||||
return false;
|
||||
|
||||
if (_spellInfo != null && _spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer) && !u.IsPlayer())
|
||||
return false;
|
||||
if (_spellInfo != null)
|
||||
{
|
||||
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))
|
||||
return false;
|
||||
|
||||
@@ -1594,6 +1594,8 @@ namespace Game.Spells
|
||||
retMask &= GridMapTypeMask.Corpse | GridMapTypeMask.Player;
|
||||
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts))
|
||||
retMask &= GridMapTypeMask.Player;
|
||||
if (m_spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||
retMask &= ~GridMapTypeMask.Player;
|
||||
|
||||
if (condList != null)
|
||||
retMask &= Global.ConditionMgr.GetSearcherTypeMaskForConditionList(condList);
|
||||
|
||||
@@ -1080,8 +1080,16 @@ namespace Game.Spells
|
||||
return SpellCastResult.SpellCastOk;
|
||||
|
||||
// corpseOwner and unit specific target checks
|
||||
if (HasAttribute(SpellAttr3.OnlyOnPlayer) && !unitTarget.IsTypeId(TypeId.Player))
|
||||
return SpellCastResult.TargetNotPlayer;
|
||||
if (!unitTarget.IsPlayer())
|
||||
{
|
||||
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())
|
||||
return SpellCastResult.TargetsDead;
|
||||
|
||||
Reference in New Issue
Block a user