ServUO Version
Publish 57
Ultima Expansion
Endless Journey
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();
}
}
}
}
What is the best way to go about creating independent spell timers? For example, I want a player to cast magic missile with a 30 second recovery delay but be able to cast fireball which has a 5 second recovery delay. While magic missile is recovering the player can cast fireballs every 5 seconds.
I referred to the question in the post of "Samuel Packham". I'm asking for your help because I don't have the right answer. Can you tell me an example of adding to the magic spell? Or can you show me the example you applied to the Wildfire.cs script above?
I've been holding on to this issue for months quite a long time. I desperately need help.
 
You can use the built in servuo code to do this using these methods:
CanBeginAction
BeginAction
EndAction

Here's a quick example in a spell
public override bool CheckCast() { if (!base.CheckCast()) return false; if (!Caster.CanBeginAction(typeof(IpsumUmbraSpell))|| !Caster.CanBeginAction(typeof(IpsumDivinusSpell))|| !Caster.CanBeginAction(typeof(IpsumFlammaSpell))|| !Caster.CanBeginAction(typeof(IpsumVentusSpell))) { Caster.SendMessage("Aura spells are on cooldown."); return false; } return true; } public override void OnCast() { if ( CheckSequence() ) { if (Caster.BeginAction(typeof(IpsumUmbraSpell))) { if (Caster.IsStaff()) { Caster.EndAction(typeof(IpsumUmbraSpell)); } else { Timer.DelayCall(TimeSpan.FromSeconds(20), delegate { Caster.EndAction(typeof(IpsumUmbraSpell)); }); } //do spell effects Caster.PlaySound(350); Caster.FixedParticles(0x374A, 9, 30, 0, 1246, 0, EffectLayer.Waist); new ExpireTimer(Caster, 0, this).Start(); } } FinishSequence(); }
You can see how in CheckCast I'm checking for 4 different "Aura" spells that I have sharing the same "cooldown". If the spell is not on cooldown then the check passes and they can cast it.
In OnCast the method BeginAction locks down the spell and begins the timeout. This could last until the next server reboot if you don't follow up with EndAction somewhere, that's why I have the Timer.DelayCall set to 20 seconds. Also I have a check there for Caster.IsStaff so that staff characters won't have the cooldown in place.
 
You can use the built in servuo code to do this using these methods:
CanBeginAction
BeginAction
EndAction

Here's a quick example in a spell
public override bool CheckCast() { if (!base.CheckCast()) return false; if (!Caster.CanBeginAction(typeof(IpsumUmbraSpell))|| !Caster.CanBeginAction(typeof(IpsumDivinusSpell))|| !Caster.CanBeginAction(typeof(IpsumFlammaSpell))|| !Caster.CanBeginAction(typeof(IpsumVentusSpell))) { Caster.SendMessage("Aura spells are on cooldown."); return false; } return true; } public override void OnCast() { if ( CheckSequence() ) { if (Caster.BeginAction(typeof(IpsumUmbraSpell))) { if (Caster.IsStaff()) { Caster.EndAction(typeof(IpsumUmbraSpell)); } else { Timer.DelayCall(TimeSpan.FromSeconds(20), delegate { Caster.EndAction(typeof(IpsumUmbraSpell)); }); } //do spell effects Caster.PlaySound(350); Caster.FixedParticles(0x374A, 9, 30, 0, 1246, 0, EffectLayer.Waist); new ExpireTimer(Caster, 0, this).Start(); } } FinishSequence(); }
You can see how in CheckCast I'm checking for 4 different "Aura" spells that I have sharing the same "cooldown". If the spell is not on cooldown then the check passes and they can cast it.
In OnCast the method BeginAction locks down the spell and begins the timeout. This could last until the next server reboot if you don't follow up with EndAction somewhere, that's why I have the Timer.DelayCall set to 20 seconds. Also I have a check there for Caster.IsStaff so that staff characters won't have the cooldown in place.
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();
}
}
}
}
Thank you from the bottom of my heart for your answer. I don't know how to apply it because I lack a lot of knowledge. Can you show me the example I applied to the Wildfire.cs script I posted? I'm sorry for bothering you. I'd appreciate it if you could help me a little more.
 
Sure! It's easy enough, just insert the CheckCast part, and add the bits of code into the same similar spots as in the example.
using System; using System.Collections.Generic; using System.Linq; using Server.Targeting; using Server.Multis; using Server.Regions; using Server.Mobiles; using Server.Spells.Spellcraft; 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 bool CheckCast() { if (!base.CheckCast()) return false; if (!Caster.CanBeginAction(typeof(WildfireSpell)) ) { Caster.SendMessage("Wildfire Spell is on cooldown."); return false; } return true; } 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()) { if (Caster.BeginAction(typeof(WildfireSpell))) { if (Caster.IsStaff()) { Caster.EndAction(typeof(WildfireSpell)); } else { Timer.DelayCall(TimeSpan.FromSeconds(30), delegate { Caster.EndAction(typeof(WildfireSpell)); }); } 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(); } } } }
 
Sure! It's easy enough, just insert the CheckCast part, and add the bits of code into the same similar spots as in the example.
using System; using System.Collections.Generic; using System.Linq; using Server.Targeting; using Server.Multis; using Server.Regions; using Server.Mobiles; using Server.Spells.Spellcraft; 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 bool CheckCast() { if (!base.CheckCast()) return false; if (!Caster.CanBeginAction(typeof(WildfireSpell)) ) { Caster.SendMessage("Wildfire Spell is on cooldown."); return false; } return true; } 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()) { if (Caster.BeginAction(typeof(WildfireSpell))) { if (Caster.IsStaff()) { Caster.EndAction(typeof(WildfireSpell)); } else { Timer.DelayCall(TimeSpan.FromSeconds(30), delegate { Caster.EndAction(typeof(WildfireSpell)); }); } 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(); } } } }
Wow! It works perfectly. I feel like I solved a really difficult homework. Thank you so much for solving my long-standing worries. I wish you happiness and blessings all the time.
Oh! Can I ask you one more question? This will be my last question I swear.
if (!Caster.CanBeginAction(typeof(WildfireSpell)) )

{

Caster.SendMessage("Wildfire Spell is on cooldown.");

return false;

}
When you try magic while cooldown, the message above appears. Is there a way to display the remaining time in real time? I was wondering if there is a way to show the current time as a message if I try magic when I have 20 seconds of cooldown time for example. I wonder if it's possible to mark it like "You have a few seconds left".
 
Last edited:
I’m sure it’s possible but it’s not coded into the existing systems. It would take some custom coding to set up the checks and replies.
 
I’m sure it’s possible but it’s not coded into the existing systems. It would take some custom coding to set up the checks and replies.
Oh! Thank you for letting me know. I am satisfied enough even if the problem is not solved. Once again, thanks to you, I have solved difficult homework for a long time. I heartily thank you.
 
Back