public void Cast(UserGroup target, Xml.SpawnCastable creatureCastable, Xml.TargetType targetType) { var inRange = Map.EntityTree.GetObjects(GetViewport()).OfType <User>(); var result = inRange.Intersect(target.Members).ToList(); var castable = World.WorldData.GetByIndex <Xml.Castable>(creatureCastable.Name); if (targetType == Xml.TargetType.Group) { foreach (var user in result) { UseCastable(castable, user, creatureCastable); } } if (targetType == Xml.TargetType.Random) { var rngSelection = _random.Next(0, result.Count); var user = result[rngSelection]; UseCastable(castable, user, creatureCastable); } Condition.Casting = false; }
public void Cast(Creature target, Xml.SpawnCastable creatureCastable) { var castable = World.WorldData.GetByIndex <Xml.Castable>(creatureCastable.Name); if (target is Merchant) { return; } UseCastable(castable, target, creatureCastable); Condition.Casting = false; }
public Xml.SpawnCastable SelectSpawnCastable(SpawnCastType castType) { Xml.SpawnCastable creatureCastable = null; switch (castType) { case SpawnCastType.Offensive: creatureCastable = _castables.Offense.Castables.PickRandom(true); break; case SpawnCastType.Defensive: creatureCastable = _castables.Defense.Castables.PickRandom(true); break; case SpawnCastType.NearDeath: creatureCastable = _castables.NearDeath.Castables.PickRandom(true); break; case SpawnCastType.OnDeath: creatureCastable = _castables.OnDeath.PickRandom(true); break; } return(creatureCastable); }
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); }