chore(content): Cleans up abilities and some spell timers (#427)
- [X] Exposes Index/Count on the timer. - [X] Cleans up abilities - [X] Combines some spell context/info objects with their timers to reduce allocations - [X] Fixes a bug where immolating weapon both finishes effect or stops in the wrong order due to a race condition.
This commit is contained in:
parent
9ef7752beb
commit
178f71c6dc
33 changed files with 469 additions and 677 deletions
|
|
@ -15,10 +15,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063347,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063347, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Server.Items
|
|||
/// </summary>
|
||||
public class BleedAttack : WeaponAbility
|
||||
{
|
||||
private static readonly Dictionary<Mobile, Timer> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, Timer> _table = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
|
||||
|
|
@ -46,9 +46,9 @@ namespace Server.Items
|
|||
defender.NonlocalOverheadMessage(
|
||||
MessageType.Regular,
|
||||
0x21,
|
||||
1060758,
|
||||
1060758, // ~1_NAME~ is bleeding profusely
|
||||
defender.Name
|
||||
); // ~1_NAME~ is bleeding profusely
|
||||
);
|
||||
}
|
||||
|
||||
defender.PlaySound(0x133);
|
||||
|
|
@ -57,15 +57,15 @@ namespace Server.Items
|
|||
BeginBleed(defender, attacker);
|
||||
}
|
||||
|
||||
public static bool IsBleeding(Mobile m) => m_Table.ContainsKey(m);
|
||||
public static bool IsBleeding(Mobile m) => _table.ContainsKey(m);
|
||||
|
||||
public static void BeginBleed(Mobile m, Mobile from)
|
||||
{
|
||||
m_Table.TryGetValue(m, out var t);
|
||||
t?.Stop();
|
||||
_table.TryGetValue(m, out var timer);
|
||||
timer?.Stop();
|
||||
|
||||
m_Table[m] = t = new InternalTimer(from, m);
|
||||
t.Start();
|
||||
_table[m] = timer = new InternalTimer(from, m);
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public static void DoBleed(Mobile m, Mobile from, int level)
|
||||
|
|
@ -93,7 +93,7 @@ namespace Server.Items
|
|||
|
||||
public static void EndBleed(Mobile m, bool message)
|
||||
{
|
||||
if (!m_Table.Remove(m, out var t))
|
||||
if (!_table.Remove(m, out var t))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -110,9 +110,8 @@ namespace Server.Items
|
|||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly Mobile m_Mobile;
|
||||
private int m_Count;
|
||||
|
||||
public InternalTimer(Mobile from, Mobile m) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0))
|
||||
public InternalTimer(Mobile from, Mobile m) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), 5)
|
||||
{
|
||||
m_From = from;
|
||||
m_Mobile = m;
|
||||
|
|
@ -121,9 +120,9 @@ namespace Server.Items
|
|||
|
||||
protected override void OnTick()
|
||||
{
|
||||
DoBleed(m_Mobile, m_From, 5 - m_Count);
|
||||
DoBleed(m_Mobile, m_From, 5 - Index);
|
||||
|
||||
if (++m_Count == 5)
|
||||
if (Index == 4)
|
||||
{
|
||||
EndBleed(m_Mobile, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Server.Items
|
|||
/// </summary>
|
||||
public class Block : WeaponAbility
|
||||
{
|
||||
private static readonly Dictionary<Mobile, BlockInfo> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, InternalTimer> _table = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
|
||||
|
|
@ -16,10 +16,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063347,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063347, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +48,7 @@ namespace Server.Items
|
|||
|
||||
public static bool GetBonus(Mobile targ, ref int bonus)
|
||||
{
|
||||
if (!m_Table.TryGetValue(targ, out var info))
|
||||
if (!_table.TryGetValue(targ, out var info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -62,35 +60,25 @@ namespace Server.Items
|
|||
public static void BeginBlock(Mobile m, int bonus)
|
||||
{
|
||||
EndBlock(m);
|
||||
m_Table[m] = new BlockInfo(m, bonus);
|
||||
_table[m] = new InternalTimer(m, bonus);
|
||||
}
|
||||
|
||||
public static void EndBlock(Mobile m)
|
||||
{
|
||||
if (m_Table.Remove(m, out var info))
|
||||
if (_table.Remove(m, out var timer))
|
||||
{
|
||||
info.m_Timer?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockInfo
|
||||
{
|
||||
public readonly int m_Bonus;
|
||||
public readonly Timer m_Timer;
|
||||
|
||||
public BlockInfo(Mobile target, int bonus)
|
||||
{
|
||||
m_Bonus = bonus;
|
||||
m_Timer = new InternalTimer(target);
|
||||
timer?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
public readonly int m_Bonus;
|
||||
|
||||
public InternalTimer(Mobile m) : base(TimeSpan.FromSeconds(6.0))
|
||||
public InternalTimer(Mobile m, int bonus) : base(TimeSpan.FromSeconds(6.0))
|
||||
{
|
||||
m_Bonus = bonus;
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ namespace Server.Items
|
|||
/// </summary>
|
||||
public class DefenseMastery : WeaponAbility
|
||||
{
|
||||
private static readonly Dictionary<Mobile, DefenseMasteryInfo>
|
||||
m_Table = new();
|
||||
private static readonly Dictionary<Mobile, DefenseMasteryInfo> _table = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
|
||||
|
|
@ -18,10 +17,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063347,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063347, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +43,7 @@ namespace Server.Items
|
|||
((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) -
|
||||
50.0) / 70.0));
|
||||
|
||||
if (m_Table.TryGetValue(attacker, out var info))
|
||||
if (_table.TryGetValue(attacker, out var info))
|
||||
{
|
||||
EndDefense(info);
|
||||
}
|
||||
|
|
@ -57,14 +54,14 @@ namespace Server.Items
|
|||
info = new DefenseMasteryInfo(attacker, 80 - modifier, mod);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(3.0), EndDefense, info);
|
||||
|
||||
m_Table[attacker] = info;
|
||||
_table[attacker] = info;
|
||||
|
||||
attacker.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
|
||||
public static bool GetMalus(Mobile targ, ref int damageMalus)
|
||||
{
|
||||
if (!m_Table.TryGetValue(targ, out var info))
|
||||
if (!_table.TryGetValue(targ, out var info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -84,7 +81,7 @@ namespace Server.Items
|
|||
|
||||
// No message is sent to the player.
|
||||
|
||||
m_Table.Remove(info.m_From);
|
||||
_table.Remove(info.m_From);
|
||||
|
||||
info.m_From.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,17 +17,17 @@ namespace Server.Items
|
|||
{
|
||||
if (!base.CheckSkills( from ))
|
||||
return false;
|
||||
|
||||
|
||||
if (!(from.Weapon is Fists))
|
||||
return true;
|
||||
|
||||
|
||||
Skill skill = from.Skills.ArmsLore;
|
||||
|
||||
|
||||
if (skill?.Base >= 80.0)
|
||||
return true;
|
||||
|
||||
|
||||
from.SendLocalizedMessage( 1061812 ); // You lack the required skill in armslore to perform that attack!
|
||||
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker))
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ namespace Server.Items
|
|||
{
|
||||
attacker.SendLocalizedMessage(1060849); // Your target is already unarmed!
|
||||
}
|
||||
else if (CheckMana(attacker, true))
|
||||
else
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060092); // You disarm their weapon!
|
||||
defender.SendLocalizedMessage(1060093); // Your weapon has been disarmed!
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker))
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -60,11 +60,6 @@ namespace Server.Items
|
|||
return;
|
||||
}
|
||||
|
||||
if (!CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Core.ML && attacker is LesserHiryu && Utility.RandomDouble() <= 0.8)
|
||||
{
|
||||
return; // Lesser Hiryu have an 80% chance of missing this attack
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker))
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ namespace Server.Items
|
|||
{
|
||||
attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent.
|
||||
}
|
||||
else if (CheckMana(attacker, true))
|
||||
else
|
||||
{
|
||||
// attacker.SendLocalizedMessage( 1060092 ); // You disarm their weapon!
|
||||
defender.SendLocalizedMessage(1062002); // You can no longer wear your ~1_ARMOR~
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063347,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063347, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063352,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063352, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063347,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063347, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063347,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063347, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +29,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker)) // Mana check after check that there are targets
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker))
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -39,14 +39,8 @@ namespace Server.Items
|
|||
|
||||
if (p == null || weapon.PoisonCharges <= 0)
|
||||
{
|
||||
attacker.SendLocalizedMessage(
|
||||
1061141
|
||||
); // Your weapon must have a dose of poison to perform an infectious strike!
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckMana(attacker, true))
|
||||
{
|
||||
// Your weapon must have a dose of poison to perform an infectious strike!
|
||||
attacker.SendLocalizedMessage(1061141);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Server.Items
|
|||
public static readonly TimeSpan PlayerDuration = TimeSpan.FromSeconds(6.0);
|
||||
public static readonly TimeSpan NPCDuration = TimeSpan.FromSeconds(12.0);
|
||||
|
||||
private static readonly Dictionary<Mobile, InternalTimer> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, Timer> _table = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
|
||||
|
|
@ -39,16 +39,16 @@ namespace Server.Items
|
|||
}
|
||||
}
|
||||
|
||||
public static bool IsWounded(Mobile m) => m_Table.ContainsKey(m);
|
||||
public static bool IsWounded(Mobile m) => _table.ContainsKey(m);
|
||||
|
||||
public static void BeginWound(Mobile m, TimeSpan duration)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (_table.TryGetValue(m, out var timer))
|
||||
{
|
||||
timer?.Stop();
|
||||
}
|
||||
|
||||
m_Table[m] = timer = new InternalTimer(m, duration);
|
||||
_table[m] = timer = Timer.DelayCall(duration, EndWound, m);
|
||||
timer.Start();
|
||||
|
||||
m.YellowHealthbar = true;
|
||||
|
|
@ -56,7 +56,7 @@ namespace Server.Items
|
|||
|
||||
public static void EndWound(Mobile m)
|
||||
{
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
if (_table.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
}
|
||||
|
|
@ -64,21 +64,5 @@ namespace Server.Items
|
|||
m.YellowHealthbar = false;
|
||||
m.SendLocalizedMessage(1060208); // You are no longer mortally wounded.
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public InternalTimer(Mobile m, TimeSpan duration) : base(duration)
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndWound(m_Mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1070768,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
from.SendLocalizedMessage(1070768, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Server.Items
|
|||
|
||||
public static readonly TimeSpan FreezeDelayDuration = TimeSpan.FromSeconds(8.0);
|
||||
|
||||
private static readonly Dictionary<Mobile, InternalTimer> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, Timer> _table = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
|
||||
|
|
@ -80,41 +80,25 @@ namespace Server.Items
|
|||
BeginImmunity(defender, duration + FreezeDelayDuration);
|
||||
}
|
||||
|
||||
public static bool IsImmune(Mobile m) => m_Table.ContainsKey(m);
|
||||
public static bool IsImmune(Mobile m) => _table.ContainsKey(m);
|
||||
|
||||
public static void BeginImmunity(Mobile m, TimeSpan duration)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
if (_table.TryGetValue(m, out var timer))
|
||||
{
|
||||
timer?.Stop();
|
||||
}
|
||||
|
||||
m_Table[m] = timer = new InternalTimer(m, duration);
|
||||
_table[m] = timer = Timer.DelayCall(duration, EndImmunity, m);
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public static void EndImmunity(Mobile m)
|
||||
{
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
if (_table.Remove(m, out var timer))
|
||||
{
|
||||
timer?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public InternalTimer(Mobile m, TimeSpan duration) : base(duration)
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndImmunity(m_Mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1070768,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
from.SendLocalizedMessage(1070768, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Server.Items
|
|||
/// </summary>
|
||||
public class TalonStrike : WeaponAbility
|
||||
{
|
||||
private static readonly HashSet<Mobile> m_Table = new();
|
||||
private static readonly HashSet<Mobile> _defenders = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
public override double DamageScalar => 1.2;
|
||||
|
|
@ -17,10 +17,8 @@ namespace Server.Items
|
|||
{
|
||||
if (GetSkill(from, SkillName.Ninjitsu) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1063352,
|
||||
"50"
|
||||
); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
|
||||
// You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
|
||||
from.SendLocalizedMessage(1063352, "50");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +27,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (m_Table.Contains(defender) || !Validate(attacker) || !CheckMana(attacker, true))
|
||||
if (_defenders.Contains(defender) || !Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -48,7 +46,7 @@ namespace Server.Items
|
|||
|
||||
timer.Start();
|
||||
|
||||
m_Table.Add(defender);
|
||||
_defenders.Add(defender);
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
|
|
@ -58,12 +56,12 @@ namespace Server.Items
|
|||
private double m_DamageRemaining;
|
||||
private double m_DamageToDo;
|
||||
|
||||
public InternalTimer(Mobile defender, int totalDamage)
|
||||
: base(
|
||||
TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(0.25),
|
||||
12
|
||||
) // 3 seconds at .25 seconds apart = 12. Confirm delay inbetween of .25 each.
|
||||
// 3 seconds at 0.25 seconds apart = 12. Confirm delay in between of 0.25 each.
|
||||
public InternalTimer(Mobile defender, int totalDamage) : base(
|
||||
TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(0.25),
|
||||
12
|
||||
)
|
||||
{
|
||||
m_Defender = defender;
|
||||
m_DamageRemaining = totalDamage;
|
||||
|
|
@ -77,7 +75,7 @@ namespace Server.Items
|
|||
if (!m_Defender.Alive || m_DamageRemaining <= 0)
|
||||
{
|
||||
Stop();
|
||||
m_Table.Remove(m_Defender);
|
||||
_defenders.Remove(m_Defender);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +99,7 @@ namespace Server.Items
|
|||
if (!m_Defender.Alive || m_DamageRemaining <= 0)
|
||||
{
|
||||
Stop();
|
||||
m_Table.Remove(m_Defender);
|
||||
_defenders.Remove(m_Defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,10 +185,8 @@ namespace Server.Items
|
|||
|
||||
if (Core.ML && reqTactics && from.Skills.Tactics.Base < reqSkill)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1079308,
|
||||
reqSkill.ToString()
|
||||
); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
||||
// You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
||||
from.SendLocalizedMessage(1079308, reqSkill.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -208,17 +206,13 @@ namespace Server.Items
|
|||
|
||||
if (reqTactics)
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1079308,
|
||||
reqSkill.ToString()
|
||||
); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
||||
// You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
||||
from.SendLocalizedMessage(1079308, reqSkill.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(
|
||||
1060182,
|
||||
reqSkill.ToString()
|
||||
); // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack
|
||||
// You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack
|
||||
from.SendLocalizedMessage(1060182, reqSkill.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -239,10 +233,8 @@ namespace Server.Items
|
|||
return true;
|
||||
}
|
||||
|
||||
from.SendLocalizedMessage(
|
||||
1060181,
|
||||
mana.ToString()
|
||||
); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack
|
||||
// You need ~1_MANA_REQUIREMENT~ mana to perform that attack
|
||||
from.SendLocalizedMessage(1060181, mana.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Server.Items
|
|||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker))
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -33,11 +33,6 @@ namespace Server.Items
|
|||
return;
|
||||
}
|
||||
|
||||
if (!CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attacker.FixedEffect(0x3728, 10, 15);
|
||||
attacker.PlaySound(0x2A1);
|
||||
|
||||
|
|
|
|||
|
|
@ -838,10 +838,9 @@ namespace Server.Spells
|
|||
|
||||
public void Add(Mobile m, Timer t)
|
||||
{
|
||||
if (m_Contexts.TryGetValue(m, out var oldTimer))
|
||||
if (m_Contexts.Remove(m, out var oldTimer))
|
||||
{
|
||||
oldTimer.Stop();
|
||||
m_Contexts.Remove(m);
|
||||
}
|
||||
|
||||
m_Contexts.Add(m, t);
|
||||
|
|
@ -850,6 +849,7 @@ namespace Server.Spells
|
|||
public void Remove(Mobile m)
|
||||
{
|
||||
m_Contexts.Remove(m);
|
||||
// TODO: Should we stop the timer?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,9 +78,7 @@ namespace Server.Spells.Bushido
|
|||
m_Table.TryGetValue(m, out var timer);
|
||||
timer?.Stop();
|
||||
|
||||
m_Table[m] = timer = new InternalTimer(m);
|
||||
|
||||
timer.Start();
|
||||
m_Table[m] = Timer.DelayCall(TimeSpan.FromSeconds(30.0), EndCountering, m);
|
||||
}
|
||||
|
||||
public static void StopCountering(Mobile m)
|
||||
|
|
@ -93,21 +91,10 @@ namespace Server.Spells.Bushido
|
|||
OnEffectEnd(m, typeof(CounterAttack));
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
private static void EndCountering(Mobile m)
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public InternalTimer(Mobile m) : base(TimeSpan.FromSeconds(30.0))
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
StopCountering(m_Mobile);
|
||||
m_Mobile.SendLocalizedMessage(1063119); // You return to your normal stance.
|
||||
}
|
||||
StopCountering(m);
|
||||
m.SendLocalizedMessage(1063119); // You return to your normal stance.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ namespace Server.Spells.Bushido
|
|||
{
|
||||
public class HonorableExecution : SamuraiMove
|
||||
{
|
||||
private static readonly Dictionary<Mobile, HonorableExecutionInfo> m_Table =
|
||||
new();
|
||||
private static readonly Dictionary<Mobile, HonorableExecutionTimer> m_Table = new();
|
||||
|
||||
public override int BaseMana => 0;
|
||||
public override double RequiredSkill => 25.0;
|
||||
|
|
@ -14,13 +13,9 @@ namespace Server.Spells.Bushido
|
|||
public override TextDefinition AbilityMessage =>
|
||||
new(1063122); // You better kill your enemy with your next hit or you'll be rather sorry...
|
||||
|
||||
public override double GetDamageScalar(Mobile attacker, Mobile defender)
|
||||
{
|
||||
var bushido = attacker.Skills.Bushido.Value;
|
||||
|
||||
public override double GetDamageScalar(Mobile attacker, Mobile defender) =>
|
||||
// TODO: 20 -> Perfection
|
||||
return 1.0 + bushido * 20 / 10000;
|
||||
}
|
||||
1.0 + attacker.Skills.Bushido.Value * 20 / 10000;
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
|
|
@ -30,27 +25,20 @@ namespace Server.Spells.Bushido
|
|||
}
|
||||
|
||||
ClearCurrentMove(attacker);
|
||||
|
||||
if (m_Table.TryGetValue(attacker, out var info))
|
||||
{
|
||||
info.Clear();
|
||||
info.m_Timer?.Stop();
|
||||
}
|
||||
RemovePenalty(attacker);
|
||||
|
||||
if (!defender.Alive)
|
||||
{
|
||||
attacker.FixedParticles(0x373A, 1, 17, 0x7E2, EffectLayer.Waist);
|
||||
|
||||
var bushido = attacker.Skills.Bushido.Value;
|
||||
bushido *= bushido;
|
||||
|
||||
attacker.Hits += 20 + (int)(bushido * bushido / 480.0);
|
||||
attacker.Hits += 20 + (int)(bushido / 480.0);
|
||||
|
||||
var swingBonus = Math.Max((int)(bushido * bushido / 720.0), 1);
|
||||
var swingBonus = Math.Max(1, (int)(bushido / 720.0));
|
||||
|
||||
info = new HonorableExecutionInfo(attacker, swingBonus);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(20.0), RemovePenalty, info.m_Mobile);
|
||||
|
||||
m_Table[attacker] = info;
|
||||
m_Table[attacker] = new HonorableExecutionTimer(attacker, swingBonus);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -70,12 +58,10 @@ namespace Server.Spells.Bushido
|
|||
mods.Add(new DefaultSkillMod(SkillName.MagicResist, true, -resSpells));
|
||||
}
|
||||
|
||||
info = new HonorableExecutionInfo(attacker, mods);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(7.0), RemovePenalty, info.m_Mobile);
|
||||
|
||||
m_Table[attacker] = info;
|
||||
m_Table[attacker] = new HonorableExecutionTimer(attacker, mods);
|
||||
}
|
||||
|
||||
attacker.Delta(MobileDelta.WeaponDamage);
|
||||
CheckGain(attacker);
|
||||
}
|
||||
|
||||
|
|
@ -85,28 +71,30 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public static void RemovePenalty(Mobile target)
|
||||
{
|
||||
if (!m_Table.Remove(target, out var info) || !info.m_Penalty)
|
||||
if (m_Table.Remove(target, out var timer))
|
||||
{
|
||||
return;
|
||||
timer.Clear();
|
||||
}
|
||||
|
||||
info.Clear();
|
||||
info.m_Timer?.Stop();
|
||||
}
|
||||
|
||||
private class HonorableExecutionInfo
|
||||
private class HonorableExecutionTimer : Timer
|
||||
{
|
||||
public readonly Mobile m_Mobile;
|
||||
public readonly List<object> m_Mods;
|
||||
public readonly bool m_Penalty;
|
||||
public readonly int m_SwingBonus;
|
||||
public Timer m_Timer;
|
||||
|
||||
public HonorableExecutionInfo(Mobile from, List<object> mods) : this(from, 0, mods, mods != null)
|
||||
public HonorableExecutionTimer(Mobile from, List<object> mods) : this(TimeSpan.FromSeconds(7.0), from, 0, mods, mods != null)
|
||||
{
|
||||
}
|
||||
|
||||
public HonorableExecutionInfo(Mobile from, int swingBonus, List<object> mods = null, bool penalty = false)
|
||||
public HonorableExecutionTimer(Mobile from, int swingBonus) : this(TimeSpan.FromSeconds(20.0), from, swingBonus)
|
||||
{
|
||||
}
|
||||
|
||||
public HonorableExecutionTimer(
|
||||
TimeSpan duration, Mobile from, int swingBonus, List<object> mods = null, bool penalty = false)
|
||||
: base(duration)
|
||||
{
|
||||
m_Mobile = from;
|
||||
m_SwingBonus = swingBonus;
|
||||
|
|
@ -116,6 +104,12 @@ namespace Server.Spells.Bushido
|
|||
Apply();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile?.Delta(MobileDelta.WeaponDamage);
|
||||
RemovePenalty(m_Mobile);
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (m_Mods == null)
|
||||
|
|
@ -140,6 +134,8 @@ namespace Server.Spells.Bushido
|
|||
|
||||
public void Clear()
|
||||
{
|
||||
Stop();
|
||||
|
||||
if (m_Mods == null)
|
||||
{
|
||||
return;
|
||||
|
|
@ -151,11 +147,11 @@ namespace Server.Spells.Bushido
|
|||
|
||||
if (mod is ResistanceMod resistanceMod)
|
||||
{
|
||||
m_Mobile.RemoveResistanceMod(resistanceMod);
|
||||
m_Mobile?.RemoveResistanceMod(resistanceMod);
|
||||
}
|
||||
else if (mod is SkillMod skillMod)
|
||||
{
|
||||
m_Mobile.RemoveSkillMod(skillMod);
|
||||
m_Mobile?.RemoveSkillMod(skillMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Server.Spells.Fifth
|
|||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
private static readonly Dictionary<Mobile, InternalTimer> m_Timers = new();
|
||||
private static readonly Dictionary<Mobile, Timer> m_Table = new();
|
||||
|
||||
public IncognitoSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info)
|
||||
{
|
||||
|
|
@ -55,24 +55,34 @@ namespace Server.Spells.Fifth
|
|||
if (Sigil.ExistsOn(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1010445); // You cannot incognito if you have a sigil
|
||||
return;
|
||||
}
|
||||
else if (!Caster.CanBeginAction<IncognitoSpell>())
|
||||
|
||||
if (!Caster.CanBeginAction<IncognitoSpell>())
|
||||
{
|
||||
Caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
return;
|
||||
}
|
||||
else if (Caster.BodyMod == 183 || Caster.BodyMod == 184)
|
||||
|
||||
if (Caster.BodyMod == 183 || Caster.BodyMod == 184)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1042402); // You cannot use incognito while wearing body paint
|
||||
return;
|
||||
}
|
||||
else if (DisguiseTimers.IsDisguised(Caster))
|
||||
|
||||
if (DisguiseTimers.IsDisguised(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061631); // You can't do that while disguised.
|
||||
return;
|
||||
}
|
||||
else if (!Caster.CanBeginAction<PolymorphSpell>() || Caster.IsBodyMod)
|
||||
|
||||
if (!Caster.CanBeginAction<PolymorphSpell>() || Caster.IsBodyMod)
|
||||
{
|
||||
DoFizzle();
|
||||
return;
|
||||
}
|
||||
else if (CheckSequence())
|
||||
|
||||
if (CheckSequence())
|
||||
{
|
||||
if (Caster.BeginAction<IncognitoSpell>())
|
||||
{
|
||||
|
|
@ -98,19 +108,10 @@ namespace Server.Spells.Fifth
|
|||
|
||||
StopTimer(Caster);
|
||||
|
||||
var timeVal = 6 * Caster.Skills.Magery.Fixed / 50 + 1;
|
||||
|
||||
if (timeVal > 144)
|
||||
{
|
||||
timeVal = 144;
|
||||
}
|
||||
var timeVal = Math.Min(6 * Caster.Skills.Magery.Fixed / 50 + 1, 144);
|
||||
|
||||
var length = TimeSpan.FromSeconds(timeVal);
|
||||
|
||||
var t = new InternalTimer(Caster, length);
|
||||
m_Timers[Caster] = t;
|
||||
|
||||
t.Start();
|
||||
m_Table[Caster] = Timer.DelayCall(length, EndIncognito, Caster);
|
||||
|
||||
BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.Incognito, 1075819, length, Caster));
|
||||
}
|
||||
|
|
@ -125,52 +126,31 @@ namespace Server.Spells.Fifth
|
|||
|
||||
public static void StopTimer(Mobile m)
|
||||
{
|
||||
if (!m_Timers.TryGetValue(m, out var t))
|
||||
if (m_Table.Remove(m, out var t))
|
||||
{
|
||||
t.Stop();
|
||||
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Incognito);
|
||||
}
|
||||
|
||||
private static void EndIncognito(Mobile m)
|
||||
{
|
||||
if (m.CanBeginAction<IncognitoSpell>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
t.Stop();
|
||||
m_Timers.Remove(m);
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Incognito);
|
||||
}
|
||||
(m as PlayerMobile)?.SetHairMods(-1, -1);
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Owner;
|
||||
m.BodyMod = 0;
|
||||
m.HueMod = -1;
|
||||
m.NameMod = null;
|
||||
m.EndAction<IncognitoSpell>();
|
||||
|
||||
public InternalTimer(Mobile owner, TimeSpan length) : base(length)
|
||||
{
|
||||
m_Owner = owner;
|
||||
|
||||
/*
|
||||
int val = ((6 * owner.Skills.Magery.Fixed) / 50) + 1;
|
||||
|
||||
if (val > 144)
|
||||
val = 144;
|
||||
|
||||
Delay = TimeSpan.FromSeconds( val );
|
||||
* */
|
||||
Priority = TimerPriority.OneSecond;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Owner.CanBeginAction<IncognitoSpell>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(m_Owner as PlayerMobile)?.SetHairMods(-1, -1);
|
||||
|
||||
m_Owner.BodyMod = 0;
|
||||
m_Owner.HueMod = -1;
|
||||
m_Owner.NameMod = null;
|
||||
m_Owner.EndAction<IncognitoSpell>();
|
||||
|
||||
BaseArmor.ValidateMobile(m_Owner);
|
||||
BaseClothing.ValidateMobile(m_Owner);
|
||||
}
|
||||
BaseArmor.ValidateMobile(m);
|
||||
BaseClothing.ValidateMobile(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ namespace Server.Spells.Mysticism
|
|||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
private static readonly Dictionary<Mobile, SpellPlagueContext>
|
||||
m_Table = new();
|
||||
private static readonly Dictionary<Mobile, SpellPlagueTimer> m_Table = new();
|
||||
|
||||
public SpellPlagueSpell(Mobile caster, Item scroll = null)
|
||||
: base(caster, scroll, m_Info)
|
||||
|
|
@ -67,16 +66,16 @@ namespace Server.Spells.Mysticism
|
|||
var damage = GetNewAosDamage(33, 1, 5, targeted);
|
||||
SpellHelper.Damage(this, targeted, damage, 0, 0, 0, 0, 0);
|
||||
|
||||
var context = new SpellPlagueContext(this, targeted);
|
||||
var timer = new SpellPlagueTimer(this, targeted);
|
||||
|
||||
if (m_Table.TryGetValue(targeted, out var oldContext))
|
||||
if (m_Table.TryGetValue(targeted, out var oldtimer))
|
||||
{
|
||||
oldContext.SetNext(context);
|
||||
oldtimer.SetNext(timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Table[targeted] = context;
|
||||
context.Start();
|
||||
m_Table[targeted] = timer;
|
||||
timer.StartPlague();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,94 +113,86 @@ namespace Server.Spells.Mysticism
|
|||
to.FixedParticles(0x3779, 1, 15, 0x251E, 0x43, 7, EffectLayer.Head, 0);
|
||||
}
|
||||
|
||||
private class SpellPlagueContext
|
||||
private class SpellPlagueTimer : Timer
|
||||
{
|
||||
private readonly SpellPlagueSpell m_Owner;
|
||||
private readonly Mobile m_Target;
|
||||
private int m_Explosions;
|
||||
private DateTime m_LastExploded;
|
||||
private SpellPlagueContext m_Next;
|
||||
private Timer m_Timer;
|
||||
private SpellPlagueTimer m_Next;
|
||||
|
||||
public SpellPlagueContext(SpellPlagueSpell owner, Mobile target)
|
||||
public SpellPlagueTimer(SpellPlagueSpell owner, Mobile target) : base(TimeSpan.FromSeconds(8.0))
|
||||
{
|
||||
m_Owner = owner;
|
||||
m_Target = target;
|
||||
}
|
||||
|
||||
public void SetNext(SpellPlagueContext context)
|
||||
public void SetNext(SpellPlagueTimer timer)
|
||||
{
|
||||
if (m_Next == null)
|
||||
{
|
||||
m_Next = context;
|
||||
m_Next = timer;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Next.SetNext(context);
|
||||
m_Next.SetNext(timer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
public void StartPlague()
|
||||
{
|
||||
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(8.0), EndPlague);
|
||||
m_Timer.Start();
|
||||
|
||||
BuffInfo.AddBuff(
|
||||
m_Target,
|
||||
new BuffInfo(BuffIcon.SpellPlague, 1031690, 1080167, TimeSpan.FromSeconds(8.5), m_Target)
|
||||
);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
public void OnDamage()
|
||||
{
|
||||
if (DateTime.Now > m_LastExploded + TimeSpan.FromSeconds(2.0))
|
||||
if (DateTime.Now <= m_LastExploded + TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
var exploChance = 90 - m_Explosions * 30;
|
||||
return;
|
||||
}
|
||||
|
||||
var resist = m_Target.Skills.MagicResist.Value;
|
||||
var exploChance = 90 - m_Explosions * 30;
|
||||
|
||||
if (resist >= 70)
|
||||
var resist = m_Target.Skills.MagicResist.Value;
|
||||
|
||||
if (resist >= 70)
|
||||
{
|
||||
exploChance -= (int)((resist - 70.0) * 3.0 / 10.0);
|
||||
}
|
||||
|
||||
if (exploChance > Utility.Random(100))
|
||||
{
|
||||
m_Owner.VisualEffect(m_Target);
|
||||
|
||||
var damage = m_Owner.GetNewAosDamage(15 + m_Explosions * 3, 1, 5, m_Target);
|
||||
|
||||
m_Explosions++;
|
||||
m_LastExploded = DateTime.Now;
|
||||
|
||||
SpellHelper.Damage(m_Owner, m_Target, damage, 0, 0, 0, 0, 0, 100);
|
||||
|
||||
if (m_Explosions >= 3)
|
||||
{
|
||||
exploChance -= (int)((resist - 70.0) * 3.0 / 10.0);
|
||||
}
|
||||
|
||||
if (exploChance > Utility.Random(100))
|
||||
{
|
||||
m_Owner.VisualEffect(m_Target);
|
||||
|
||||
var damage = m_Owner.GetNewAosDamage(15 + m_Explosions * 3, 1, 5, m_Target);
|
||||
|
||||
m_Explosions++;
|
||||
m_LastExploded = DateTime.Now;
|
||||
|
||||
SpellHelper.Damage(m_Owner, m_Target, damage, 0, 0, 0, 0, 0, 100);
|
||||
|
||||
if (m_Explosions >= 3)
|
||||
{
|
||||
EndPlague();
|
||||
}
|
||||
EndPlague();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EndPlague()
|
||||
public void EndPlague(bool restart = true)
|
||||
{
|
||||
EndPlague(true);
|
||||
}
|
||||
|
||||
public void EndPlague(bool restart)
|
||||
{
|
||||
m_Timer?.Stop();
|
||||
|
||||
if (restart && m_Next != null)
|
||||
{
|
||||
m_Table[m_Target] = m_Next;
|
||||
m_Next.Start();
|
||||
m_Next.StartPlague();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Table.Remove(m_Target);
|
||||
|
||||
BuffInfo.RemoveBuff(m_Target, BuffIcon.SpellPlague);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,14 +73,13 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
public static void ClearMindRotScalar(Mobile m)
|
||||
{
|
||||
if (!m_Table.Remove(m, out var tmpB))
|
||||
if (m_Table.Remove(m, out var tmpB))
|
||||
{
|
||||
return;
|
||||
tmpB.m_MRExpireTimer.Stop();
|
||||
m.SendLocalizedMessage(1060872); // Your mind feels normal again.
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Mindrot);
|
||||
tmpB.m_MRExpireTimer.Stop();
|
||||
m.SendLocalizedMessage(1060872); // Your mind feels normal again.
|
||||
}
|
||||
|
||||
public static bool HasMindRotScalar(Mobile m) => m_Table.ContainsKey(m);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Server.Items;
|
||||
using Server.SkillHandlers;
|
||||
|
||||
|
|
@ -7,7 +8,7 @@ namespace Server.Spells.Ninjitsu
|
|||
{
|
||||
public class DeathStrike : NinjaMove
|
||||
{
|
||||
private static readonly Dictionary<Mobile, DeathStrikeInfo> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, DeathStrikeTimer> m_Table = new();
|
||||
|
||||
public override int BaseMana => 30;
|
||||
public override double RequiredSkill => 85.0;
|
||||
|
|
@ -50,16 +51,16 @@ namespace Server.Spells.Ninjitsu
|
|||
|
||||
var damageBonus = 0;
|
||||
|
||||
if (m_Table.Remove(defender, out var info))
|
||||
if (m_Table.Remove(defender, out var timer))
|
||||
{
|
||||
defender.SendLocalizedMessage(1063092); // Your opponent lands another Death Strike!
|
||||
|
||||
if (info.m_Steps > 0)
|
||||
if (timer.Steps > 0)
|
||||
{
|
||||
damageBonus = attacker.Skills.Ninjitsu.Fixed / 150;
|
||||
}
|
||||
|
||||
info.m_Timer?.Stop();
|
||||
timer.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -71,113 +72,108 @@ namespace Server.Spells.Ninjitsu
|
|||
defender.FixedParticles(0x374A, 1, 17, 0x26BC, EffectLayer.Waist);
|
||||
attacker.PlaySound(attacker.Female ? 0x50D : 0x50E);
|
||||
|
||||
info = new DeathStrikeInfo(defender, attacker, damageBonus, isRanged)
|
||||
{
|
||||
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), ProcessDeathStrike, defender)
|
||||
};
|
||||
var t = new DeathStrikeTimer(defender, attacker, damageBonus, isRanged);
|
||||
|
||||
m_Table[defender] = info;
|
||||
m_Table[defender] = t;
|
||||
|
||||
t.Start();
|
||||
|
||||
CheckGain(attacker);
|
||||
}
|
||||
|
||||
public static void AddStep(Mobile m)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var info) && ++info.m_Steps >= 5)
|
||||
if (m_Table.TryGetValue(m, out var timer) && ++timer.Steps >= 5)
|
||||
{
|
||||
ProcessDeathStrike(m);
|
||||
timer.ProcessDeathStrike();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessDeathStrike(Mobile defender)
|
||||
{
|
||||
if (!m_Table.Remove(defender, out var info))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int damage;
|
||||
|
||||
var ninjitsu = info.m_Attacker.Skills.Ninjitsu.Value;
|
||||
var stalkingBonus = Tracking.GetStalkingBonus(info.m_Attacker, info.m_Target);
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
var scalar = (info.m_Attacker.Skills.Hiding.Value +
|
||||
info.m_Attacker.Skills.Stealth.Value) / 220;
|
||||
|
||||
if (scalar > 1)
|
||||
{
|
||||
scalar = 1;
|
||||
}
|
||||
|
||||
// New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30.
|
||||
if (info.m_Steps >= 5)
|
||||
{
|
||||
damage = (int)Math.Floor(Math.Min(60, ninjitsu / 3 * (0.3 + 0.7 * scalar) + stalkingBonus));
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = (int)Math.Floor(Math.Min(30, ninjitsu / 9 * (0.3 + 0.7 * scalar) + stalkingBonus));
|
||||
}
|
||||
|
||||
if (info.m_isRanged)
|
||||
{
|
||||
damage /= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var divisor = info.m_Steps >= 5 ? 30 : 80;
|
||||
var baseDamage = ninjitsu / divisor * 10;
|
||||
|
||||
var maxDamage = info.m_Steps >= 5 ? 62 : 22;
|
||||
damage = Math.Clamp((int)(baseDamage + stalkingBonus), 0, maxDamage) + info.m_DamageBonus;
|
||||
}
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
info.m_Target.Damage(damage, info.m_Attacker); // Damage is direct.
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(
|
||||
info.m_Target,
|
||||
info.m_Attacker,
|
||||
damage,
|
||||
true,
|
||||
100,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
); // Damage is physical.
|
||||
}
|
||||
|
||||
info.m_Timer?.Stop();
|
||||
}
|
||||
|
||||
private class DeathStrikeInfo
|
||||
private class DeathStrikeTimer : Timer
|
||||
{
|
||||
public readonly Mobile m_Attacker;
|
||||
public readonly int m_DamageBonus;
|
||||
public readonly bool m_isRanged;
|
||||
public readonly Mobile m_Target;
|
||||
public int m_Steps;
|
||||
public Timer m_Timer;
|
||||
public int Steps { get; set; }
|
||||
|
||||
public DeathStrikeInfo(Mobile target, Mobile attacker, int damageBonus, bool isRanged)
|
||||
internal DeathStrikeTimer(Mobile target, Mobile attacker, int damageBonus, bool isRanged)
|
||||
: base(TimeSpan.FromSeconds(5.0))
|
||||
{
|
||||
m_Target = target;
|
||||
m_Attacker = attacker;
|
||||
m_DamageBonus = damageBonus;
|
||||
m_isRanged = isRanged;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnTick()
|
||||
{
|
||||
ProcessDeathStrike();
|
||||
}
|
||||
|
||||
public void ProcessDeathStrike()
|
||||
{
|
||||
int damage;
|
||||
|
||||
var ninjitsu = m_Attacker.Skills.Ninjitsu.Value;
|
||||
var stalkingBonus = Tracking.GetStalkingBonus(m_Attacker, m_Target);
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
var scalar = Math.Min(1, (m_Attacker.Skills.Hiding.Value +
|
||||
m_Attacker.Skills.Stealth.Value) / 220);
|
||||
|
||||
// New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30.
|
||||
if (Steps >= 5)
|
||||
{
|
||||
damage = (int)Math.Floor(Math.Min(60, ninjitsu / 3 * (0.3 + 0.7 * scalar) + stalkingBonus));
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = (int)Math.Floor(Math.Min(30, ninjitsu / 9 * (0.3 + 0.7 * scalar) + stalkingBonus));
|
||||
}
|
||||
|
||||
if (m_isRanged)
|
||||
{
|
||||
damage /= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var divisor = Steps >= 5 ? 30 : 80;
|
||||
var baseDamage = ninjitsu / divisor * 10;
|
||||
|
||||
var maxDamage = Steps >= 5 ? 62 : 22;
|
||||
damage = Math.Clamp((int)(baseDamage + stalkingBonus), 0, maxDamage) + m_DamageBonus;
|
||||
}
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
m_Target.Damage(damage, m_Attacker); // Damage is direct.
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(
|
||||
m_Target,
|
||||
m_Attacker,
|
||||
damage,
|
||||
true,
|
||||
100,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
); // Damage is physical.
|
||||
}
|
||||
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace Server.Spells.Ninjitsu
|
|||
{
|
||||
public class KiAttack : NinjaMove
|
||||
{
|
||||
private static readonly Dictionary<Mobile, KiAttackInfo> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, KiAttackTimer> m_Table = new();
|
||||
|
||||
public override int BaseMana => 25;
|
||||
public override double RequiredSkill => 80.0;
|
||||
|
|
@ -21,10 +21,9 @@ namespace Server.Spells.Ninjitsu
|
|||
return;
|
||||
}
|
||||
|
||||
var info = new KiAttackInfo(from);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(2.0), EndKiAttack, info);
|
||||
|
||||
m_Table[from] = info;
|
||||
var t = new KiAttackTimer(from);
|
||||
m_Table[from] = t;
|
||||
t.Start();
|
||||
}
|
||||
|
||||
public override bool Validate(Mobile from)
|
||||
|
|
@ -87,53 +86,43 @@ namespace Server.Spells.Ninjitsu
|
|||
|
||||
public override void OnClearMove(Mobile from)
|
||||
{
|
||||
if (m_Table.Remove(from, out var info))
|
||||
if (m_Table.Remove(from, out var t))
|
||||
{
|
||||
info.m_Timer.Stop();
|
||||
t.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static double GetBonus(Mobile from)
|
||||
{
|
||||
if (!m_Table.TryGetValue(from, out var info))
|
||||
if (!m_Table.TryGetValue(from, out var t))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var xDelta = info.m_Location.X - from.X;
|
||||
var yDelta = info.m_Location.Y - from.Y;
|
||||
var xDelta = t.m_Location.X - from.X;
|
||||
var yDelta = t.m_Location.Y - from.Y;
|
||||
|
||||
var bonus = Math.Sqrt(xDelta * xDelta + yDelta * yDelta);
|
||||
|
||||
if (bonus > 20.0)
|
||||
{
|
||||
bonus = 20.0;
|
||||
}
|
||||
|
||||
return bonus;
|
||||
return Math.Min(Math.Sqrt(xDelta * xDelta + yDelta * yDelta), 20.0);
|
||||
}
|
||||
|
||||
private static void EndKiAttack(KiAttackInfo info)
|
||||
{
|
||||
info.m_Timer?.Stop();
|
||||
|
||||
ClearCurrentMove(info.m_Mobile);
|
||||
info.m_Mobile.SendLocalizedMessage(1063102); // You failed to complete your Ki Attack in time.
|
||||
|
||||
m_Table.Remove(info.m_Mobile);
|
||||
}
|
||||
|
||||
private class KiAttackInfo
|
||||
private class KiAttackTimer : Timer
|
||||
{
|
||||
public readonly Mobile m_Mobile;
|
||||
public Point3D m_Location;
|
||||
public Timer m_Timer;
|
||||
|
||||
public KiAttackInfo(Mobile m)
|
||||
public KiAttackTimer(Mobile m) : base(TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Location = m.Location;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
ClearCurrentMove(m_Mobile);
|
||||
m_Mobile.SendLocalizedMessage(1063102); // You failed to complete your Ki Attack in time.
|
||||
|
||||
m_Table.Remove(m_Mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Server.Spells.Seventh
|
|||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
private static readonly Dictionary<Mobile, InternalTimer> m_Timers = new();
|
||||
private static readonly Dictionary<Mobile, Timer> m_Table = new();
|
||||
|
||||
private readonly int m_NewBody;
|
||||
|
||||
|
|
@ -29,45 +29,47 @@ namespace Server.Spells.Seventh
|
|||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
/*if (Caster.Mounted)
|
||||
var caster = Caster;
|
||||
|
||||
/*if (caster.Mounted)
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1042561 ); //Please dismount first.
|
||||
caster.SendLocalizedMessage( 1042561 ); //Please dismount first.
|
||||
return false;
|
||||
}
|
||||
else */
|
||||
if (Sigil.ExistsOn(Caster))
|
||||
if (Sigil.ExistsOn(caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil
|
||||
caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TransformationSpellHelper.UnderTransformation(Caster))
|
||||
if (TransformationSpellHelper.UnderTransformation(caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form.
|
||||
caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DisguiseTimers.IsDisguised(Caster))
|
||||
if (DisguiseTimers.IsDisguised(caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised.
|
||||
caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Caster.BodyMod == 183 || Caster.BodyMod == 184)
|
||||
if (caster.BodyMod == 183 || caster.BodyMod == 184)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint
|
||||
caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Caster.CanBeginAction<PolymorphSpell>())
|
||||
if (!caster.CanBeginAction<PolymorphSpell>())
|
||||
{
|
||||
if (Core.ML)
|
||||
{
|
||||
EndPolymorph(Caster);
|
||||
EndPolymorph(caster);
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -75,9 +77,9 @@ namespace Server.Spells.Seventh
|
|||
|
||||
if (m_NewBody == 0)
|
||||
{
|
||||
var gump = Core.SE ? (Gump)new NewPolymorphGump(Caster, Scroll) : new PolymorphGump(Caster, Scroll);
|
||||
var gump = Core.SE ? (Gump)new NewPolymorphGump(caster, Scroll) : new PolymorphGump(caster, Scroll);
|
||||
|
||||
Caster.SendGump(gump);
|
||||
caster.SendGump(gump);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -86,51 +88,67 @@ namespace Server.Spells.Seventh
|
|||
|
||||
public override void OnCast()
|
||||
{
|
||||
/*if (Caster.Mounted)
|
||||
var caster = Caster;
|
||||
|
||||
/*if (caster.Mounted)
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1042561 ); //Please dismount first.
|
||||
}
|
||||
else */
|
||||
if (Sigil.ExistsOn(Caster))
|
||||
caster.SendLocalizedMessage(1042561); // Please dismount first.
|
||||
return;
|
||||
}*/
|
||||
|
||||
if (Sigil.ExistsOn(caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil
|
||||
caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil
|
||||
return;
|
||||
}
|
||||
else if (!Caster.CanBeginAction<PolymorphSpell>())
|
||||
|
||||
if (!caster.CanBeginAction<PolymorphSpell>())
|
||||
{
|
||||
if (Core.ML)
|
||||
{
|
||||
EndPolymorph(Caster);
|
||||
EndPolymorph(caster);
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (TransformationSpellHelper.UnderTransformation(Caster))
|
||||
|
||||
if (TransformationSpellHelper.UnderTransformation(caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form.
|
||||
caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form.
|
||||
return;
|
||||
}
|
||||
else if (DisguiseTimers.IsDisguised(Caster))
|
||||
|
||||
if (DisguiseTimers.IsDisguised(caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised.
|
||||
caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised.
|
||||
return;
|
||||
}
|
||||
else if (Caster.BodyMod == 183 || Caster.BodyMod == 184)
|
||||
|
||||
if (caster.BodyMod == 183 || caster.BodyMod == 184)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint
|
||||
caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint
|
||||
return;
|
||||
}
|
||||
else if (!Caster.CanBeginAction<IncognitoSpell>() || Caster.IsBodyMod)
|
||||
|
||||
if (!caster.CanBeginAction<IncognitoSpell>() || caster.IsBodyMod)
|
||||
{
|
||||
DoFizzle();
|
||||
return;
|
||||
}
|
||||
else if (CheckSequence())
|
||||
|
||||
if (CheckSequence())
|
||||
{
|
||||
if (Caster.BeginAction<PolymorphSpell>())
|
||||
if (caster.BeginAction<PolymorphSpell>())
|
||||
{
|
||||
if (m_NewBody != 0)
|
||||
{
|
||||
if (!((Body)m_NewBody).IsHuman)
|
||||
{
|
||||
var mt = Caster.Mount;
|
||||
var mt = caster.Mount;
|
||||
|
||||
if (mt != null)
|
||||
{
|
||||
|
|
@ -138,35 +156,33 @@ namespace Server.Spells.Seventh
|
|||
}
|
||||
}
|
||||
|
||||
Caster.BodyMod = m_NewBody;
|
||||
caster.BodyMod = m_NewBody;
|
||||
|
||||
if (m_NewBody == 400 || m_NewBody == 401)
|
||||
{
|
||||
Caster.HueMod = Caster.Race.RandomSkinHue();
|
||||
caster.HueMod = caster.Race.RandomSkinHue();
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.HueMod = 0;
|
||||
caster.HueMod = 0;
|
||||
}
|
||||
|
||||
BaseArmor.ValidateMobile(Caster);
|
||||
BaseClothing.ValidateMobile(Caster);
|
||||
BaseArmor.ValidateMobile(caster);
|
||||
BaseClothing.ValidateMobile(caster);
|
||||
|
||||
if (!Core.ML)
|
||||
{
|
||||
StopTimer(Caster);
|
||||
StopTimer(caster);
|
||||
|
||||
var timer = new InternalTimer(Caster);
|
||||
var duration = Math.Max((int)caster.Skills.Magery.Value, 120);
|
||||
|
||||
m_Timers[Caster] = timer;
|
||||
|
||||
timer.Start();
|
||||
m_Table[caster] = Timer.DelayCall(TimeSpan.FromSeconds(duration), EndPolymorph, caster);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
caster.SendLocalizedMessage(1005559); // This spell is already in effect.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,13 +191,10 @@ namespace Server.Spells.Seventh
|
|||
|
||||
public static void StopTimer(Mobile m)
|
||||
{
|
||||
if (!m_Timers.TryGetValue(m, out var timer))
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
return;
|
||||
timer.Stop();
|
||||
}
|
||||
|
||||
timer?.Stop();
|
||||
m_Timers.Remove(m);
|
||||
}
|
||||
|
||||
private static void EndPolymorph(Mobile m)
|
||||
|
|
@ -198,30 +211,5 @@ namespace Server.Spells.Seventh
|
|||
BaseArmor.ValidateMobile(m);
|
||||
BaseClothing.ValidateMobile(m);
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Owner;
|
||||
|
||||
public InternalTimer(Mobile owner) : base(TimeSpan.FromSeconds(0))
|
||||
{
|
||||
m_Owner = owner;
|
||||
|
||||
var val = (int)owner.Skills.Magery.Value;
|
||||
|
||||
if (val > 120)
|
||||
{
|
||||
val = 120;
|
||||
}
|
||||
|
||||
Delay = TimeSpan.FromSeconds(val);
|
||||
Priority = TimerPriority.OneSecond;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndPolymorph(m_Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,19 +62,21 @@ namespace Server.Spells.Sixth
|
|||
|
||||
var duration = TimeSpan.FromSeconds(1.2 * Caster.Skills.Magery.Fixed / 10);
|
||||
|
||||
Timer t = new InternalTimer(m, duration);
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.HidingAndOrStealth);
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Invisibility, 1075825, duration, m)); // Invisibility/Invisible
|
||||
|
||||
m_Table[m] = t;
|
||||
|
||||
t.Start();
|
||||
m_Table[m] = Timer.DelayCall(duration, EndInvisiblity, m);
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static void EndInvisiblity(Mobile m)
|
||||
{
|
||||
m.RevealingAction();
|
||||
RemoveTimer(m);
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if (DuelContext.CheckSuddenDeath(Caster))
|
||||
|
|
@ -100,22 +102,5 @@ namespace Server.Spells.Sixth
|
|||
t.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public InternalTimer(Mobile m, TimeSpan duration) : base(duration)
|
||||
{
|
||||
Priority = TimerPriority.OneSecond;
|
||||
m_Mobile = m;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile.RevealingAction();
|
||||
RemoveTimer(m_Mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Server.Spells.Spellweaving
|
|||
{
|
||||
private static readonly SpellInfo m_Info = new("Essence of Wind", "Anathrae", -1);
|
||||
|
||||
private static readonly Dictionary<Mobile, EssenceOfWindInfo> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, EssenceOfWindTimer> m_Table = new();
|
||||
|
||||
public EssenceOfWindSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info)
|
||||
{
|
||||
|
|
@ -53,7 +53,10 @@ namespace Server.Spells.Spellweaving
|
|||
continue;
|
||||
}
|
||||
|
||||
m_Table[m] = new EssenceOfWindInfo(m, fcMalus, ssiMalus, duration);
|
||||
var t = new EssenceOfWindTimer(m, fcMalus, ssiMalus, duration);
|
||||
t.Start();
|
||||
|
||||
m_Table[m] = t;
|
||||
|
||||
BuffInfo.AddBuff(
|
||||
m,
|
||||
|
|
@ -73,58 +76,44 @@ namespace Server.Spells.Spellweaving
|
|||
FinishSequence();
|
||||
}
|
||||
|
||||
public static int GetFCMalus(Mobile m) => m_Table.TryGetValue(m, out var info) ? info.FCMalus : 0;
|
||||
public static int GetFCMalus(Mobile m) => m_Table.TryGetValue(m, out var timer) ? timer._fcMalus : 0;
|
||||
|
||||
public static int GetSSIMalus(Mobile m) => m_Table.TryGetValue(m, out var info) ? info.SSIMalus : 0;
|
||||
public static int GetSSIMalus(Mobile m) => m_Table.TryGetValue(m, out var timer) ? timer._ssiMalus : 0;
|
||||
|
||||
public static bool IsDebuffed(Mobile m) => m_Table.ContainsKey(m);
|
||||
|
||||
public static void StopDebuffing(Mobile m, bool message)
|
||||
{
|
||||
if (m_Table.TryGetValue(m, out var info))
|
||||
if (m_Table.TryGetValue(m, out var timer))
|
||||
{
|
||||
info.Timer.DoExpire(message);
|
||||
timer.DoExpire(message);
|
||||
}
|
||||
}
|
||||
|
||||
private class EssenceOfWindInfo
|
||||
private class EssenceOfWindTimer : Timer
|
||||
{
|
||||
public EssenceOfWindInfo(Mobile defender, int fcMalus, int ssiMalus, TimeSpan duration)
|
||||
private readonly Mobile _defender;
|
||||
internal readonly int _fcMalus;
|
||||
internal readonly int _ssiMalus;
|
||||
|
||||
internal EssenceOfWindTimer(Mobile defender, int fcMalus, int ssiMalus, TimeSpan duration) : base(duration)
|
||||
{
|
||||
Defender = defender;
|
||||
FCMalus = fcMalus;
|
||||
SSIMalus = ssiMalus;
|
||||
|
||||
Timer = new ExpireTimer(Defender, duration);
|
||||
Timer.Start();
|
||||
_defender = defender;
|
||||
_fcMalus = fcMalus;
|
||||
_ssiMalus = ssiMalus;
|
||||
}
|
||||
|
||||
public Mobile Defender { get; }
|
||||
|
||||
public int FCMalus { get; }
|
||||
|
||||
public int SSIMalus { get; }
|
||||
|
||||
public ExpireTimer Timer { get; }
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public ExpireTimer(Mobile m, TimeSpan delay) : base(delay) => m_Mobile = m;
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
DoExpire(true);
|
||||
DoExpire();
|
||||
}
|
||||
|
||||
public void DoExpire(bool message)
|
||||
internal void DoExpire(bool message = true)
|
||||
{
|
||||
Stop();
|
||||
m_Table.Remove(m_Mobile);
|
||||
m_Table.Remove(_defender);
|
||||
|
||||
BuffInfo.RemoveBuff(m_Mobile, BuffIcon.EssenceOfWind);
|
||||
BuffInfo.RemoveBuff(_defender, BuffIcon.EssenceOfWind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Server.Spells.Spellweaving
|
|||
-1
|
||||
);
|
||||
|
||||
private static readonly Dictionary<Mobile, GiftOfRenewalInfo> m_Table = new();
|
||||
private static readonly Dictionary<Mobile, GiftOfRenewalTimer> m_Table = new();
|
||||
|
||||
public GiftOfRenewalSpell(Mobile caster, Item scroll = null)
|
||||
: base(caster, scroll, m_Info)
|
||||
|
|
@ -59,29 +59,19 @@ namespace Server.Spells.Spellweaving
|
|||
var skill = Caster.Skills.Spellweaving.Value;
|
||||
|
||||
var hitsPerRound = 5 + (int)(skill / 24) + FocusLevel;
|
||||
var duration = TimeSpan.FromSeconds(30 + FocusLevel * 10);
|
||||
var duration = 30 + FocusLevel * 10;
|
||||
|
||||
var info = new GiftOfRenewalInfo(Caster, m, hitsPerRound);
|
||||
var t = new GiftOfRenewalTimer(Caster, m, hitsPerRound, duration);
|
||||
|
||||
Timer.DelayCall(
|
||||
duration,
|
||||
() =>
|
||||
{
|
||||
if (StopEffect(m))
|
||||
{
|
||||
m.PlaySound(0x455);
|
||||
m.SendLocalizedMessage(1075071); // The Gift of Renewal has faded.
|
||||
}
|
||||
}
|
||||
);
|
||||
m_Table[m] = t;
|
||||
|
||||
m_Table[m] = info;
|
||||
t.Start();
|
||||
|
||||
Caster.BeginAction<GiftOfRenewalSpell>();
|
||||
|
||||
BuffInfo.AddBuff(
|
||||
m,
|
||||
new BuffInfo(BuffIcon.GiftOfRenewal, 1031602, 1075797, duration, m, hitsPerRound.ToString())
|
||||
new BuffInfo(BuffIcon.GiftOfRenewal, 1031602, 1075797, TimeSpan.FromSeconds(duration), m, hitsPerRound.ToString())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -96,48 +86,43 @@ namespace Server.Spells.Spellweaving
|
|||
|
||||
public static bool StopEffect(Mobile m)
|
||||
{
|
||||
if (!m_Table.Remove(m, out var info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
info.m_Timer.Stop();
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.GiftOfRenewal);
|
||||
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(60), info.m_Caster.EndAction<GiftOfRenewalSpell>);
|
||||
if (m_Table.Remove(m, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(60), timer.m_Caster.EndAction<GiftOfRenewalSpell>);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private class GiftOfRenewalInfo
|
||||
private class GiftOfRenewalTimer : Timer
|
||||
{
|
||||
public readonly Mobile m_Caster;
|
||||
public readonly int m_HitsPerRound;
|
||||
public readonly Mobile m_Mobile;
|
||||
public readonly InternalTimer m_Timer;
|
||||
|
||||
public GiftOfRenewalInfo(Mobile caster, Mobile mobile, int hitsPerRound)
|
||||
internal GiftOfRenewalTimer(Mobile caster, Mobile mobile, int hitsPerRound, int duration)
|
||||
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), duration / 2)
|
||||
{
|
||||
m_Caster = caster;
|
||||
m_Mobile = mobile;
|
||||
m_HitsPerRound = hitsPerRound;
|
||||
|
||||
m_Timer = new InternalTimer(this);
|
||||
m_Timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly GiftOfRenewalInfo m_GiftInfo;
|
||||
|
||||
public InternalTimer(GiftOfRenewalInfo info)
|
||||
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0)) =>
|
||||
m_GiftInfo = info;
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
var m = m_GiftInfo.m_Mobile;
|
||||
if (Index + 1 == Count)
|
||||
{
|
||||
StopEffect(m_Mobile);
|
||||
m_Mobile.PlaySound(0x455);
|
||||
m_Mobile.SendLocalizedMessage(1075071); // The Gift of Renewal has faded.
|
||||
return;
|
||||
}
|
||||
|
||||
var m = m_Mobile;
|
||||
|
||||
if (!m_Table.ContainsKey(m))
|
||||
{
|
||||
|
|
@ -157,9 +142,9 @@ namespace Server.Spells.Spellweaving
|
|||
return;
|
||||
}
|
||||
|
||||
var toHeal = m_GiftInfo.m_HitsPerRound;
|
||||
var toHeal = m_HitsPerRound;
|
||||
|
||||
SpellHelper.Heal(toHeal, m, m_GiftInfo.m_Caster);
|
||||
SpellHelper.Heal(toHeal, m, m_Caster);
|
||||
m.FixedParticles(0x376A, 9, 32, 5005, EffectLayer.Waist);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ namespace Server.Spells.Spellweaving
|
|||
-1
|
||||
);
|
||||
|
||||
private static readonly Dictionary<BaseWeapon, ImmolatingWeaponEntry> m_WeaponDamageTable =
|
||||
new();
|
||||
private static readonly Dictionary<BaseWeapon, ImmolatingWeaponTimer> m_Table = new();
|
||||
|
||||
public ImmolatingWeaponSpell(Mobile caster, Item scroll = null)
|
||||
: base(caster, scroll, m_Info)
|
||||
|
|
@ -54,9 +53,10 @@ namespace Server.Spells.Spellweaving
|
|||
var duration = 10 + (int)(skill / 24) + FocusLevel;
|
||||
var damage = 5 + (int)(skill / 24) + FocusLevel;
|
||||
|
||||
var stopTimer = Timer.DelayCall(TimeSpan.FromSeconds(duration), StopImmolating, weapon);
|
||||
var t = new ImmolatingWeaponTimer(TimeSpan.FromSeconds(duration), damage, Caster, weapon);
|
||||
m_Table[weapon] = t;
|
||||
t.Start();
|
||||
|
||||
m_WeaponDamageTable[weapon] = new ImmolatingWeaponEntry(damage, stopTimer, Caster);
|
||||
weapon.InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
|
@ -64,60 +64,53 @@ namespace Server.Spells.Spellweaving
|
|||
FinishSequence();
|
||||
}
|
||||
|
||||
public static bool IsImmolating(BaseWeapon weapon) => m_WeaponDamageTable.ContainsKey(weapon);
|
||||
public static bool IsImmolating(BaseWeapon weapon) => m_Table.ContainsKey(weapon);
|
||||
|
||||
public static int GetImmolatingDamage(BaseWeapon weapon) =>
|
||||
m_WeaponDamageTable.TryGetValue(weapon, out var entry) ? entry.m_Damage : 0;
|
||||
m_Table.TryGetValue(weapon, out var entry) ? entry._damage : 0;
|
||||
|
||||
public static void DoEffect(BaseWeapon weapon, Mobile target)
|
||||
{
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(0.25), FinishEffect, new DelayedEffectEntry(weapon, target));
|
||||
if (m_Table.Remove(weapon, out var timer))
|
||||
{
|
||||
timer.Stop();
|
||||
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(0.25), FinishEffect, target, timer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FinishEffect(DelayedEffectEntry effect)
|
||||
private static void FinishEffect(Mobile target, ImmolatingWeaponTimer timer)
|
||||
{
|
||||
if (m_WeaponDamageTable.TryGetValue(effect.m_Weapon, out var entry))
|
||||
{
|
||||
AOS.Damage(effect.m_Target, entry.m_Caster, entry.m_Damage, 0, 100, 0, 0, 0);
|
||||
}
|
||||
AOS.Damage(target, timer._caster, timer._damage, 0, 100, 0, 0, 0);
|
||||
}
|
||||
|
||||
public static void StopImmolating(BaseWeapon weapon)
|
||||
{
|
||||
if (!m_WeaponDamageTable.Remove(weapon, out var entry))
|
||||
if (m_Table.Remove(weapon, out var timer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
timer._caster?.PlaySound(0x27);
|
||||
timer.Stop();
|
||||
|
||||
entry.m_Caster?.PlaySound(0x27);
|
||||
entry.m_Timer.Stop();
|
||||
|
||||
weapon.InvalidateProperties();
|
||||
}
|
||||
|
||||
private class ImmolatingWeaponEntry
|
||||
{
|
||||
public readonly Mobile m_Caster;
|
||||
public readonly int m_Damage;
|
||||
public readonly Timer m_Timer;
|
||||
|
||||
public ImmolatingWeaponEntry(int damage, Timer stopTimer, Mobile caster)
|
||||
{
|
||||
m_Damage = damage;
|
||||
m_Timer = stopTimer;
|
||||
m_Caster = caster;
|
||||
weapon.InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private class DelayedEffectEntry
|
||||
private class ImmolatingWeaponTimer : Timer
|
||||
{
|
||||
public readonly Mobile m_Target;
|
||||
public readonly BaseWeapon m_Weapon;
|
||||
public readonly Mobile _caster;
|
||||
public readonly int _damage;
|
||||
public readonly BaseWeapon _weapon;
|
||||
|
||||
public DelayedEffectEntry(BaseWeapon weapon, Mobile target)
|
||||
public ImmolatingWeaponTimer(TimeSpan duration, int damage, Mobile caster, BaseWeapon weapon) : base(duration)
|
||||
{
|
||||
m_Weapon = weapon;
|
||||
m_Target = target;
|
||||
_damage = damage;
|
||||
_caster = caster;
|
||||
_weapon = weapon;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
StopImmolating(_weapon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue