318 lines
8.6 KiB
C#
318 lines
8.6 KiB
C#
using System;
|
|
using Server.Mobiles;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Server.Spells.Chivalry
|
|
{
|
|
public class EnemyOfOneSpell : PaladinSpell
|
|
{
|
|
private static readonly SpellInfo m_Info = new SpellInfo(
|
|
"Enemy of One", "Forul Solum",
|
|
-1,
|
|
9002);
|
|
|
|
public EnemyOfOneSpell(Mobile caster, Item scroll)
|
|
: base(caster, scroll, m_Info)
|
|
{
|
|
}
|
|
|
|
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(0.5); } }
|
|
|
|
public override double RequiredSkill { get { return 45.0; } }
|
|
public override int RequiredMana { get { return 20; } }
|
|
public override int RequiredTithing { get { return 10; } }
|
|
public override int MantraNumber { get { return 1060723; } } // Forul Solum
|
|
public override bool BlocksMovement { get { return false; } }
|
|
|
|
public override TimeSpan GetCastDelay()
|
|
{
|
|
TimeSpan delay = base.GetCastDelay();
|
|
|
|
if (Core.SA && UnderEffect(Caster))
|
|
{
|
|
double milliseconds = delay.TotalMilliseconds / 2;
|
|
|
|
delay = TimeSpan.FromMilliseconds(milliseconds);
|
|
}
|
|
|
|
return delay;
|
|
}
|
|
|
|
public override void OnCast()
|
|
{
|
|
if (Core.SA && UnderEffect(Caster))
|
|
{
|
|
PlayEffects();
|
|
|
|
// As per Pub 71, Enemy of one has now been changed to a Spell Toggle. You can remove the effect
|
|
// before the duration expires by recasting the spell.
|
|
RemoveEffect(Caster);
|
|
}
|
|
else if (CheckSequence())
|
|
{
|
|
PlayEffects();
|
|
|
|
// TODO: validate formula
|
|
var seconds = ComputePowerValue(1);
|
|
Utility.FixMinMax(ref seconds, 67, 228);
|
|
|
|
var delay = TimeSpan.FromSeconds(seconds);
|
|
|
|
var timer = Timer.DelayCall(delay, RemoveEffect, Caster);
|
|
|
|
var expire = DateTime.UtcNow + delay;
|
|
|
|
var context = new EnemyOfOneContext(Caster, timer, expire);
|
|
context.OnCast();
|
|
m_Table[Caster] = context;
|
|
}
|
|
|
|
FinishSequence();
|
|
}
|
|
|
|
private void PlayEffects()
|
|
{
|
|
Caster.PlaySound(0x0F5);
|
|
Caster.PlaySound(0x1ED);
|
|
|
|
Caster.FixedParticles(0x375A, 1, 30, 9966, 33, 2, EffectLayer.Head);
|
|
Caster.FixedParticles(0x37B9, 1, 30, 9502, 43, 3, EffectLayer.Head);
|
|
}
|
|
|
|
private static readonly Dictionary<Mobile, EnemyOfOneContext> m_Table = new Dictionary<Mobile, EnemyOfOneContext>();
|
|
|
|
public static EnemyOfOneContext GetContext(Mobile m)
|
|
{
|
|
if (!m_Table.ContainsKey(m))
|
|
return null;
|
|
|
|
return m_Table[m];
|
|
}
|
|
|
|
public static bool UnderEffect(Mobile m)
|
|
{
|
|
return m_Table.ContainsKey(m);
|
|
}
|
|
|
|
public static void RemoveEffect(Mobile m)
|
|
{
|
|
if (m_Table.ContainsKey(m))
|
|
{
|
|
var context = m_Table[m];
|
|
|
|
m_Table.Remove(m);
|
|
|
|
context.OnRemoved();
|
|
|
|
m.PlaySound(0x1F8);
|
|
}
|
|
}
|
|
|
|
public static Dictionary<Type, string> NameCache { get; set; }
|
|
|
|
public static void Configure()
|
|
{
|
|
if (NameCache == null)
|
|
NameCache = new Dictionary<Type, string>();
|
|
}
|
|
|
|
public static string GetTypeName(Mobile defender)
|
|
{
|
|
if (defender is PlayerMobile || (defender is BaseCreature && ((BaseCreature)defender).GetMaster() is PlayerMobile))
|
|
{
|
|
return defender.Name;
|
|
}
|
|
|
|
Type t = defender.GetType();
|
|
|
|
if (NameCache.ContainsKey(t))
|
|
{
|
|
return NameCache[t];
|
|
}
|
|
|
|
return AddNameToCache(t);
|
|
}
|
|
|
|
public static string AddNameToCache(Type t)
|
|
{
|
|
string name = t.Name;
|
|
|
|
if (name != null)
|
|
{
|
|
for (int i = 0; i < name.Length; i++)
|
|
{
|
|
if (i > 0 && Char.IsUpper(name[i]))
|
|
{
|
|
name = name.Insert(i, " ");
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (name.EndsWith("y"))
|
|
{
|
|
name = name.Substring(0, name.Length - 1);
|
|
name = name + "ies";
|
|
}
|
|
else if (!name.EndsWith("s"))
|
|
{
|
|
name = name + "s";
|
|
}
|
|
|
|
|
|
NameCache[t] = name.ToLower();
|
|
}
|
|
|
|
return name;
|
|
}
|
|
}
|
|
|
|
public class EnemyOfOneContext
|
|
{
|
|
private Mobile m_Owner;
|
|
private Timer m_Timer;
|
|
private DateTime m_Expire;
|
|
private Type m_TargetType;
|
|
private int m_DamageScalar;
|
|
private string m_TypeName;
|
|
|
|
private Mobile m_PlayerOrPet;
|
|
|
|
public Mobile Owner { get { return m_Owner; } }
|
|
public Timer Timer { get { return m_Timer; } }
|
|
public Type TargetType { get { return m_TargetType; } }
|
|
public int DamageScalar { get { return m_DamageScalar; } }
|
|
public string TypeName { get { return m_TypeName; } }
|
|
|
|
public EnemyOfOneContext(Mobile owner, Timer timer, DateTime expire)
|
|
{
|
|
m_Owner = owner;
|
|
m_Timer = timer;
|
|
m_Expire = expire;
|
|
m_TargetType = null;
|
|
m_DamageScalar = 50;
|
|
}
|
|
|
|
public bool IsWaitingForEnemy { get { return m_TargetType == null; } }
|
|
|
|
public bool IsEnemy(Mobile m)
|
|
{
|
|
if (m is BaseCreature && ((BaseCreature)m).GetMaster() == Owner)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_PlayerOrPet != null)
|
|
{
|
|
if (m_PlayerOrPet == m)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (m_TargetType == m.GetType())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void OnCast()
|
|
{
|
|
UpdateBuffInfo();
|
|
}
|
|
|
|
private void UpdateDamage()
|
|
{
|
|
var chivalry = (int)m_Owner.Skills.Chivalry.Value;
|
|
m_DamageScalar = 10 + ((chivalry - 40) * 9) / 10;
|
|
|
|
if (m_PlayerOrPet != null)
|
|
m_DamageScalar /= 2;
|
|
}
|
|
|
|
private void UpdateBuffInfo()
|
|
{
|
|
if (m_TypeName == null)
|
|
{
|
|
BuffInfo.AddBuff(m_Owner, new BuffInfo(BuffIcon.EnemyOfOne, 1075653, 1075902, m_Expire - DateTime.UtcNow, m_Owner, string.Format("{0}\t{1}", m_DamageScalar, "100"), true));
|
|
}
|
|
else
|
|
{
|
|
BuffInfo.AddBuff(m_Owner, new BuffInfo(BuffIcon.EnemyOfOne, 1075653, 1075654, m_Expire - DateTime.UtcNow, m_Owner, string.Format("{0}\t{1}\t{2}\t{3}", m_DamageScalar, TypeName, ".", "100"), true));
|
|
}
|
|
}
|
|
|
|
public void OnHit(Mobile defender)
|
|
{
|
|
if (m_TargetType == null)
|
|
{
|
|
m_TypeName = EnemyOfOneSpell.GetTypeName(defender);
|
|
|
|
if (defender is PlayerMobile || (defender is BaseCreature && ((BaseCreature)defender).GetMaster() is PlayerMobile))
|
|
{
|
|
m_PlayerOrPet = defender;
|
|
TimeSpan duration = TimeSpan.FromSeconds(8);
|
|
|
|
if (DateTime.UtcNow + duration < m_Expire)
|
|
{
|
|
m_Expire = DateTime.UtcNow + duration;
|
|
}
|
|
|
|
if (m_Timer != null)
|
|
{
|
|
m_Timer.Stop();
|
|
m_Timer = null;
|
|
}
|
|
|
|
m_Timer = Timer.DelayCall(duration, EnemyOfOneSpell.RemoveEffect, m_Owner);
|
|
}
|
|
else
|
|
{
|
|
m_TargetType = defender.GetType();
|
|
}
|
|
|
|
UpdateDamage();
|
|
DeltaEnemies();
|
|
UpdateBuffInfo();
|
|
}
|
|
else if (Core.SA)
|
|
{
|
|
// Odd but OSI recalculates when the target changes...
|
|
UpdateDamage();
|
|
}
|
|
}
|
|
|
|
public void OnRemoved()
|
|
{
|
|
if (m_Timer != null)
|
|
m_Timer.Stop();
|
|
|
|
DeltaEnemies();
|
|
|
|
BuffInfo.RemoveBuff(m_Owner, BuffIcon.EnemyOfOne);
|
|
}
|
|
|
|
private void DeltaEnemies()
|
|
{
|
|
IPooledEnumerable eable = m_Owner.GetMobilesInRange(18);
|
|
|
|
foreach (Mobile m in eable)
|
|
{
|
|
if (m_PlayerOrPet != null)
|
|
{
|
|
if (m == m_PlayerOrPet)
|
|
{
|
|
m.Delta(MobileDelta.Noto);
|
|
}
|
|
}
|
|
else if (m.GetType() == m_TargetType)
|
|
{
|
|
m.Delta(MobileDelta.Noto);
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
}
|
|
}
|
|
}
|