240 lines
7.3 KiB
C#
240 lines
7.3 KiB
C#
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<Mobile, long> m_Table = new Dictionary<Mobile, long>();
|
|
public static Dictionary<Mobile, long> Table { get { return m_Table; } }
|
|
|
|
public static void DefragTable()
|
|
{
|
|
List<Mobile> mobiles = new List<Mobile>(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<Mobile> GetTargets()
|
|
{
|
|
WildfireSpell.DefragTable();
|
|
|
|
return m_Spell.AcquireIndirectTargets(m_Location, m_Range).OfType<Mobile>().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();
|
|
}
|
|
}
|
|
}
|
|
}
|