using System; using System.Collections.Generic; using System.Linq; using Server.Targeting; using Server.Multis; using Server.Regions; using Server.Mobiles; namespace Server.Spells.Spellweaving { public class WildfireSpell : ArcanistSpell { private static readonly SpellInfo m_Info = new SpellInfo( "Wildfire", "Haelyn", -1, false); public WildfireSpell(Mobile caster, Item scroll) : base(caster, scroll, m_Info) { } public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(2.5); } } public override double RequiredSkill { get { return 66.0; } } public override int RequiredMana { get { return 50; } } public override void OnCast() { Caster.Target = new InternalTarget(this); } public void Target(Point3D p) { if (!Caster.CanSee(p)) { Caster.SendLocalizedMessage(500237); // Target can not be seen. } else if (CheckSequence()) { int level = GetFocusLevel(Caster); double skill = Caster.Skills[CastSkill].Value; int tiles = 5 + level; int damage = 10 + (int)Math.Max(1, (skill / 24)) + level; int duration = (int)Math.Max(1, skill / 24) + level; for (int x = p.X - tiles; x <= p.X + tiles; x += tiles) { for (int y = p.Y - tiles; y <= p.Y + tiles; y += tiles) { if (p.X == x && p.Y == y) continue; Point3D p3d = new Point3D(x, y, Caster.Map.GetAverageZ(x, y)); if (CanFitFire(p3d, Caster)) new FireItem(duration).MoveToWorld(p3d, Caster.Map); } } Effects.PlaySound(p, Caster.Map, 0x5CF); NegativeAttributes.OnCombatAction(Caster); new InternalTimer(this, Caster, p, damage, tiles, duration).Start(); } FinishSequence(); } private bool CanFitFire(Point3D p, Mobile caster) { if (!Caster.Map.CanFit(p, 12, true, false)) return false; if (BaseHouse.FindHouseAt(p, caster.Map, 20) != null) return false; foreach(RegionRect r in caster.Map.GetSector(p).RegionRects) { if (!r.Contains(p)) continue; GuardedRegion reg = (GuardedRegion)Region.Find(p, caster.Map).GetRegion(typeof(GuardedRegion)); if (reg != null && !reg.Disabled) return false; } return true; } private static Dictionary m_Table = new Dictionary(); public static Dictionary Table { get { return m_Table; } } public static void DefragTable() { List mobiles = new List(m_Table.Keys); foreach (Mobile m in mobiles) { if (Core.TickCount - m_Table[m] >= 0) m_Table.Remove(m); } ColUtility.Free(mobiles); } public class InternalTarget : Target { private readonly WildfireSpell m_Owner; public InternalTarget(WildfireSpell owner) : base(12, true, TargetFlags.None) { m_Owner = owner; } protected override void OnTarget(Mobile m, object o) { if (o is IPoint3D) { m_Owner.Target(new Point3D((IPoint3D)o)); } } protected override void OnTargetFinish(Mobile m) { m_Owner.FinishSequence(); } } public class InternalTimer : Timer { private readonly Spell m_Spell; private readonly Mobile m_Owner; private readonly Point3D m_Location; private readonly int m_Damage; private readonly int m_Range; private int m_LifeSpan; private Map m_Map; public InternalTimer(Spell spell, Mobile owner, Point3D location, int damage, int range, int duration) : base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), duration) { m_Spell = spell; m_Owner = owner; m_Location = location; m_Damage = damage; m_Range = range; m_LifeSpan = duration; m_Map = owner.Map; } protected override void OnTick() { if (m_Owner == null || m_Map == null || m_Map == Map.Internal) return; m_LifeSpan -= 1; var targets = GetTargets().Where(m => BaseHouse.FindHouseAt(m.Location, m.Map, 20) == null).ToList(); int count = targets.Count; foreach (Mobile m in targets) { m_Owner.DoHarmful(m); if (m_Map.CanFit(m.Location, 12, true, false)) new FireItem(m_LifeSpan).MoveToWorld(m.Location, m_Map); Effects.PlaySound(m.Location, m_Map, 0x5CF); double sdiBonus = (double)AosAttributes.GetValue(m_Owner, AosAttribute.SpellDamage) / 100; if (m is PlayerMobile && sdiBonus > .15) sdiBonus = .15; int damage = m_Damage + (int)((double)m_Damage * sdiBonus); if (count > 1) damage /= Math.Min(3, count); AOS.Damage(m, m_Owner, damage, 0, 100, 0, 0, 0, 0, 0, DamageType.SpellAOE); WildfireSpell.Table[m] = Core.TickCount + 1000; } ColUtility.Free(targets); } private IEnumerable GetTargets() { WildfireSpell.DefragTable(); return m_Spell.AcquireIndirectTargets(m_Location, m_Range).OfType().Where(m => !m_Table.ContainsKey(m)); } } public class FireItem : Item { public FireItem(int duration) : base(Utility.RandomBool() ? 0x398C : 0x3996) { Movable = false; Timer.DelayCall(TimeSpan.FromSeconds(duration), new TimerCallback(Delete)); } public FireItem(Serial serial) : base(serial) { } public override void Serialize(GenericWriter writer) { base.Serialize(writer); writer.Write((int)0); // version } public override void Deserialize(GenericReader reader) { base.Deserialize(reader); int version = reader.ReadInt(); Delete(); } } } }