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:
parent
1cf7b6c056
commit
4f9714818d
16 changed files with 311 additions and 382 deletions
|
|
@ -5,9 +5,10 @@ namespace Server.Spells
|
|||
{
|
||||
public abstract class MagerySpell : Spell
|
||||
{
|
||||
private const double ChanceOffset = 20.0, ChanceLength = 100.0 / 7.0;
|
||||
|
||||
private static readonly int[] m_ManaTable = { 4, 6, 9, 11, 14, 20, 40, 50 };
|
||||
private static readonly int[] _manaTable = { 4, 6, 9, 11, 14, 20, 40, 50 };
|
||||
private static readonly double[] _requiredSkill = Core.ML ?
|
||||
new[] { 0.0, -4.0, 10.0, 24.0, 38.0, 52.0, 66.0, 80.0 } :
|
||||
new[] { 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0 };
|
||||
|
||||
public MagerySpell(Mobile caster, Item scroll, SpellInfo info) : base(caster, scroll, info)
|
||||
{
|
||||
|
|
@ -29,13 +30,20 @@ namespace Server.Spells
|
|||
circle -= 2;
|
||||
}
|
||||
|
||||
var avg = ChanceLength * circle;
|
||||
// Original RunUO algorithm for required skill
|
||||
// const double chanceOffset = 20.0
|
||||
// const double chanceLength = 100.0 / 7.0
|
||||
// var avg = chanceLength * circle;
|
||||
// min = avg - chanceOffset;
|
||||
// max = avg + chanceOffset;
|
||||
|
||||
min = avg - ChanceOffset;
|
||||
max = avg + ChanceOffset;
|
||||
// Correct algorithm according to OSI.
|
||||
// TODO: Verify this algorithm on OSI for latest expansion.
|
||||
min = _requiredSkill[circle];
|
||||
max = min + 40;
|
||||
}
|
||||
|
||||
public override int GetMana() => Scroll is BaseWand ? 0 : m_ManaTable[(int)Circle];
|
||||
public override int GetMana() => Scroll is BaseWand ? 0 : _manaTable[(int)Circle];
|
||||
|
||||
public override double GetResistSkill(Mobile m)
|
||||
{
|
||||
|
|
@ -79,12 +87,13 @@ namespace Server.Spells
|
|||
|
||||
public virtual double GetResistPercentForCircle(Mobile target, SpellCircle circle)
|
||||
{
|
||||
var firstPercent = target.Skills.MagicResist.Value / 5.0;
|
||||
var secondPercent = target.Skills.MagicResist.Value -
|
||||
var magicResist = target.Skills.MagicResist.Value;
|
||||
var firstPercent = magicResist / 5.0;
|
||||
var secondPercent = magicResist -
|
||||
((Caster.Skills[CastSkill].Value - 20.0) / 5.0 + (1 + (int)circle) * 5.0);
|
||||
|
||||
return (firstPercent > secondPercent ? firstPercent : secondPercent) /
|
||||
2.0; // Seems should be about half of what stratics says.
|
||||
// Seems should be about half of what stratics says.
|
||||
return (firstPercent > secondPercent ? firstPercent : secondPercent) / 2.0;
|
||||
}
|
||||
|
||||
public virtual double GetResistPercent(Mobile target) => GetResistPercentForCircle(target, Circle);
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ namespace Server.Spells
|
|||
{
|
||||
public abstract class SpecialMove
|
||||
{
|
||||
private static readonly Dictionary<Mobile, SpecialMoveContext> m_PlayersTable =
|
||||
new();
|
||||
private static readonly Dictionary<Mobile, SpecialMoveContext> _playersTable = new();
|
||||
|
||||
public virtual int BaseMana => 0;
|
||||
|
||||
|
|
@ -159,40 +158,18 @@ namespace Server.Spells
|
|||
return false;
|
||||
}
|
||||
|
||||
string option = null;
|
||||
|
||||
if (this is Backstab)
|
||||
string option = this switch
|
||||
{
|
||||
option = "Backstab";
|
||||
}
|
||||
else if (this is DeathStrike)
|
||||
{
|
||||
option = "Death Strike";
|
||||
}
|
||||
else if (this is FocusAttack)
|
||||
{
|
||||
option = "Focus Attack";
|
||||
}
|
||||
else if (this is KiAttack)
|
||||
{
|
||||
option = "Ki Attack";
|
||||
}
|
||||
else if (this is SurpriseAttack)
|
||||
{
|
||||
option = "Surprise Attack";
|
||||
}
|
||||
else if (this is HonorableExecution)
|
||||
{
|
||||
option = "Honorable Execution";
|
||||
}
|
||||
else if (this is LightningStrike)
|
||||
{
|
||||
option = "Lightning Strike";
|
||||
}
|
||||
else if (this is MomentumStrike)
|
||||
{
|
||||
option = "Momentum Strike";
|
||||
}
|
||||
Backstab => "Backstab",
|
||||
DeathStrike => "Death Strike",
|
||||
FocusAttack => "Focus Attack",
|
||||
KiAttack => "Ki Attack",
|
||||
SurpriseAttack => "Surprise Attack",
|
||||
HonorableExecution => "Honorable Execution",
|
||||
LightningStrike => "Lightning Strike",
|
||||
MomentumStrike => "Momentum Strike",
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (option != null && !DuelContext.AllowSpecialMove(from, option, this))
|
||||
{
|
||||
|
|
@ -303,7 +280,7 @@ namespace Server.Spells
|
|||
|
||||
private static void AddContext(Mobile m, SpecialMoveContext context)
|
||||
{
|
||||
m_PlayersTable[m] = context;
|
||||
_playersTable[m] = context;
|
||||
}
|
||||
|
||||
private static void RemoveContext(Mobile m)
|
||||
|
|
@ -312,23 +289,20 @@ namespace Server.Spells
|
|||
|
||||
if (context != null)
|
||||
{
|
||||
m_PlayersTable.Remove(m);
|
||||
_playersTable.Remove(m);
|
||||
|
||||
context.Timer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static SpecialMoveContext GetContext(Mobile m) =>
|
||||
m_PlayersTable.TryGetValue(m, out var context) ? context : null;
|
||||
_playersTable.TryGetValue(m, out var context) ? context : null;
|
||||
|
||||
private class SpecialMoveTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public SpecialMoveTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0))
|
||||
{
|
||||
m_Mobile = from;
|
||||
}
|
||||
public SpecialMoveTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) => m_Mobile = from;
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ namespace Server.Spells
|
|||
|
||||
public static void Turn(Mobile from, object to)
|
||||
{
|
||||
if (!(to is IPoint3D target))
|
||||
if (to is not IPoint3D target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -333,7 +333,7 @@ namespace Server.Spells
|
|||
public static bool AddStatCurse(Mobile caster, Mobile target, StatType type, int curse, TimeSpan duration)
|
||||
{
|
||||
var offset = -curse;
|
||||
var name = $"[Magic] {type} Offset";
|
||||
var name = $"[Magic] {type} Curse";
|
||||
|
||||
var mod = target.GetStatMod(name);
|
||||
|
||||
|
|
@ -447,26 +447,8 @@ namespace Server.Spells
|
|||
var bcFrom = from as BaseCreature;
|
||||
var bcTarg = to as BaseCreature;
|
||||
|
||||
PlayerMobile pmFrom;
|
||||
PlayerMobile pmTarg;
|
||||
|
||||
if (bcFrom?.Summoned == true)
|
||||
{
|
||||
pmFrom = bcFrom.SummonMaster as PlayerMobile;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmFrom = from as PlayerMobile;
|
||||
}
|
||||
|
||||
if (bcTarg?.Summoned == true)
|
||||
{
|
||||
pmTarg = bcTarg.SummonMaster as PlayerMobile;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmTarg = to as PlayerMobile;
|
||||
}
|
||||
var pmFrom = (bcFrom?.Summoned == true ? bcFrom.SummonMaster : from) as PlayerMobile;
|
||||
var pmTarg = (bcTarg?.Summoned == true ? bcTarg.SummonMaster : to) as PlayerMobile;
|
||||
|
||||
if (pmFrom?.DuelContext != null && pmFrom.DuelContext == pmTarg?.DuelContext && pmFrom.DuelContext.Started &&
|
||||
pmFrom.DuelPlayer != null && pmTarg?.DuelPlayer != null)
|
||||
|
|
@ -864,17 +846,6 @@ namespace Server.Spells
|
|||
return x < 0 || y < 0 || x >= map.Width || y >= map.Height;
|
||||
}
|
||||
|
||||
// towns
|
||||
public static bool IsTown(IPoint3D ip, Mobile caster)
|
||||
{
|
||||
if (ip is Item item)
|
||||
{
|
||||
ip = item.GetWorldLocation();
|
||||
}
|
||||
|
||||
return IsTown(new Point3D(ip), caster);
|
||||
}
|
||||
|
||||
public static bool IsTown(Point3D loc, Mobile caster)
|
||||
{
|
||||
var map = caster.Map;
|
||||
|
|
@ -897,15 +868,8 @@ namespace Server.Spells
|
|||
return reg?.IsDisabled() == false;
|
||||
}
|
||||
|
||||
public static bool CheckTown(IPoint3D ip, Mobile caster)
|
||||
{
|
||||
if (ip is Item item)
|
||||
{
|
||||
ip = item.GetWorldLocation();
|
||||
}
|
||||
|
||||
return CheckTown(new Point3D(ip), caster);
|
||||
}
|
||||
public static bool CheckTown(IPoint3D ip, Mobile caster) =>
|
||||
CheckTown((ip as Item)?.GetWorldLocation() ?? new Point3D(ip), caster);
|
||||
|
||||
public static bool CheckTown(Point3D loc, Mobile caster)
|
||||
{
|
||||
|
|
@ -919,13 +883,13 @@ namespace Server.Spells
|
|||
}
|
||||
|
||||
// magic reflection
|
||||
public static void CheckReflect(int circle, Mobile caster, ref Mobile target)
|
||||
{
|
||||
public static bool CheckReflect(int circle, Mobile caster, ref Mobile target) =>
|
||||
CheckReflect(circle, ref caster, ref target);
|
||||
}
|
||||
|
||||
public static void CheckReflect(int circle, ref Mobile caster, ref Mobile target)
|
||||
public static bool CheckReflect(int circle, ref Mobile caster, ref Mobile target)
|
||||
{
|
||||
var reflect = false;
|
||||
|
||||
if (target.MagicDamageAbsorb > 0)
|
||||
{
|
||||
++circle;
|
||||
|
|
@ -933,41 +897,26 @@ namespace Server.Spells
|
|||
target.MagicDamageAbsorb -= circle;
|
||||
|
||||
// This order isn't very intuitive, but you have to nullify reflect before target gets switched
|
||||
|
||||
var reflect = target.MagicDamageAbsorb >= 0;
|
||||
|
||||
(target as BaseCreature)?.CheckReflect(caster, ref reflect);
|
||||
|
||||
reflect = target.MagicDamageAbsorb >= 0;
|
||||
if (target.MagicDamageAbsorb <= 0)
|
||||
{
|
||||
target.MagicDamageAbsorb = 0;
|
||||
DefensiveSpell.Nullify(target);
|
||||
}
|
||||
|
||||
if (reflect)
|
||||
{
|
||||
target.FixedEffect(0x37B9, 10, 5);
|
||||
|
||||
var temp = caster;
|
||||
caster = target;
|
||||
target = temp;
|
||||
}
|
||||
}
|
||||
else if (target is BaseCreature creature)
|
||||
|
||||
if (target is BaseCreature creature)
|
||||
{
|
||||
var reflect = false;
|
||||
|
||||
creature.CheckReflect(caster, ref reflect);
|
||||
|
||||
if (reflect)
|
||||
{
|
||||
creature.FixedEffect(0x37B9, 10, 5);
|
||||
|
||||
var temp = caster;
|
||||
caster = creature;
|
||||
target = temp;
|
||||
}
|
||||
}
|
||||
|
||||
if (reflect)
|
||||
{
|
||||
target.FixedEffect(0x37B9, 10, 5);
|
||||
(caster, target) = (target, caster);
|
||||
}
|
||||
|
||||
return reflect;
|
||||
}
|
||||
|
||||
public static void Damage(Spell spell, Mobile target, double damage)
|
||||
|
|
@ -995,20 +944,21 @@ namespace Server.Spells
|
|||
{
|
||||
(from as BaseCreature)?.AlterSpellDamageTo(target, ref iDamage);
|
||||
|
||||
(target as BaseCreature)?.AlterSpellDamageFrom(from, ref iDamage);
|
||||
var bcTarget = target as BaseCreature;
|
||||
bcTarget?.AlterSpellDamageFrom(from, ref iDamage);
|
||||
|
||||
target.Damage(iDamage, from);
|
||||
|
||||
if (from != null)
|
||||
{
|
||||
bcTarget?.OnHarmfulSpell(from);
|
||||
bcTarget?.OnDamagedBySpell(from);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new SpellDamageTimer(spell, target, from, iDamage, delay).Start();
|
||||
}
|
||||
|
||||
if (target is BaseCreature c && from != null && delay == TimeSpan.Zero)
|
||||
{
|
||||
c.OnHarmfulSpell(from);
|
||||
c.OnDamagedBySpell(from);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Damage(
|
||||
|
|
@ -1086,6 +1036,11 @@ namespace Server.Spells
|
|||
|
||||
public static void DoLeech(int damageGiven, Mobile from, Mobile target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = TransformationSpellHelper.GetContext(from);
|
||||
|
||||
if (context == null) /* cleanup */
|
||||
|
|
@ -1134,7 +1089,7 @@ namespace Server.Spells
|
|||
m_Damage = damage;
|
||||
m_Spell = s;
|
||||
|
||||
if (m_Spell?.DelayedDamage == true && !m_Spell.DelayedDamageStacking)
|
||||
if (m_Spell?.DelayedDamage == true)
|
||||
{
|
||||
m_Spell.StartDelayedDamageContext(target, this);
|
||||
}
|
||||
|
|
@ -1143,7 +1098,6 @@ namespace Server.Spells
|
|||
protected override void OnTick()
|
||||
{
|
||||
(m_From as BaseCreature)?.AlterSpellDamageTo(m_Target, ref m_Damage);
|
||||
|
||||
(m_Target as BaseCreature)?.AlterSpellDamageFrom(m_From, ref m_Damage);
|
||||
|
||||
m_Target.Damage(m_Damage);
|
||||
|
|
@ -1181,7 +1135,8 @@ namespace Server.Spells
|
|||
m_Chaos = chaos;
|
||||
m_DFA = dfa;
|
||||
m_Spell = s;
|
||||
if (m_Spell?.DelayedDamage == true && !m_Spell.DelayedDamageStacking)
|
||||
|
||||
if (m_Spell?.DelayedDamage == true)
|
||||
{
|
||||
m_Spell.StartDelayedDamageContext(target, this);
|
||||
}
|
||||
|
|
@ -1189,10 +1144,9 @@ namespace Server.Spells
|
|||
|
||||
protected override void OnTick()
|
||||
{
|
||||
var bcFrom = m_From as BaseCreature;
|
||||
var bcTarg = m_Target as BaseCreature;
|
||||
|
||||
if (bcFrom != null && m_Target != null)
|
||||
if (m_From is BaseCreature bcFrom && m_Target != null)
|
||||
{
|
||||
bcFrom.AlterSpellDamageTo(m_Target, ref m_Damage);
|
||||
}
|
||||
|
|
@ -1253,7 +1207,7 @@ namespace Server.Spells
|
|||
|
||||
public static bool OnCast(Mobile caster, Spell spell)
|
||||
{
|
||||
if (!(spell is ITransformationSpell transformSpell))
|
||||
if (spell is not ITransformationSpell transformSpell)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,9 +51,8 @@ namespace Server.Spells.Eighth
|
|||
}
|
||||
else if (m.Region?.IsPartOf("Khaldun") == true)
|
||||
{
|
||||
Caster.SendLocalizedMessage(
|
||||
1010395
|
||||
); // The veil of death in this area is too strong and resists thy efforts to restore life.
|
||||
// The veil of death in this area is too strong and resists thy efforts to restore life.
|
||||
Caster.SendLocalizedMessage(1010395);
|
||||
}
|
||||
else if (CheckBSequence(m, true))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ namespace Server.Spells.First
|
|||
|
||||
SpellHelper.CheckReflect((int)Circle, Caster, ref m);
|
||||
|
||||
// TODO: StoneForm immunity
|
||||
|
||||
SpellHelper.AddStatCurse(Caster, m, StatType.Int);
|
||||
|
||||
m.Spell?.OnCasterHurt();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.First
|
||||
|
|
@ -18,7 +19,7 @@ namespace Server.Spells.First
|
|||
|
||||
public override SpellCircle Circle => SpellCircle.First;
|
||||
|
||||
public override bool DelayedDamageStacking => !Core.AOS;
|
||||
public override Type[] DelayedDamageSpellFamilyStacking => AOSNoDelayedDamageStackingSelf;
|
||||
|
||||
public override bool DelayedDamage => true;
|
||||
|
||||
|
|
@ -30,6 +31,12 @@ namespace Server.Spells.First
|
|||
|
||||
SpellHelper.Turn(source, m);
|
||||
|
||||
if (Core.SA && HasDelayedDamageContext(m))
|
||||
{
|
||||
DoHurtFizzle();
|
||||
return;
|
||||
}
|
||||
|
||||
SpellHelper.CheckReflect((int)Circle, ref source, ref m);
|
||||
|
||||
double damage;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Server.Collections;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
|
|
@ -34,37 +33,39 @@ namespace Server.Spells.Fourth
|
|||
|
||||
SpellHelper.GetSurfaceTop(ref p);
|
||||
|
||||
var targets = new List<Mobile>();
|
||||
|
||||
var map = Caster.Map;
|
||||
var directTarget = p as Mobile;
|
||||
var loc = new Point3D(p);
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
using var pool = PooledRefQueue<Mobile>.Create();
|
||||
var directTarget = p as Mobile;
|
||||
var loc = new Point3D(p);
|
||||
|
||||
var feluccaRules = map.Rules == MapRules.FeluccaRules;
|
||||
|
||||
// You can target any living mobile directly, beneficial checks apply
|
||||
if (directTarget != null && Caster.CanBeBeneficial(directTarget, false))
|
||||
{
|
||||
targets.Add(directTarget);
|
||||
pool.Enqueue(directTarget);
|
||||
}
|
||||
|
||||
var eable = map.GetMobilesInRange(loc, 2);
|
||||
targets.AddRange(eable.Where(m => m != directTarget).Where(m => AreaCanTarget(m, feluccaRules)));
|
||||
foreach (var m in eable)
|
||||
{
|
||||
if (m != directTarget && AreaCanTarget(m, feluccaRules))
|
||||
{
|
||||
pool.Enqueue(m);
|
||||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
Effects.PlaySound(loc, Caster.Map, 0x299);
|
||||
Effects.PlaySound(loc, Caster.Map, 0x299);
|
||||
|
||||
if (targets.Count > 0)
|
||||
{
|
||||
var cured = 0;
|
||||
|
||||
for (var i = 0; i < targets.Count; ++i)
|
||||
while (pool.Count > 0)
|
||||
{
|
||||
var m = targets[i];
|
||||
var m = pool.Dequeue();
|
||||
|
||||
Caster.DoBeneficial(m);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Server.Spells.Fourth
|
|||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
private static readonly Dictionary<Mobile, int> _Table = new();
|
||||
private static readonly Dictionary<Mobile, int> _table = new();
|
||||
|
||||
public ArchProtectionSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info)
|
||||
{
|
||||
|
|
@ -107,12 +107,12 @@ namespace Server.Spells.Fourth
|
|||
|
||||
private static void AddEntry(Mobile m, int v)
|
||||
{
|
||||
_Table[m] = v;
|
||||
_table[m] = v;
|
||||
}
|
||||
|
||||
public static void RemoveEntry(Mobile m)
|
||||
{
|
||||
if (_Table.Remove(m, out var v))
|
||||
if (_table.Remove(m, out var v))
|
||||
{
|
||||
m.EndAction<ArchProtectionSpell>();
|
||||
m.VirtualArmorMod -= Math.Min(v, m.VirtualArmorMod);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Server.Spells.Fourth
|
|||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
private static readonly HashSet<Mobile> m_UnderEffect = new();
|
||||
private static readonly HashSet<Mobile> _underEffect = new();
|
||||
|
||||
public CurseSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info)
|
||||
{
|
||||
|
|
@ -41,7 +41,7 @@ namespace Server.Spells.Fourth
|
|||
if (Caster.Player && m.Player /*&& Caster != m */ && !UnderEffect(m))
|
||||
{
|
||||
var duration = SpellHelper.GetDuration(Caster, m);
|
||||
m_UnderEffect.Add(m);
|
||||
_underEffect.Add(m);
|
||||
Timer.StartTimer(duration, () => RemoveEffect(m));
|
||||
m.UpdateResistances();
|
||||
}
|
||||
|
|
@ -71,13 +71,13 @@ namespace Server.Spells.Fourth
|
|||
Caster.Target = new SpellTargetMobile(this, TargetFlags.Harmful, Core.ML ? 10 : 12);
|
||||
}
|
||||
|
||||
public static void RemoveEffect(Mobile m)
|
||||
public static bool RemoveEffect(Mobile m)
|
||||
{
|
||||
m_UnderEffect.Remove(m);
|
||||
|
||||
var effectRemoved = _underEffect.Remove(m);
|
||||
m.UpdateResistances();
|
||||
return effectRemoved;
|
||||
}
|
||||
|
||||
public static bool UnderEffect(Mobile m) => m_UnderEffect.Contains(m);
|
||||
public static bool UnderEffect(Mobile m) => _underEffect.Contains(m);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Server.Spells.Second
|
|||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new SpellTargetItem(this, range: Core.ML ? 10 : 12);
|
||||
Caster.SendMessage("What do you wish to untrap?"); // TODO: Localization?
|
||||
Caster.SendLocalizedMessage(502368);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server.Spells.Seventh
|
||||
{
|
||||
|
|
@ -27,89 +26,77 @@ namespace Server.Spells.Seventh
|
|||
|
||||
public void Target(IPoint3D p)
|
||||
{
|
||||
if (SpellHelper.CheckTown(p, Caster) && CheckSequence())
|
||||
var loc = (p as Item)?.GetWorldLocation() ?? new Point3D(p);
|
||||
if (SpellHelper.CheckTown(loc, Caster) && CheckSequence())
|
||||
{
|
||||
SpellHelper.Turn(Caster, p);
|
||||
|
||||
if (p is Item item)
|
||||
{
|
||||
p = item.GetWorldLocation();
|
||||
}
|
||||
|
||||
var targets = new List<Mobile>();
|
||||
|
||||
var map = Caster.Map;
|
||||
|
||||
var playerVsPlayer = false;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
var eable = map.GetMobilesInRange(new Point3D(p), 2);
|
||||
using var pool = PooledRefQueue<Mobile>.Create();
|
||||
var pvp = false;
|
||||
|
||||
targets.AddRange(
|
||||
eable.Where(
|
||||
m =>
|
||||
{
|
||||
if (Core.AOS && (m == Caster || !Caster.InLOS(m)) ||
|
||||
!SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
!Caster.CanBeHarmful(m, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.Player)
|
||||
{
|
||||
playerVsPlayer = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.ToList()
|
||||
);
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
double damage;
|
||||
|
||||
damage = Core.AOS
|
||||
? GetNewAosDamage(51, 1, 5, playerVsPlayer)
|
||||
: Utility.Random(27, 22);
|
||||
|
||||
if (targets.Count > 0)
|
||||
{
|
||||
if (Core.AOS && targets.Count > 2)
|
||||
var eable = map.GetMobilesInRange(loc, 2);
|
||||
foreach (var m in eable)
|
||||
{
|
||||
damage = damage * 2 / targets.Count;
|
||||
}
|
||||
else if (!Core.AOS)
|
||||
{
|
||||
damage /= targets.Count;
|
||||
}
|
||||
|
||||
for (var i = 0; i < targets.Count; ++i)
|
||||
{
|
||||
var toDeal = damage;
|
||||
var m = targets[i];
|
||||
|
||||
if (!Core.AOS && CheckResisted(m))
|
||||
if (Core.AOS && (m == Caster || !Caster.InLOS(m)) ||
|
||||
!SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
!Caster.CanBeHarmful(m, false))
|
||||
{
|
||||
toDeal *= 0.5;
|
||||
|
||||
m.SendLocalizedMessage(501783); // You feel yourself resisting magical energy.
|
||||
continue;
|
||||
}
|
||||
|
||||
toDeal *= GetDamageScalar(m);
|
||||
Caster.DoHarmful(m);
|
||||
SpellHelper.Damage(this, m, toDeal, 0, 0, 0, 0, 100);
|
||||
if (m.Player)
|
||||
{
|
||||
pvp = true;
|
||||
}
|
||||
|
||||
m.BoltEffect(0);
|
||||
pool.Enqueue(m);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
if (pool.Count > 0)
|
||||
{
|
||||
double damage = Core.AOS
|
||||
? GetNewAosDamage(51, 1, 5, pvp)
|
||||
: Utility.Random(27, 22);
|
||||
|
||||
if (pool.Count > 2)
|
||||
{
|
||||
if (Core.AOS)
|
||||
{
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
damage /= pool.Count;
|
||||
}
|
||||
|
||||
while (pool.Count > 0)
|
||||
{
|
||||
var toDeal = damage;
|
||||
var m = pool.Dequeue();
|
||||
|
||||
if (!Core.AOS && CheckResisted(m))
|
||||
{
|
||||
toDeal *= 0.5;
|
||||
|
||||
m.SendLocalizedMessage(501783); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
toDeal *= GetDamageScalar(m);
|
||||
Caster.DoHarmful(m);
|
||||
SpellHelper.Damage(this, m, toDeal, 0, 0, 0, 0, 100);
|
||||
|
||||
m.BoltEffect(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.PlaySound(0x29);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.PlaySound(0x29);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,10 +45,8 @@ namespace Server.Spells.Seventh
|
|||
}
|
||||
else
|
||||
{
|
||||
duration = TimeSpan.FromSeconds(
|
||||
Caster.Skills.Magery.Value * 0.28 +
|
||||
2.0
|
||||
); // (28% of magery) + 2.0 seconds
|
||||
// (28% of magery) + 2.0 seconds
|
||||
duration = TimeSpan.FromSeconds(Caster.Skills.Magery.Value * 0.28 + 2.0);
|
||||
}
|
||||
|
||||
var itemID = eastToWest ? 0x3946 : 0x3956;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server.Spells.Seventh
|
||||
{
|
||||
|
|
@ -36,8 +35,6 @@ namespace Server.Spells.Seventh
|
|||
p = item.GetWorldLocation();
|
||||
}
|
||||
|
||||
List<Mobile> targets;
|
||||
|
||||
var map = Caster.Map;
|
||||
|
||||
var playerVsPlayer = false;
|
||||
|
|
@ -46,69 +43,63 @@ namespace Server.Spells.Seventh
|
|||
if (map != null)
|
||||
{
|
||||
var eable = map.GetMobilesInRange(loc, 2);
|
||||
|
||||
targets = eable.Where(
|
||||
m =>
|
||||
{
|
||||
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
!Caster.CanBeHarmful(m, false) ||
|
||||
Core.AOS && !Caster.InLOS(m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.Player)
|
||||
{
|
||||
playerVsPlayer = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.ToList();
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
else
|
||||
{
|
||||
targets = new List<Mobile>();
|
||||
}
|
||||
|
||||
double damage = Core.AOS
|
||||
? GetNewAosDamage(51, 1, 5, playerVsPlayer)
|
||||
: Utility.Random(27, 22);
|
||||
|
||||
if (targets.Count > 0)
|
||||
{
|
||||
Effects.PlaySound(loc, Caster.Map, 0x160);
|
||||
|
||||
if (Core.AOS && targets.Count > 2)
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
{
|
||||
damage = damage * 2 / targets.Count;
|
||||
}
|
||||
else if (!Core.AOS)
|
||||
{
|
||||
damage /= targets.Count;
|
||||
}
|
||||
|
||||
for (var i = 0; i < targets.Count; ++i)
|
||||
{
|
||||
var m = targets[i];
|
||||
|
||||
var toDeal = damage;
|
||||
|
||||
if (!Core.AOS && CheckResisted(m))
|
||||
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
!Caster.CanBeHarmful(m, false) || Core.AOS && !Caster.InLOS(m))
|
||||
{
|
||||
damage *= 0.5;
|
||||
|
||||
m.SendLocalizedMessage(501783); // You feel yourself resisting magical energy.
|
||||
continue;
|
||||
}
|
||||
|
||||
toDeal *= GetDamageScalar(m);
|
||||
Caster.DoHarmful(m);
|
||||
SpellHelper.Damage(this, m, toDeal, 0, 100, 0, 0, 0);
|
||||
if (m.Player)
|
||||
{
|
||||
playerVsPlayer = true;
|
||||
}
|
||||
|
||||
Caster.MovingParticles(m, 0x36D4, 7, 0, false, true, 9501, 1, 0, 0x100);
|
||||
queue.Enqueue(m);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
double damage = Core.AOS
|
||||
? GetNewAosDamage(51, 1, 5, playerVsPlayer)
|
||||
: Utility.Random(27, 22);
|
||||
|
||||
int count = queue.Count;
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
Effects.PlaySound(loc, Caster.Map, 0x160);
|
||||
|
||||
if (Core.AOS && count > 2)
|
||||
{
|
||||
damage = damage * 2 / count;
|
||||
}
|
||||
else if (!Core.AOS)
|
||||
{
|
||||
damage /= count;
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var m = queue.Dequeue();
|
||||
|
||||
var toDeal = damage;
|
||||
|
||||
if (!Core.AOS && CheckResisted(m))
|
||||
{
|
||||
damage *= 0.5;
|
||||
|
||||
m.SendLocalizedMessage(501783); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
toDeal *= GetDamageScalar(m);
|
||||
Caster.DoHarmful(m);
|
||||
SpellHelper.Damage(this, m, toDeal, 0, 100, 0, 0, 0);
|
||||
|
||||
Caster.MovingParticles(m, 0x36D4, 7, 0, false, true, 9501, 1, 0, 0x100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,22 +20,26 @@ namespace Server.Spells.Sixth
|
|||
|
||||
public override SpellCircle Circle => SpellCircle.Sixth;
|
||||
|
||||
public override bool DelayedDamageStacking => !Core.AOS;
|
||||
public override Type[] DelayedDamageSpellFamilyStacking => AOSNoDelayedDamageStackingSelf;
|
||||
|
||||
public override bool DelayedDamage => false;
|
||||
|
||||
public void Target(Mobile m)
|
||||
{
|
||||
if (Core.SA && HasDelayedDamageContext(m))
|
||||
{
|
||||
DoHurtFizzle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Caster.CanBeHarmful(m) && CheckSequence())
|
||||
{
|
||||
Mobile attacker = Caster, defender = m;
|
||||
Mobile defender = m;
|
||||
|
||||
SpellHelper.Turn(Caster, m);
|
||||
|
||||
SpellHelper.CheckReflect((int)Circle, Caster, ref m);
|
||||
|
||||
var t = new InternalTimer(this, attacker, defender, m);
|
||||
t.Start();
|
||||
var t = new InternalTimer(this, Caster, defender, m).Start();
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
|
|
@ -48,52 +52,52 @@ namespace Server.Spells.Sixth
|
|||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Attacker;
|
||||
private readonly Mobile m_Defender;
|
||||
private readonly MagerySpell m_Spell;
|
||||
private readonly Mobile m_Target;
|
||||
private readonly Mobile _attacker;
|
||||
private readonly Mobile _defender;
|
||||
private readonly MagerySpell _spell;
|
||||
private readonly Mobile _target;
|
||||
|
||||
public InternalTimer(MagerySpell spell, Mobile attacker, Mobile defender, Mobile target)
|
||||
: base(TimeSpan.FromSeconds(Core.AOS ? 3.0 : 2.5))
|
||||
{
|
||||
m_Spell = spell;
|
||||
m_Attacker = attacker;
|
||||
m_Defender = defender;
|
||||
m_Target = target;
|
||||
_spell = spell;
|
||||
_attacker = attacker;
|
||||
_defender = defender;
|
||||
_target = target;
|
||||
|
||||
m_Spell?.StartDelayedDamageContext(attacker, this);
|
||||
_spell?.StartDelayedDamageContext(_attacker, this);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Attacker.HarmfulCheck(m_Defender))
|
||||
if (_attacker.HarmfulCheck(_defender))
|
||||
{
|
||||
double damage;
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
damage = m_Spell.GetNewAosDamage(40, 1, 5, m_Defender);
|
||||
damage = _spell.GetNewAosDamage(40, 1, 5, _defender);
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = Utility.Random(23, 22);
|
||||
|
||||
if (m_Spell.CheckResisted(m_Target))
|
||||
if (_spell.CheckResisted(_target))
|
||||
{
|
||||
damage *= 0.75;
|
||||
|
||||
m_Target.SendLocalizedMessage(501783); // You feel yourself resisting magical energy.
|
||||
_target.SendLocalizedMessage(501783); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
damage *= m_Spell.GetDamageScalar(m_Target);
|
||||
damage *= _spell.GetDamageScalar(_target);
|
||||
}
|
||||
|
||||
m_Target.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
|
||||
m_Target.PlaySound(0x307);
|
||||
_target.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
|
||||
_target.PlaySound(0x307);
|
||||
|
||||
SpellHelper.Damage(m_Spell, m_Target, damage, 0, 100, 0, 0, 0);
|
||||
SpellHelper.Damage(_spell, _target, damage, 0, 100, 0, 0, 0);
|
||||
|
||||
m_Spell?.RemoveDelayedDamageContext(m_Attacker);
|
||||
_spell?.RemoveDelayedDamageContext(_attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,24 +43,13 @@ namespace Server.Spells.Third
|
|||
{
|
||||
if (Caster.InRange(m, 2))
|
||||
{
|
||||
var total = (Caster.Skills.Magery.Fixed + Caster.Skills.Poisoning.Fixed) / 2;
|
||||
|
||||
if (total >= 1000)
|
||||
level = (Caster.Skills.Magery.Fixed + Caster.Skills.Poisoning.Fixed) / 2 switch
|
||||
{
|
||||
level = 3;
|
||||
}
|
||||
else if (total > 850)
|
||||
{
|
||||
level = 2;
|
||||
}
|
||||
else if (total > 650)
|
||||
{
|
||||
level = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
>= 1000 => 3,
|
||||
> 850 => 2,
|
||||
> 650 => 1,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue