fix: Fixes skill requirements and cleans up magery spells (#870)

* Preps damage stacking for mysticism, necromancy fixes, and spellweaving
* Removes extra LINQ allocations
* Fixes skill requirements for magery spells.
This commit is contained in:
Kamron Batman 2021-12-05 09:40:57 -08:00 • committed by GitHub
parent 1cf7b6c056
commit 4f9714818d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 311 additions and 382 deletions

View file

@ -17,19 +17,16 @@ namespace Server.Spells
public abstract class Spell : ISpell
{
private static readonly TimeSpan NextSpellDelay = TimeSpan.FromSeconds(0.75);
private static readonly TimeSpan AnimateDelay = TimeSpan.FromSeconds(1.5);
// In reality, it's ANY delayed Damage spell Post-AoS that can't stack, but, only
// Expo & Magic Arrow have enough delay and a short enough cast time to bring up
// the possibility of stacking 'em. Note that a MA & an Explosion will stack, but
// of course, two MA's won't.
private static readonly Dictionary<Type, DelayedDamageContextWrapper> m_ContextTable =
new();
private static readonly Dictionary<Type, DelayedDamageContextWrapper> _contextTable = new();
private AnimTimer m_AnimTimer;
private CastTimer m_CastTimer;
private AnimTimer _animTimer;
private CastTimer _castTimer;
public Spell(Mobile caster, Item scroll, SpellInfo info)
{
@ -60,13 +57,17 @@ namespace Server.Spells
public virtual bool DelayedDamage => false;
public virtual bool DelayedDamageStacking => true;
public static readonly Type[] AOSNoDelayedDamageStackingSelf = Core.AOS ? Array.Empty<Type>() : null;
// Null means stacking is allowed while empty indicates no stacking with self
// More than zero means no stacking with self and other spells
public virtual Type[] DelayedDamageSpellFamilyStacking => null;
public virtual bool BlockedByHorrificBeast => true;
public virtual bool BlockedByAnimalForm => true;
public virtual bool BlocksMovement => true;
public virtual bool CheckNextSpellTime => !(Scroll is BaseWand);
public virtual bool CheckNextSpellTime => Scroll is not BaseWand;
public virtual int CastRecoveryBase => 6;
public virtual int CastRecoveryFastScalar => 1;
@ -149,22 +150,39 @@ namespace Server.Spells
public void StartDelayedDamageContext(Mobile m, Timer t)
{
if (DelayedDamageStacking)
var damageStacking = DelayedDamageSpellFamilyStacking;
if (damageStacking == null)
{
return; // Sanity
}
if (!m_ContextTable.TryGetValue(GetType(), out var contexts))
var type = GetType();
if (!_contextTable.TryGetValue(type, out var context))
{
m_ContextTable[GetType()] = contexts = new DelayedDamageContextWrapper();
_contextTable[type] = context = new DelayedDamageContextWrapper();
for (int i = 0; i < damageStacking.Length; i++)
{
_contextTable.Add(damageStacking[i], context);
}
}
contexts.Add(m, t);
context.Add(m, t);
}
public bool HasDelayedDamageContext(Mobile m) =>
DelayedDamageSpellFamilyStacking != null &&
_contextTable.TryGetValue(GetType(), out var context) && context.Contains(m);
public void RemoveDelayedDamageContext(Mobile m)
{
if (m_ContextTable.TryGetValue(GetType(), out var contexts))
if (m == null || DelayedDamageSpellFamilyStacking == null)
{
return; // Sanity
}
if (_contextTable.TryGetValue(GetType(), out var contexts))
{
contexts.Remove(m);
}
@ -389,8 +407,8 @@ namespace Server.Spells
{
OnDisturb(type, true);
m_CastTimer?.Stop();
m_AnimTimer?.Stop();
_castTimer?.Stop();
_animTimer?.Stop();
if (Core.AOS && Caster.Player && type == DisturbType.Hurt)
{
@ -469,7 +487,7 @@ namespace Server.Spells
{
Caster.SendLocalizedMessage(1061091); // You cannot cast that spell in this form.
}
else if (!(Scroll is BaseWand) && (Caster.Paralyzed || Caster.Frozen))
else if (Scroll is not BaseWand && (Caster.Paralyzed || Caster.Frozen))
{
Caster.SendLocalizedMessage(502643); // You can not cast a spell while frozen.
}
@ -492,7 +510,7 @@ namespace Server.Spells
State = SpellState.Casting;
Caster.Spell = this;
if (!(Scroll is BaseWand) && RevealOnCast)
if (Scroll is not BaseWand && RevealOnCast)
{
Caster.RevealingAction();
}
@ -507,8 +525,8 @@ namespace Server.Spells
if (count != 0)
{
m_AnimTimer = new AnimTimer(this, count);
m_AnimTimer.Start();
_animTimer = new AnimTimer(this, count);
_animTimer.Start();
}
if (Info.LeftHandEffect > 0)
@ -532,18 +550,18 @@ namespace Server.Spells
WeaponAbility.ClearCurrentAbility(Caster);
}
m_CastTimer = new CastTimer(this, castDelay);
_castTimer = new CastTimer(this, castDelay);
// m_CastTimer.Start();
OnBeginCast();
if (castDelay > TimeSpan.Zero)
{
m_CastTimer.Start();
_castTimer.Start();
}
else
{
m_CastTimer.Tick();
_castTimer.Tick();
}
return true;
@ -647,11 +665,6 @@ namespace Server.Spells
return TimeSpan.FromSeconds((double)delay / CastRecoveryPerSecond);
}
// public virtual int CastDelayBase{ get{ return 3; } }
// public virtual int CastDelayFastScalar{ get{ return 1; } }
// public virtual int CastDelayPerSecond{ get{ return 4; } }
// public virtual int CastDelayMinimum{ get{ return 1; } }
public virtual TimeSpan GetCastDelay()
{
if (Scroll is BaseWand)
@ -665,7 +678,7 @@ namespace Server.Spells
// Paladins with magery of 70.0 or above are subject to a faster casting cap of 2
var fcMax = 4;
if (CastSkill == SkillName.Magery || CastSkill == SkillName.Necromancy ||
if (CastSkill is SkillName.Magery or SkillName.Necromancy ||
CastSkill == SkillName.Chivalry && Caster.Skills.Magery.Value >= 70.0)
{
fcMax = 2;
@ -749,12 +762,9 @@ namespace Server.Spells
Scroll.Movable = m;
}
else
else if (ClearHandsOnCast)
{
if (ClearHandsOnCast)
{
Caster.ClearHands();
}
Caster.ClearHands();
}
var karma = ComputeKarmaAward();
@ -838,10 +848,14 @@ namespace Server.Spells
m_Contexts.Add(m, t);
}
public bool Contains(Mobile m) => m_Contexts.ContainsKey(m);
public void Remove(Mobile m)
{
m_Contexts.Remove(m);
// TODO: Should we stop the timer?
if (m_Contexts.Remove(m, out var t))
{
t.Stop();
}
}
}
@ -876,7 +890,7 @@ namespace Server.Spells
if (!Running)
{
m_Spell.m_AnimTimer = null;
m_Spell._animTimer = null;
}
}
}
@ -900,7 +914,7 @@ namespace Server.Spells
if (m_Spell.State == SpellState.Casting && m_Spell.Caster.Spell == m_Spell)
{
m_Spell.State = SpellState.Sequencing;
m_Spell.m_CastTimer = null;
m_Spell._castTimer = null;
m_Spell.Caster.OnSpellCast(m_Spell);
m_Spell.Caster.Region?.OnSpellCast(m_Spell.Caster, m_Spell);
m_Spell.Caster.NextSpellTime =
@ -915,7 +929,7 @@ namespace Server.Spells
m_Spell.Caster.Target?.BeginTimeout(m_Spell.Caster, 30000); // 30 seconds
}
m_Spell.m_CastTimer = null;
m_Spell._castTimer = null;
}
}