Example #1
0
    public CreatureStatus(Xml.Status xmlstatus, Creature target, Xml.Castable castable = null, Creature source = null, int duration = -1, int tickFrequency = -1, double intensity = 1.0)
    {
        Target    = target;
        XmlStatus = xmlstatus;
        Start     = DateTime.Now;
        Target    = target;
        Source    = source;
        Duration  = duration == -1 ? xmlstatus.Duration : duration;
        Tick      = tickFrequency == -1 ? xmlstatus.Tick : tickFrequency;
        Intensity = intensity;

        // Calculate damage/heal effects. Note that a castable MUST be passed here for a status
        // to have damage effects as the castable itself has fields we need to access
        // (intensity, etc) in order to do damage calculations.

        if (castable != null)
        {
            var start  = CalculateNumericEffects(castable, xmlstatus.Effects.OnApply, source);
            var tick   = CalculateNumericEffects(castable, xmlstatus.Effects.OnTick, source);
            var end    = CalculateNumericEffects(castable, xmlstatus.Effects.OnRemove, source);
            var expire = CalculateNumericEffects(castable, xmlstatus.Effects.OnExpire, source);
            OnStartEffect  = new SimpleStatusEffect(start.Heal, start.Damage);
            OnTickEffect   = new SimpleStatusEffect(tick.Heal, tick.Damage);
            OnRemoveEffect = new SimpleStatusEffect(end.Heal, end.Damage);
            OnExpireEffect = new SimpleStatusEffect(expire.Heal, expire.Damage);
            BonusModifiers = NumberCruncher.CalculateStatusModifiers(castable, intensity, xmlstatus.Effects.OnApply.StatModifiers, source, target);
        }
    }
Example #2
0
    private (double Heal, DamageOutput Damage) CalculateNumericEffects(Xml.Castable castable, Xml.ModifierEffect effect, Creature source)
    {
        double heal = 0;
        var    dmg  = new DamageOutput();

        if (effect == null)
        {
            return(heal, dmg);
        }
        if (effect.Heal != null)
        {
            heal = NumberCruncher.CalculateHeal(castable, effect, Target, source, Name);
        }
        if (effect.Damage != null)
        {
            dmg = NumberCruncher.CalculateDamage(castable, effect, Target, source, Name);
        }
        return(heal, dmg);
    }
Example #3
0
        public virtual bool UseCastable(Xml.Castable castObject, Creature target = null, Xml.SpawnCastable spawnCastable = null)
        {
            if (!Condition.CastingAllowed)
            {
                return(false);
            }

            if (this is User)
            {
                GameLog.UserActivityInfo($"UseCastable: {Name} begin casting {castObject.Name} on target: {target?.Name ?? "no target"} CastingAllowed: {Condition.CastingAllowed}");
            }

            var             damage  = castObject.Effects.Damage;
            List <Creature> targets = new List <Creature>();

            if (this is Monster)
            {
                if (spawnCastable != null)
                {
                    damage = new Xml.CastableDamage
                    {
                        Simple = new Xml.SimpleQuantity
                        {
                            Min = (uint)spawnCastable.MinDmg,
                            Max = (uint)spawnCastable.MaxDmg
                        }
                    };

                    castObject.Effects.Damage = damage;                //set damage based on spawncastable settings.
                    castObject.Element        = spawnCastable.Element; //handle defined element without redoing a ton of code.
                }
            }

            targets = GetTargets(castObject, target);

            // Quick checks
            // If no targets and is not an assail, do nothing
            if (targets.Count() == 0 && castObject.IsAssail == false && string.IsNullOrEmpty(castObject.Script))
            {
                GameLog.UserActivityInfo($"UseCastable: {Name}: no targets and not assail");
                return(false);
            }
            // Is this a pvpable spell? If so, is pvp enabled?

            // We do these next steps to ensure effects are displayed uniformly and as fast as possible
            var deadMobs = new List <Creature>();

            if (castObject.Effects?.Animations?.OnCast != null)
            {
                foreach (var tar in targets)
                {
                    foreach (var user in tar.viewportUsers.ToList())
                    {
                        GameLog.UserActivityInfo($"UseCastable: Sending {user.Name} effect for {Name}: {castObject.Effects.Animations.OnCast.Target.Id}");
                        user.SendEffect(tar.Id, castObject.Effects.Animations.OnCast.Target.Id, castObject.Effects.Animations.OnCast.Target.Speed);
                    }
                }
                if (castObject.Effects?.Animations?.OnCast?.SpellEffect != null)
                {
                    GameLog.UserActivityInfo($"UseCastable: Sending spelleffect for {Name}: {castObject.Effects.Animations.OnCast.SpellEffect.Id}");
                    Effect(castObject.Effects.Animations.OnCast.SpellEffect.Id, castObject.Effects.Animations.OnCast.SpellEffect.Speed);
                }
            }

            if (castObject.Effects?.Sound != null)
            {
                PlaySound(castObject.Effects.Sound.Id);
            }

            GameLog.UserActivityInfo($"UseCastable: {Name} casting {castObject.Name}, {targets.Count()} targets");

            if (!string.IsNullOrEmpty(castObject.Script))
            {
                // If a script is defined we fire it immediately, and let it handle targeting / etc
                if (Game.World.ScriptProcessor.TryGetScript(castObject.Script, out Script script))
                {
                    return(script.ExecuteFunction("OnUse", this));
                }
                else
                {
                    GameLog.UserActivityError($"UseCastable: {Name} casting {castObject.Name}: castable script {castObject.Script} missing");
                    return(false);
                }
            }
            if (targets.Count == 0)
            {
                GameLog.UserActivityError("{Name}: {castObject.Name}: hey fam no targets");
            }

            foreach (var tar in targets)
            {
                if (castObject.Effects?.ScriptOverride == true)
                {
                    // TODO: handle castables with scripting
                    // DoStuff();
                    continue;
                }
                if (!castObject.Effects.Damage.IsEmpty)
                {
                    Xml.Element attackElement;
                    var         damageOutput = NumberCruncher.CalculateDamage(castObject, tar, this);
                    if (castObject.Element == Xml.Element.Random)
                    {
                        Random rnd      = new Random();
                        var    Elements = Enum.GetValues(typeof(Xml.Element));
                        attackElement = (Xml.Element)Elements.GetValue(rnd.Next(Elements.Length));
                    }
                    else if (castObject.Element != Xml.Element.None)
                    {
                        attackElement = castObject.Element;
                    }
                    else
                    {
                        attackElement = (Stats.OffensiveElementOverride != Xml.Element.None ? Stats.OffensiveElementOverride : Stats.BaseOffensiveElement);
                    }
                    GameLog.UserActivityInfo($"UseCastable: {Name} casting {castObject.Name} - target: {tar.Name} damage: {damageOutput}, element {attackElement}");

                    tar.Damage(damageOutput.Amount, attackElement, damageOutput.Type, damageOutput.Flags, this, false);

                    if (this is Monster)
                    {
                        if (tar is User)
                        {
                            (tar as User).SendSystemMessage($"{this.Name} attacks you with {castObject.Name}.");
                        }
                    }

                    if (this is User)
                    {
                        if (Equipment.Weapon != null && !Equipment.Weapon.Undamageable)
                        {
                            Equipment.Weapon.Durability -= 1 / (Equipment.Weapon.MaximumDurability * ((100 - Stats.Ac) == 0 ? 1 : (100 - Stats.Ac)));
                        }
                    }

                    if (tar.Stats.Hp <= 0)
                    {
                        deadMobs.Add(tar);
                    }
                }
                // Note that we ignore castables with both damage and healing effects present - one or the other.
                // A future improvement might be to allow more complex effects.
                else if (!castObject.Effects.Heal.IsEmpty)
                {
                    var healOutput = NumberCruncher.CalculateHeal(castObject, tar, this);
                    tar.Heal(healOutput, this);
                    if (this is User)
                    {
                        GameLog.UserActivityInfo($"UseCastable: {Name} casting {castObject.Name} - target: {tar.Name} healing: {healOutput}");
                        if (Equipment.Weapon != null && !Equipment.Weapon.Undamageable)
                        {
                            Equipment.Weapon.Durability -= 1 / (Equipment.Weapon.MaximumDurability * ((100 - Stats.Ac) == 0 ? 1 : (100 - Stats.Ac)));
                        }
                    }
                }

                // Handle statuses

                foreach (var status in castObject.Effects.Statuses.Add.Where(e => e.Value != null))
                {
                    Xml.Status applyStatus;
                    if (World.WorldData.TryGetValue <Xml.Status>(status.Value.ToLower(), out applyStatus))
                    {
                        var duration = status.Duration == 0 ? applyStatus.Duration : status.Duration;
                        GameLog.UserActivityInfo($"UseCastable: {Name} casting {castObject.Name} - applying status {status.Value} - duration {duration}");
                        tar.ApplyStatus(new CreatureStatus(applyStatus, tar, castObject, this, duration));
                    }
                    else
                    {
                        GameLog.UserActivityError($"UseCastable: {Name} casting {castObject.Name} - failed to add status {status.Value}, does not exist!");
                    }
                }

                foreach (var status in castObject.Effects.Statuses.Remove)
                {
                    Xml.Status applyStatus;
                    if (World.WorldData.TryGetValue <Xml.Status>(status.ToLower(), out applyStatus))
                    {
                        GameLog.UserActivityError($"UseCastable: {Name} casting {castObject.Name} - removing status {status}");
                        tar.RemoveStatus(applyStatus.Icon);
                    }
                    else
                    {
                        GameLog.UserActivityError($"UseCastable: {Name} casting {castObject.Name} - failed to remove status {status}, does not exist!");
                    }
                }
            }

            // Now flood away
            foreach (var dead in deadMobs)
            {
                World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.HandleDeath, dead));
            }
            Condition.Casting = false;
            return(true);
        }
Example #4
0
        public virtual List <Creature> GetTargets(Xml.Castable castable, Creature target = null)
        {
            IEnumerable <Creature> actualTargets = new List <Creature>();
            var intents = castable.Intents;
            List <VisibleObject> possibleTargets = new List <VisibleObject>();
            Creature             origin;

            foreach (var intent in intents)
            {
                if (intent.IsShapeless)
                {
                    //GameLog.UserActivityInfo("GetTarget: Shapeless");
                    // No shapes specified.
                    // If UseType=Target, exact clicked target.
                    // If UseType=NoTarget Target=Self, caster.
                    // If UseType=NoTarget Target=Group, *entire* group regardless of location on map.
                    // Otherwise, no target.
                    if (intent.UseType == Xml.SpellUseType.Target)
                    {
                        // Exact clicked target
                        possibleTargets.Add(target);
                        //GameLog.UserActivityInfo("GetTarget: exact clicked target");
                    }
                    else if (intent.UseType == Xml.SpellUseType.NoTarget)
                    {
                        possibleTargets.Add(this);
                        //GameLog.UserActivityInfo("GetTarget: notarget, self");
                        if (intent.Flags.Contains(Xml.IntentFlags.Group))
                        {
                            // Add group members
                            if (this is User uo)
                            {
                                if (uo.Group != null)
                                {
                                    possibleTargets.AddRange(uo.Group.Members.Where(x => x.Connected));
                                }
                            }
                        }
                    }
                    else if (intent.UseType != Xml.SpellUseType.NoTarget)
                    {
                        GameLog.UserActivityWarning($"Unhandled intent type {intent.UseType}, ignoring");
                    }
                }

                if (intent.Map != null)
                {
                    // add entire map
                    //GameLog.UserActivityInfo("GetTarget: adding map targets");
                    possibleTargets.AddRange(Map.EntityTree.GetAllObjects().Where(e => e is Creature));
                }

                if (intent.UseType == Xml.SpellUseType.NoTarget)
                {
                    origin = this;
                    //GameLog.UserActivityInfo($"GetTarget: origin is {this.Name} at {this.X}, {this.Y}");
                }
                else
                {
                    //GameLog.UserActivityInfo($"GetTarget: origin is {target.Name} at {target.X}, {target.Y}");
                    origin = target;
                }

                // Handle shapes
                foreach (var cross in intent.Cross)
                {
                    // Process cross targets
                    foreach (Xml.Direction direction in Enum.GetValues(typeof(Xml.Direction)))
                    {
                        //GameLog.UserActivityInfo($"GetTarget: cross, {direction}, origin {origin.Name}, radius {cross.Radius}");
                        possibleTargets.AddRange(origin.GetDirectionalTargets(direction, cross.Radius));
                    }
                    // Add origin and let flags sort it out
                    possibleTargets.Add(origin);
                }
                foreach (var line in intent.Line)
                {
                    // Process line targets
                    //GameLog.UserActivityInfo($"GetTarget: line, {line.Direction}, origin {origin.Name}, length {line.Length}");
                    possibleTargets.AddRange(origin.GetDirectionalTargets(origin.GetIntentDirection(line.Direction), line.Length));
                    // Similar to above, add origin
                    possibleTargets.Add(origin);
                }
                foreach (var square in intent.Square)
                {
                    // Process square targets
                    var r    = (square.Side - 1) / 2;
                    var rect = new Rectangle(origin.X - r, origin.Y - r, square.Side, square.Side);
                    //GameLog.UserActivityInfo($"GetTarget: square, {origin.X - r}, {origin.Y - r} - origin {origin.Name}, side length {square.Side}");
                    possibleTargets.AddRange(origin.Map.EntityTree.GetObjects(rect).Where(e => e is Creature));
                }
                foreach (var tile in intent.Tile)
                {
                    // Process tile targets, which can have either direction OR relative x/y
                    if (tile.Direction == Xml.IntentDirection.None)
                    {
                        if (tile.RelativeX == 0 && tile.RelativeY == 0)
                        {
                            //GameLog.UserActivityInfo($"GetTarget: tile, origin {origin.Name}, RelativeX && RelativeY == 0, skipping");
                            continue;
                        }
                        else
                        {
                            //GameLog.UserActivityInfo($"GetTarget: tile, ({origin.X + tile.RelativeX}, {origin.Y + tile.RelativeY}, origin {origin.Name}");
                            possibleTargets.AddRange(origin.Map.GetTileContents(origin.X + tile.RelativeX, origin.Y + tile.RelativeY).Where(e => e is Creature));
                        }
                    }
                    else
                    {
                        //GameLog.UserActivityInfo($"GetTarget: tile, intent {tile.Direction}, direction {origin.GetIntentDirection(tile.Direction)}, origin {origin.Name}");
                        possibleTargets.Add(origin.GetDirectionalTarget(origin.GetIntentDirection(tile.Direction)));
                    }
                }
                List <Creature> possible = intent.MaxTargets > 0 ? possibleTargets.Take(intent.MaxTargets).OfType <Creature>().ToList() : possibleTargets.OfType <Creature>().ToList();
                if (possible != null && possible.Count > 0)
                {
                    actualTargets = actualTargets.Concat(possible);
                }
                else
                {
                    GameLog.UserActivityInfo("GetTarget: No targets found");
                }

                // Remove all merchants
                // TODO: perhaps improve with a flag or extend in the future
                actualTargets = actualTargets.SkipWhile(e => e is Merchant);

                // Process intent flags

                var this_id = this.Id;

                if (this is Monster)
                {
                    // No hostile flag: remove players
                    if (!intent.Flags.Contains(Xml.IntentFlags.Hostile))
                    {
                        actualTargets = actualTargets.SkipWhile(e => e is User);
                    }
                    // No friendly flag: remove monsters
                    if (!intent.Flags.Contains(Xml.IntentFlags.Friendly))
                    {
                        actualTargets = actualTargets.SkipWhile(e => e is Monster);
                    }
                    // Group / pvp: n/a
                }
                else if (this is User userobj)
                {
                    // No PVP flag: remove PVP flagged players
                    // No hostile flag: remove monsters
                    // No friendly flag: remove non-PVP flagged players
                    // No group flag: remove group members
                    if (!intent.Flags.Contains(Xml.IntentFlags.Hostile))
                    {
                        actualTargets = actualTargets.SkipWhile(e => e is Monster);
                    }
                    if (!intent.Flags.Contains(Xml.IntentFlags.Friendly))
                    {
                        actualTargets = actualTargets.SkipWhile(e => e is User && !(e as User).Condition.PvpEnabled && e.Id != this_id);
                    }
                    if (!intent.Flags.Contains(Xml.IntentFlags.Pvp))
                    {
                        actualTargets = actualTargets.SkipWhile(e => (e is User) && (e as User).Condition.PvpEnabled && e.Id != this_id);
                    }
                    if (!intent.Flags.Contains(Xml.IntentFlags.Group))
                    {
                        // Remove group members
                        if (userobj.Group != null)
                        {
                            actualTargets = actualTargets.SkipWhile(e => (e is User) && userobj.Group.Contains(e as User));
                        }
                    }
                }
                // No Self flag: remove self
                if (!intent.Flags.Contains(Xml.IntentFlags.Self))
                {
                    //GameLog.UserActivityInfo($"Trying to remove self: my id is {this.Id} and actualtargets contains {String.Join(',',actualTargets.Select(e => e.Id).ToList())}");
                    actualTargets = actualTargets.Where(e => e.Id != this_id);
                    //GameLog.UserActivityInfo($"did it happen :o -  my id is {this.Id} and actualtargets contains {String.Join(',', actualTargets.Select(e => e.Id).ToList())}");
                }
            }

            return(actualTargets.ToList());
        }
Example #5
0
        public virtual List <Creature> GetTargets(Xml.Castable castable, Creature target = null)
        {
            List <Creature> actualTargets = new List <Creature>();

            /* INTENT HANDLING FOR TARGETING
             *
             * This is particularly confusing so it is documented here.
             * UseType=Target Radius=0 Direction=None -> exact clicked target
             * UseType=Target Radius=0 Direction=!None -> invalid
             * UseType=Target Radius=>0 Direction=None -> rect centered on target
             * UseType=Target Radius>0 Direction=(anything but none) -> directional rect target based on click x/y
             * UseType=NoTarget Radius=0 Direction=None -> self (wings of protection, maybe custom spells / mentoring / lore / etc)?
             * UseType=NoTarget Radius>0 Direction=None -> rect from self in all directions
             * UseType=NoTarget Radius>0 Direction=!None -> rect from self in specific direction
             */

            var intents = castable.Intents;

            foreach (var intent in intents)
            {
                var possibleTargets = new List <VisibleObject>();
                if (intent.UseType == Xml.SpellUseType.NoTarget && intent.Target.Contains(Xml.IntentTarget.Group))
                {
                    // Targeting group members
                    var user = this as User;
                    if (user != null && user.Group != null)
                    {
                        possibleTargets.AddRange(user.Group.Members.Where(m => m.Map.Id == Map.Id && m.Distance(this) < intent.Radius));
                    }
                }
                else if (intent.UseType == Xml.SpellUseType.Target && intent.Radius == 0 && intent.Direction == Xml.IntentDirection.None)
                {
                    // Targeting the exact clicked target
                    if (target == null)
                    {
                        GameLog.Error($"GetTargets: {castable.Name} - intent was for exact clicked target but no target was passed?");
                    }
                    else
                    // If we're doing damage, ensure the target is attackable
                    if (!castable.Effects.Damage.IsEmpty && target.Condition.IsAttackable)
                    {
                        possibleTargets.Add(target);
                    }
                    else if (castable.Effects.Damage.IsEmpty)
                    {
                        possibleTargets.Add(target);
                    }
                }
                else if (intent.UseType == Xml.SpellUseType.NoTarget && intent.Radius == 0 && intent.Direction == Xml.IntentDirection.None)
                {
                    // Targeting self - which, currently, is only allowed for non-damaging spells
                    if (castable.Effects.Damage.IsEmpty)
                    {
                        possibleTargets.Add(this);
                    }
                }
                else
                {
                    // Area targeting, directional or otherwise

                    Rectangle rect = new Rectangle(0, 0, 0, 0);
                    byte      X    = this.X;
                    byte      Y    = this.Y;

                    // Handle area targeting with click target as the source
                    if (intent.UseType == Xml.SpellUseType.Target)
                    {
                        X = target.X;
                        Y = target.Y;
                    }

                    switch (intent.Direction)
                    {
                    case Xml.IntentDirection.Front:
                    {
                        switch (Direction)
                        {
                        case Xml.Direction.North:
                        {
                            //facing north, attack north
                            rect = new Rectangle(X, Y - intent.Radius, 1, intent.Radius);
                        }
                        break;

                        case Xml.Direction.South:
                        {
                            //facing south, attack south
                            rect = new Rectangle(X, Y, 1, 1 + intent.Radius);
                        }
                        break;

                        case Xml.Direction.East:
                        {
                            //facing east, attack east
                            rect = new Rectangle(X, Y, 1 + intent.Radius, 1);
                        }
                        break;

                        case Xml.Direction.West:
                        {
                            //facing west, attack west
                            rect = new Rectangle(X - intent.Radius, Y, intent.Radius, 1);
                        }
                        break;
                        }
                    }
                    break;

                    case Xml.IntentDirection.Back:
                    {
                        switch (Direction)
                        {
                        case Xml.Direction.North:
                        {
                            //facing north, attack south
                            rect = new Rectangle(X, Y, 1, 1 + intent.Radius);
                        }
                        break;

                        case Xml.Direction.South:
                        {
                            //facing south, attack north
                            rect = new Rectangle(X, Y - intent.Radius, 1, intent.Radius);
                        }
                        break;

                        case Xml.Direction.East:
                        {
                            //facing east, attack west
                            rect = new Rectangle(X - intent.Radius, Y, intent.Radius, 1);
                        }
                        break;

                        case Xml.Direction.West:
                        {
                            //facing west, attack east
                            rect = new Rectangle(X, Y, 1 + intent.Radius, 1);
                        }
                        break;
                        }
                    }
                    break;

                    case Xml.IntentDirection.Left:
                    {
                        switch (Direction)
                        {
                        case Xml.Direction.North:
                        {
                            //facing north, attack west
                            rect = new Rectangle(X - intent.Radius, Y, intent.Radius, 1);
                        }
                        break;

                        case Xml.Direction.South:
                        {
                            //facing south, attack east
                            rect = new Rectangle(X, Y, 1 + intent.Radius, 1);
                        }
                        break;

                        case Xml.Direction.East:
                        {
                            //facing east, attack north
                            rect = new Rectangle(X, Y, 1, 1 + intent.Radius);
                        }
                        break;

                        case Xml.Direction.West:
                        {
                            //facing west, attack south
                            rect = new Rectangle(X, Y - intent.Radius, 1, intent.Radius);
                        }
                        break;
                        }
                    }
                    break;

                    case Xml.IntentDirection.Right:
                    {
                        switch (Direction)
                        {
                        case Xml.Direction.North:
                        {
                            //facing north, attack east
                            rect = new Rectangle(X, Y, 1 + intent.Radius, 1);
                        }
                        break;

                        case Xml.Direction.South:
                        {
                            //facing south, attack west
                            rect = new Rectangle(X - intent.Radius, Y, intent.Radius, 1);
                        }
                        break;

                        case Xml.Direction.East:
                        {
                            //facing east, attack south
                            rect = new Rectangle(X, Y - intent.Radius, 1, intent.Radius);
                        }
                        break;

                        case Xml.Direction.West:
                        {
                            //facing west, attack north
                            rect = new Rectangle(X, Y, 1, 1 + intent.Radius);
                        }
                        break;
                        }
                    }
                    break;

                    case Xml.IntentDirection.Nearby:
                    case Xml.IntentDirection.None:
                    {
                        //attack radius
                        rect = new Rectangle(X - intent.Radius, Y - intent.Radius, Math.Max(intent.Radius, (byte)1) * 2, Math.Max(intent.Radius, (byte)1) * 2);
                    }
                    break;
                    }
                    GameLog.Info($"Rectangle: x: {X - intent.Radius} y: {Y - intent.Radius}, radius: {intent.Radius} - LOCATION: {rect.Location} TOP: {rect.Top}, BOTTOM: {rect.Bottom}, RIGHT: {rect.Right}, LEFT: {rect.Left}");
                    if (rect.IsEmpty)
                    {
                        continue;
                    }

                    possibleTargets.AddRange(Map.EntityTree.GetObjects(rect).Where(obj => obj is Creature && obj != this));
                }

                // Remove merchants
                possibleTargets = possibleTargets.Where(e => !(e is Merchant)).ToList();

                // Handle intent flags
                if (this is Monster)
                {
                    // No hostile flag: remove users
                    // No friendly flag: remove monsters
                    // Group / pvp: do not apply here
                    if (!intent.Target.Contains(Xml.IntentTarget.Friendly))
                    {
                        possibleTargets = possibleTargets.Where(e => !(e is Monster)).ToList();
                    }
                    if (!intent.Target.Contains(Xml.IntentTarget.Hostile))
                    {
                        possibleTargets = possibleTargets.Where(e => !(e is User)).ToList();
                    }
                }
                else if (this is User && intent.UseType != Xml.SpellUseType.NoTarget)
                {
                    var user = this as User;
                    // No hostile flag: remove monsters
                    // No friendly flag: remove users with pvp disabled
                    // No pvp: remove
                    // If we aren't targeting friendlies or pvp, remove all users entirely
                    if (!intent.Target.Contains(Xml.IntentTarget.Pvp))
                    {
                        possibleTargets = possibleTargets.Where(e => !(e is User && (e as Creature).Condition.PvpEnabled == true)).ToList();
                    }
                    if (!intent.Target.Contains(Xml.IntentTarget.Friendly))
                    {
                        possibleTargets = possibleTargets.Where(e => !(e is User && (e as Creature).Condition.PvpEnabled == false)).ToList();
                    }
                    // If we aren't targeting hostiles, remove all monsters
                    if (!intent.Target.Contains(Xml.IntentTarget.Hostile))
                    {
                        possibleTargets = possibleTargets.Where(e => !(e is Monster)).ToList();
                    }
                }

                // Finally, add the targets to our list

                List <Creature> possible = intent.MaxTargets > 0 ? possibleTargets.Take(intent.MaxTargets).OfType <Creature>().ToList() : possibleTargets.OfType <Creature>().ToList();
                if (possible != null && possible.Count > 0)
                {
                    actualTargets.AddRange(possible);
                }
                else
                {
                    GameLog.Info("No targets found");
                }
            }
            return(actualTargets);
        }