From 5725fba3733c1ee2f15cc1b202ca014d2b098d23 Mon Sep 17 00:00:00 2001 From: IAmDanielDinner <38978615+SpeedyDevil@users.noreply.github.com> Date: Mon, 4 Oct 2021 08:04:06 +0200 Subject: [PATCH] fix: Adds missing ML Abilities (#799) --- .../Items/Weapons/Abilities/ArmorPierce.cs | 13 +- .../Items/Weapons/Abilities/Bladeweave.cs | 106 +++++++++ .../Items/Weapons/Abilities/Block.cs | 13 +- .../Items/Weapons/Abilities/CrushingBlow.cs | 18 ++ .../Items/Weapons/Abilities/DefenseMastery.cs | 16 +- .../Items/Weapons/Abilities/Disarm.cs | 44 ++-- .../Items/Weapons/Abilities/Dismount.cs | 11 +- .../Items/Weapons/Abilities/DoubleShot.cs | 15 +- .../Items/Weapons/Abilities/DualWield.cs | 20 +- .../Items/Weapons/Abilities/Feint.cs | 19 +- .../Items/Weapons/Abilities/ForceArrow.cs | 164 ++++++++++++++ .../Items/Weapons/Abilities/ForceOfNature.cs | 111 ++++++++++ .../Weapons/Abilities/FrenziedWhirlwind.cs | 14 +- .../Weapons/Abilities/InfectiousStrike.cs | 2 +- .../Items/Weapons/Abilities/LightningArrow.cs | 58 +++++ .../Items/Weapons/Abilities/NerveStrike.cs | 13 +- .../Items/Weapons/Abilities/ParalyzingBlow.cs | 20 +- .../Items/Weapons/Abilities/PsychicAttack.cs | 93 ++++++++ .../Items/Weapons/Abilities/RidingSwipe.cs | 14 +- .../Items/Weapons/Abilities/SerpentArrow.cs | 46 ++++ .../Items/Weapons/Abilities/ShadowStrike.cs | 4 + .../Items/Weapons/Abilities/TalonStrike.cs | 12 +- .../Items/Weapons/Abilities/WeaponAbility.cs | 109 +++++++-- .../Weapons/Abilities/WhirlwindAttack.cs | 2 +- .../UOContent/Items/Weapons/BaseWeapon.cs | 26 ++- .../Items/Weapons/Staves/GnarledStaff.cs | 2 +- Projects/UOContent/Misc/BuffIcons.cs | 209 ++++++++++++++---- 27 files changed, 931 insertions(+), 243 deletions(-) create mode 100644 Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs create mode 100644 Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs create mode 100644 Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs create mode 100644 Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs create mode 100644 Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs create mode 100644 Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs diff --git a/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs b/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs index 448de8b64..1d3cb2507 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs @@ -10,18 +10,7 @@ namespace Server.Items public override double DamageScalar => 1.5; public override bool RequiresSE => true; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs b/Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs new file mode 100644 index 000000000..48e69f012 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs @@ -0,0 +1,106 @@ +using Server.Mobiles; +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class Bladeweave : WeaponAbility + { + private class BladeWeaveRedirect + { + public readonly WeaponAbility NewAbility; + public readonly int ClilocEntry; + + public BladeWeaveRedirect(WeaponAbility ability, int cliloc) + { + NewAbility = ability; + ClilocEntry = cliloc; + } + } + + private static readonly Dictionary _newAttack = new(); + + public static bool BladeWeaving(Mobile attacker, out WeaponAbility a) + { + if (_newAttack.TryGetValue(attacker, out var bwr)) + { + a = bwr.NewAbility; + return true; + } + + a = null; + return false; + } + + public override int BaseMana => 30; + + public override bool OnBeforeSwing(Mobile attacker, Mobile defender) + { + if (!Validate(attacker) || !CheckMana(attacker, false)) + { + return false; + } + + // Bladeweave is only from 90 fighting and tactics skill, so you need only to check bushido/ninjitsu value over 50 to perform block and feint. + var requiredBushido = Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value); + + var ran = Utility.Random(requiredBushido >= 50 ? 9 : 7); + + var newAttack = _newAttack[attacker] = GetBladeWeaveRedirect(ran); + + return newAttack.NewAbility.OnBeforeSwing(attacker, defender); + } + + private static BladeWeaveRedirect GetBladeWeaveRedirect(int number) => + number switch + { + 0 => new BladeWeaveRedirect(ArmorIgnore, 1028838), + 1 => new BladeWeaveRedirect(BleedAttack, 1028839), + 2 => new BladeWeaveRedirect(ConcussionBlow, 1028840), + 3 => new BladeWeaveRedirect(CrushingBlow, 1028841), + 4 => new BladeWeaveRedirect(DoubleStrike, 1028844), + 5 => new BladeWeaveRedirect(MortalStrike, 1028846), + 6 => new BladeWeaveRedirect(ParalyzingBlow, 1028848), + 7 => new BladeWeaveRedirect(Block, 1028853), + _ => new BladeWeaveRedirect(Feint, 1028857) //8 + }; + + public override bool OnBeforeDamage(Mobile attacker, Mobile defender) => + _newAttack.TryGetValue(attacker, out var bwr) + ? bwr.NewAbility.OnBeforeDamage(attacker, defender) + : base.OnBeforeDamage(attacker, defender); + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (CheckMana(attacker, false)) + { + if (_newAttack.TryGetValue(attacker, out var bwr)) + { + attacker.SendLocalizedMessage(1072841, $"#{bwr.ClilocEntry}"); + bwr.NewAbility.OnHit(attacker, defender, damage); + } + else + { + base.OnHit(attacker, defender, damage); + } + + _newAttack.Remove(attacker); + ClearCurrentAbility(attacker); + } + } + + public override void OnMiss(Mobile attacker, Mobile defender) + { + if (_newAttack.TryGetValue(attacker, out var bwr)) + { + bwr.NewAbility.OnMiss(attacker, defender); + } + else + { + base.OnMiss(attacker, defender); + } + + _newAttack.Remove(attacker); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/Block.cs b/Projects/UOContent/Items/Weapons/Abilities/Block.cs index 811d71dd6..8ace4d3bd 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Block.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Block.cs @@ -11,18 +11,7 @@ namespace Server.Items private static readonly Dictionary _table = new(); public override int BaseMana => 30; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs b/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs index a172b82ee..0dc5d9a60 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs @@ -8,6 +8,24 @@ namespace Server.Items public override int BaseMana => 25; public override double DamageScalar => 1.5; + public virtual double GetRequiredTactics(Mobile from) + { + if (from.Weapon is BaseWeapon weapon) + { + if (weapon.PrimaryAbility == this) + { + return 30.0; + } + + if (weapon.SecondaryAbility == this) + { + return 60.0; + } + } + + return 200.0; + } + public override void OnHit(Mobile attacker, Mobile defender, int damage) { if (!Validate(attacker) || !CheckMana(attacker, true)) diff --git a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs index 174951364..ee6ce5792 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs @@ -12,18 +12,7 @@ namespace Server.Items private static readonly Dictionary _table = new(); public override int BaseMana => 30; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -40,8 +29,7 @@ namespace Server.Items var modifier = (int)(30.0 * - ((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) - - 50.0) / 70.0)); + ((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) - 50.0) / 70.0)); if (_table.TryGetValue(attacker, out var info)) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs index e8a6c9c1f..f3ede2c1d 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs @@ -12,33 +12,25 @@ namespace Server.Items public override int BaseMana => 20; - // No longer active in pub21: - /*public override bool CheckSkills( Mobile from ) + public override bool RequiresTactics(Mobile from) => from.Weapon is not BaseWeapon { Skill: SkillName.Wrestling }; + + // Disarm is special. Doesnt need tactics when wresling and tactics need is lower than fighting skill. + public virtual double GetRequiredTactics(Mobile from) { - 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; - }*/ - - public override bool RequiresTactics(Mobile from) - { - if (!(from.Weapon is BaseWeapon weapon)) + if (from.Weapon is BaseWeapon weapon) { - return false; + if (weapon.PrimaryAbility == this) + { + return 30.0; + } + + if (weapon.SecondaryAbility == this) + { + return 60.0; + } } - return weapon.Skill != SkillName.Wrestling; + return 200.0; } public override void OnHit(Mobile attacker, Mobile defender, int damage) @@ -52,7 +44,7 @@ namespace Server.Items var toDisarm = defender.FindItemOnLayer(Layer.OneHanded); - if (toDisarm?.Movable == false) + if (toDisarm?.Movable != false) { toDisarm = defender.FindItemOnLayer(Layer.TwoHanded); } @@ -63,7 +55,7 @@ namespace Server.Items { attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. } - else if (!Core.ML && toDisarm == null || toDisarm is BaseShield || toDisarm is Spellbook) + else if (!Core.ML && toDisarm == null || toDisarm is BaseShield or Spellbook) { attacker.SendLocalizedMessage(1060849); // Your target is already unarmed! } @@ -77,6 +69,8 @@ namespace Server.Items pack.DropItem(toDisarm); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.NoRearm, 1075637, BlockEquipDuration, defender)); + BaseWeapon.BlockEquip(defender, BlockEquipDuration); } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs index 8512bf0db..903803d19 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs @@ -44,8 +44,8 @@ namespace Server.Items return; } - if (attacker.Mounted && (!(attacker.Weapon is Lance) || !(defender.Weapon is Lance)) - ) // TODO: Should there be a message here? + // TODO: Should there be a message here? + if (attacker.Mounted && (attacker.Weapon is not Lance || defender.Weapon is not Lance)) { return; } @@ -101,12 +101,9 @@ namespace Server.Items { playerMobile.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, true); } - else if (Core.ML && attacker is BaseCreature bc) + else if (Core.ML && attacker is BaseCreature { ControlMaster: PlayerMobile pm }) { - if (bc.ControlMaster is PlayerMobile pm) - { - pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false); - } + pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false); } if (!attacker.Mounted) diff --git a/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs b/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs index a97ad27f0..8f01dea14 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs @@ -1,3 +1,5 @@ +using System; + namespace Server.Items { /// @@ -7,17 +9,8 @@ namespace Server.Items { public override int BaseMana => 30; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresTactics(Mobile from) => false; + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs b/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs index 76d2597ff..c8ea1b1af 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs @@ -12,17 +12,8 @@ namespace Server.Items public override int BaseMana => 30; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063352, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Ninjitsu; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -44,8 +35,8 @@ namespace Server.Items timer = new DualWieldTimer( attacker, - (int)(20.0 + 3.0 * (attacker.Skills.Ninjitsu.Value - 50.0) / 7.0) - ); // 20-50 % increase + (int)(20.0 + 3.0 * (attacker.Skills.Ninjitsu.Value - 50.0) / 7.0) // 20-50 % increase + ); timer.Start(); Registry.Add(attacker, timer); @@ -55,8 +46,7 @@ namespace Server.Items { private readonly Mobile m_Owner; - public DualWieldTimer(Mobile owner, int bonusSwingSpeed) - : base(TimeSpan.FromSeconds(6.0)) + public DualWieldTimer(Mobile owner, int bonusSwingSpeed) : base(TimeSpan.FromSeconds(6.0)) { m_Owner = owner; BonusSwingSpeed = bonusSwingSpeed; diff --git a/Projects/UOContent/Items/Weapons/Abilities/Feint.cs b/Projects/UOContent/Items/Weapons/Abilities/Feint.cs index 70f32fc43..20c738a77 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Feint.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Feint.cs @@ -11,18 +11,7 @@ namespace Server.Items public static Dictionary Registry { get; } = new(); public override int BaseMana => 30; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -46,10 +35,8 @@ namespace Server.Items timer = new FeintTimer( defender, - (int)(20.0 + 3.0 * (Math.Max( - attacker.Skills.Ninjitsu.Value, - attacker.Skills.Bushido.Value - ) - 50.0) / 7.0) + (int)(20.0 + 3.0 * + (Math.Max(attacker.Skills.Ninjitsu.Value, attacker.Skills.Bushido.Value) - 50.0) / 7.0) ); // 20-50 % decrease timer.Start(); diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs new file mode 100644 index 000000000..d8b5c9add --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs @@ -0,0 +1,164 @@ +using Server.Spells; +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class ForceArrow : WeaponAbility + { + public override int BaseMana => 20; + + public override bool RequiresTactics(Mobile from) => false; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1074381); // You fire an arrow of pure force. + defender.SendLocalizedMessage(1074382); // You are struck by a force arrow! + + if (0.4 > Utility.RandomDouble()) + { + defender.Combatant = null; + defender.Warmode = false; + } + + ForceArrowInfo info = GetInfo(attacker, defender); + + if (info == null) + { + BeginForceArrow(attacker, defender); + } + else if (info.Timer.Running) + { + info.Timer.IncreaseExpiration(); + + BuffInfo.RemoveBuff(defender, BuffIcon.ForceArrow); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString())); + } + + if (defender.Spell is Spell spell && spell.IsCasting) + { + spell.Disturb(DisturbType.Hurt, false, true); + } + } + + private static readonly Dictionary> _table = new Dictionary>(); + + public static void BeginForceArrow(Mobile attacker, Mobile defender) + { + ForceArrowInfo info = new ForceArrowInfo(attacker, defender); + info.Timer = new ForceArrowTimer(info); + + if (_table.TryGetValue(attacker, out var list)) + { + list.Add(info); + } + else + { + _table.Add(attacker, new List() { info }); + } + + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString())); + } + + public static void EndForceArrow(ForceArrowInfo info) + { + if (info == null) + { + return; + } + + Mobile attacker = info.Attacker; + + if (_table.TryGetValue(attacker, out var list) && list.Remove(info) && list.Count == 0) + { + _table.Remove(attacker); + } + + BuffInfo.RemoveBuff(info.Defender, BuffIcon.ForceArrow); + } + + public static bool HasForceArrow(Mobile attacker, Mobile defender) + { + if (_table.TryGetValue(attacker, out var list)) + { + foreach (ForceArrowInfo info in list) + { + if (info.Defender == defender) + { + return true; + } + } + } + + return false; + } + + public static ForceArrowInfo GetInfo(Mobile attacker, Mobile defender) + { + if (_table.TryGetValue(attacker,out var list)) + { + foreach (ForceArrowInfo info in list) + { + if (info.Defender == defender) + { + return info; + } + } + } + + return null; + } + + public class ForceArrowInfo + { + public Mobile Attacker { get; } + public Mobile Defender { get; } + public ForceArrowTimer Timer { get; set; } + public int DefenseChanceMalus { get; set; } + + public ForceArrowInfo(Mobile attacker, Mobile defender) + { + Attacker = attacker; + Defender = defender; + DefenseChanceMalus = 10; + } + } + + public class ForceArrowTimer : Timer + { + private readonly ForceArrowInfo _info; + private DateTime _expires; + + public ForceArrowTimer(ForceArrowInfo info) + : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1)) + { + _info = info; + _expires = Core.Now + TimeSpan.FromSeconds(10); + + Start(); + } + + protected override void OnTick() + { + if (_expires < Core.Now) + { + Stop(); + EndForceArrow(_info); + } + } + + public void IncreaseExpiration() + { + _expires += TimeSpan.FromSeconds(2); + _info.DefenseChanceMalus += 5; + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs new file mode 100644 index 000000000..17d4ffc8a --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class ForceOfNature : WeaponAbility + { + public override int BaseMana => 35; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1074374); // You attack your enemy with the force of nature! + defender.SendLocalizedMessage(1074375); // You are assaulted with great force! + + defender.PlaySound(0x22F); + defender.FixedParticles(0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head); + defender.FixedParticles(0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255); + + Remove(attacker); + + ForceOfNatureTimer t = new ForceOfNatureTimer(attacker, defender); + t.Start(); + + _table[attacker] = t; + } + + private static readonly Dictionary _table = new Dictionary(); + + public static void Remove(Mobile m) + { + if (_table.Remove(m, out var timer)) + { + timer.Stop(); + } + } + + public static void OnHit(Mobile from, Mobile target) + { + if (_table.TryGetValue(from,out var t)) + { + t.Hits++; + t.LastHit = Core.Now; + + if (t.Hits % 12 == 0) + { + int duration = target.Skills[SkillName.MagicResist].Value >= 90.0 ? 1 : 2; + target.Paralyze(TimeSpan.FromSeconds(duration)); + + target.FixedEffect(0x376A, 9, 32); + target.PlaySound(0x204); + + t.Hits = 0; + + from.SendLocalizedMessage(1004013); // You successfully stun your opponent! + target.SendLocalizedMessage(1004014); // You have been stunned! + } + } + } + + public static double GetDamageScalar(Mobile from, Mobile target) + { + if (_table.TryGetValue(from, out var t) && t.Target == target) + { + var bonus = Math.Min(100, Math.Max(50, from.Str - 50)); + return (100.0 + bonus) / 100.0; + } + + return 1.0; + } + + private class ForceOfNatureTimer : Timer + { + public Mobile Target { get; } + public Mobile From { get; } + public int Hits { get; set; } + public DateTime LastHit { get; set; } + + public ForceOfNatureTimer(Mobile from, Mobile target) + : base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)) + { + Target = target; + From = from; + Hits = 1; + LastHit = Core.Now; + } + + protected override void OnTick() + { + if (!From.Alive || !Target.Alive || Target.Map != From.Map || Target.GetDistanceToSqrt(From.Location) > 10 || LastHit + TimeSpan.FromSeconds(20) < Core.Now || Index > 36) + { + Remove(From); + return; + } + + if (Index == 1) + { + int damage = Utility.RandomMinMax(15, 35); + + AOS.Damage(From, From, damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false); + } + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs index a523b7a1b..f6b2c6167 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs @@ -12,18 +12,8 @@ namespace Server.Items public override int BaseMana => 30; public static Dictionary Registry { get; } = new(); - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresTactics(Mobile from) => false; + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs index ce155ce09..05b0e9969 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs @@ -30,7 +30,7 @@ namespace Server.Items ClearCurrentAbility(attacker); - if (!(attacker.Weapon is BaseWeapon weapon)) + if (attacker.Weapon is not BaseWeapon weapon) { return; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs new file mode 100644 index 000000000..750b0e4cf --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs @@ -0,0 +1,58 @@ +using Server.Spells; +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class LightningArrow : WeaponAbility + { + public override int BaseMana => 20; + + //TODO - add ConsumeAmmo on weaponabilities + // public override bool ConsumeAmmo => false; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker)) + { + return; + } + + ClearCurrentAbility(attacker); + + Map map = attacker.Map; + + if (map == null || attacker.Weapon is not BaseWeapon weapon || !CheckMana(attacker, true)) + { + return; + } + + List targets = new List(); + IPooledEnumerable eable = defender.GetMobilesInRange(5); + + foreach (Mobile m in eable) + { + if (m != defender && m != attacker && SpellHelper.ValidIndirectTarget(attacker, m) && m?.Deleted == false && + m.Map == attacker.Map && m.Alive && attacker.CanSee(m) && attacker.CanBeHarmful(m) && + attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m)) + { + targets.Add(m); + } + } + + eable.Free(); + defender.BoltEffect(0); + + var mobilesLeft = Math.Min(targets.Count, 2); + while (mobilesLeft-- > 0) + { + var index = Utility.Random(targets.Count); + var m = targets[index]; + targets.RemoveAt(index); + + m.BoltEffect(0); + AOS.Damage(m, attacker, Utility.RandomMinMax(29, 40), 0, 0, 0, 0, 100); + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs index cead5302b..11eb81352 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs @@ -9,17 +9,8 @@ namespace Server.Items { public override int BaseMana => 30; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! - from.SendLocalizedMessage(1070768, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Bushido; public override bool OnBeforeSwing(Mobile attacker, Mobile defender) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs index 9c8184ebd..43f985df1 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs @@ -17,25 +17,7 @@ namespace Server.Items public override int BaseMana => 30; - // No longer active in pub21: - /*public override bool CheckSkills( Mobile from ) - { - if (!base.CheckSkills( from )) - return false; - - if (!(from.Weapon is Fists)) - return true; - - Skill skill = from.Skills.Anatomy; - - if (skill?.Base >= 80.0) - return true; - - from.SendLocalizedMessage( 1061811 ); // You lack the required anatomy skill to perform that attack! - - return false; - }*/ - + // When using Wrestling, tactics isnt needed. public override bool RequiresTactics(Mobile from) => !(from.Weapon is BaseWeapon weapon && weapon.Skill == SkillName.Wrestling); diff --git a/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs new file mode 100644 index 000000000..c38d387dd --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class PsychicAttack : WeaponAbility + { + public static Dictionary Registry { get; } = new(); + public override int BaseMana => 30; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1074383); // Your shot sends forth a wave of psychic energy. + defender.SendLocalizedMessage(1074384); // Your mind is attacked by psychic force! + + defender.FixedParticles(0x3789, 10, 25, 5032, EffectLayer.Head); + defender.PlaySound(0x1F8); + + if (Registry.TryGetValue(defender, out var timer)) + { + if (!timer.DoneIncrease) + { + timer.SpellDamageMalus *= 2; + timer.ManaCostMalus *= 2; + } + } + else + { + timer = new PsychicAttackTimer(defender); + timer.Start(); + Registry.Add(defender, timer); + } + + BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack); + + string args = $"{timer.SpellDamageMalus}\t{timer.ManaCostMalus}"; + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.PsychicAttack, 1151296, 1151297, args)); + } + + public static void RemoveEffects(Mobile defender) + { + if (defender == null) + { + return; + } + + BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack); + + Registry.Remove(defender); + + defender.SendLocalizedMessage(1150292); // You recover from the effects of the psychic attack. + } + + public class PsychicAttackTimer : Timer + { + private readonly Mobile _defender; + private int _spellDamageMalus; + private int _manaCostMalus; + + public int SpellDamageMalus + { + get => _spellDamageMalus; + set { _spellDamageMalus = value; DoneIncrease = true; } + } + public int ManaCostMalus + { + get => _manaCostMalus; + set { _manaCostMalus = value; DoneIncrease = true; } + } + public bool DoneIncrease { get; private set; } + + public PsychicAttackTimer(Mobile defender) : base(TimeSpan.FromSeconds(10)) + { + _defender = defender; + _spellDamageMalus = 15; + _manaCostMalus = 15; + DoneIncrease = false; + } + + protected override void OnTick() + { + RemoveEffects(_defender); + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs b/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs index bd880c6b9..cf2be4a65 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs @@ -13,18 +13,8 @@ namespace Server.Items public override int BaseMana => 30; public override bool RequiresSE => true; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! - from.SendLocalizedMessage(1070768, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Bushido; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs new file mode 100644 index 000000000..138dc25bf --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs @@ -0,0 +1,46 @@ +namespace Server.Items +{ + public class SerpentArrow : WeaponAbility + { + public override int BaseMana => 25; + + // Serpent arrow is used only as secondary ability on "Elven composite longbow" so needed skill is always 60, but for sure we will use GetRequiredSecondarySkill + public override bool RequiresSecondarySkill(Mobile from) => true; + public override double GetRequiredSecondarySkill(Mobile from) => 60; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Poisoning; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + defender.SendLocalizedMessage(1112369); // You have been poisoned by a lethal arrow! + + int level; + + if (attacker.InRange(defender, 2)) + { + level = attacker.Skills.Poisoning.Fixed / 2 switch + { + >= 1000 => 3, + > 850 => 2, + > 650 => 1, + _ => 0 + }; + } + else + { + level = 0; + } + + defender.ApplyPoison(attacker, Poison.GetPoison(level)); + + defender.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist); + defender.PlaySound(0x474); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs index a43bf94f2..29d6e5eaa 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs @@ -12,6 +12,10 @@ namespace Server.Items public override bool RequiresTactics(Mobile from) => false; + public override bool RequiresSecondarySkill(Mobile from) => true; + public override double GetRequiredSecondarySkill(Mobile from) => 80; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Stealth; + public override bool CheckSkills(Mobile from) { if (!base.CheckSkills(from)) diff --git a/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs index 9741fdcb6..13963d11d 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs @@ -13,17 +13,9 @@ namespace Server.Items public override int BaseMana => 30; public override double DamageScalar => 1.2; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063352, "50"); - return false; - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Ninjitsu; - return base.CheckSkills(from); - } public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index dff1d95c6..9ad7e996f 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -38,12 +38,12 @@ namespace Server.Items new DualWield(), new DoubleShot(), new ArmorPierce(), - null, - null, - null, - null, - null, - null, + new Bladeweave(), + new ForceArrow(), + new LightningArrow(), + new PsychicAttack(), + new SerpentArrow(), + new ForceOfNature(), new Disrobe() }; @@ -109,16 +109,60 @@ namespace Server.Items public virtual bool RequiresTactics(Mobile from) => true; + public virtual bool RequiresSecondarySkill(Mobile from) => false; + + /// + /// Return primary skill for ability. Default 70/90 + /// + /// + /// public virtual double GetRequiredSkill(Mobile from) { if (from.Weapon is BaseWeapon weapon) { - if (weapon.PrimaryAbility == this) + if (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave) { return 70.0; } - if (weapon.SecondaryAbility == this) + if (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave) + { + return 90.0; + } + } + + return 200.0; + } + + /// + /// Returns secondary skill for Ability. Default 50. + /// + /// + /// + public virtual double GetRequiredSecondarySkill(Mobile from) => 50.0; + + /// + /// Return secondary skillName. Default bushido/ninjitsu + /// + /// + /// + public virtual SkillName GetSecondarySkillName(Mobile from) => from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido; + + /// + /// Returns tactics needed for Ability. Default 70/90. + /// + /// + /// + public virtual double GetRequiredTactics(Mobile from) + { + if (from.Weapon is BaseWeapon weapon) + { + if (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave) + { + return 70.0; + } + + if (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave) { return 90.0; } @@ -184,13 +228,32 @@ namespace Server.Items var reqSkill = GetRequiredSkill(from); var reqTactics = Core.ML && RequiresTactics(from); - if (Core.ML && reqTactics && from.Skills.Tactics.Base < reqSkill) + if (reqTactics && from.Skills.Tactics.Base < GetRequiredTactics(from)) { // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack from.SendLocalizedMessage(1079308, reqSkill.ToString()); return false; } + var reqSecondarySkill = GetRequiredSecondarySkill(from); + SkillName secondarySkill = GetSecondarySkillName(from); + + if (RequiresSecondarySkill(from) && from.Skills[secondarySkill].Base < reqSecondarySkill) + { + int loc = GetSkillLocalization(secondarySkill); + + if (loc == 1060184) + { + from.SendLocalizedMessage(loc); + } + else + { + from.SendLocalizedMessage(loc, reqSecondarySkill.ToString()); + } + + return false; + } + if (skill?.Base >= reqSkill) { return true; @@ -207,19 +270,22 @@ namespace Server.Items } /* */ - if (reqTactics) - { - // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack - from.SendLocalizedMessage(1079308, reqSkill.ToString()); - } - else - { - // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack - from.SendLocalizedMessage(1060182, reqSkill.ToString()); - } + // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack + from.SendLocalizedMessage(1060182, reqSkill.ToString()); return false; } + private int GetSkillLocalization(SkillName skill) + { + return skill switch + { + SkillName.Bushido => 1063347, // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + SkillName.Ninjitsu => 1063347, // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + SkillName.Poisoning => 1060184, // You lack the required poisoning to perform that attack + _ => 1157351 // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack + }; + } + public virtual bool CheckSkills(Mobile from) => CheckWeaponSkill(from); @@ -420,10 +486,7 @@ namespace Server.Items { private readonly Mobile m_Mobile; - public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) - { - m_Mobile = from; - } + public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) => m_Mobile = from; protected override void OnTick() { diff --git a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs index 316ae4c53..32496a2a7 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs @@ -28,7 +28,7 @@ namespace Server.Items return; } - if (!(attacker.Weapon is BaseWeapon weapon)) + if (attacker.Weapon is not BaseWeapon weapon) { return; } diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index aef60a032..b50b97f32 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -1024,6 +1024,7 @@ namespace Server.Items } ImmolatingWeaponSpell.StopImmolating(this); + ForceOfNature.Remove(m); m.CheckStatTimers(); @@ -1157,6 +1158,13 @@ namespace Server.Items bonus = AosAttributes.GetValue(defender, AosAttribute.DefendChance); + var info = ForceArrow.GetInfo(attacker, defender); + + if (info != null && info.Defender == defender) + { + bonus -= info.DefenseChanceMalus; + } + if (DivineFurySpell.UnderEffect(defender)) { bonus -= 20; // defender loses 20% bonus when they're under divine fury @@ -1662,6 +1670,8 @@ namespace Server.Items percentageBonus += (int)(move.GetDamageScalar(attacker, defender) * 100) - 100; } + percentageBonus += (int)(ForceOfNature.GetDamageScalar(attacker, defender) * 100) - 100; + percentageBonus += (int)(damageBonus * 100) - 100; var cs = CheckSlayers(attacker, defender); @@ -1864,7 +1874,10 @@ namespace Server.Items move = null; } - var ignoreArmor = a is ArmorIgnore || move?.IgnoreArmor(attacker) == true; + var ignoreArmor = a is ArmorIgnore || + move?.IgnoreArmor(attacker) == true || + Bladeweave.BladeWeaving(attacker, out var bladeweavingAbi) && + bladeweavingAbi is ArmorIgnore; var damageGiven = AOS.Damage( defender, @@ -1981,8 +1994,8 @@ namespace Server.Items mobile.LocalOverheadMessage( MessageType.Regular, 0x3B2, - 1061121 - ); // Your equipment is severely damaged. + 1061121 // Your equipment is severely damaged. + ); } } else @@ -2101,6 +2114,8 @@ namespace Server.Items a?.OnHit(attacker, defender, damage); move?.OnHit(attacker, defender, damage); + ForceOfNature.OnHit(attacker, defender); + if (defender is IHonorTarget it) { it.ReceivedHonorContext?.OnTargetHit(attacker); @@ -2142,6 +2157,11 @@ namespace Server.Items // SDI bonus damageBonus += AosAttributes.GetValue(attacker, AosAttribute.SpellDamage); + if(PsychicAttack.Registry.TryGetValue(attacker,out var timer)) + { + damageBonus -= timer.SpellDamageMalus; + } + var context = TransformationSpellHelper.GetContext(attacker); if (context?.Spell is ReaperFormSpell spell) diff --git a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs index aaa4d6e19..8ca5f06a6 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs @@ -8,7 +8,7 @@ namespace Server.Items public GnarledStaff() : base(0x13F8) => Weight = 3.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; - public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; + public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature; public override int AosStrengthReq => 20; public override int AosMinDamage => 15; diff --git a/Projects/UOContent/Misc/BuffIcons.cs b/Projects/UOContent/Misc/BuffIcons.cs index 7008ed1cd..0c9e48340 100644 --- a/Projects/UOContent/Misc/BuffIcons.cs +++ b/Projects/UOContent/Misc/BuffIcons.cs @@ -228,57 +228,190 @@ namespace Server { DismountPrevention = 0x3E9, NoRearm = 0x3EA, - - // Currently, no 0x3EB or 0x3EC - NightSight = 0x3ED, // * + //Currently, no 0x3EB or 0x3EC + NightSight = 0x3ED, //* DeathStrike, EvilOmen, - UnknownStandingSwirl, // Which is healing throttle & Stamina throttle? - UnknownKneelingSword, - DivineFury, // * - EnemyOfOne, // * - HidingAndOrStealth, // * - ActiveMeditation, // * - BloodOathCaster, // * - BloodOathCurse, // * - CorpseSkin, // * - Mindrot, // * - PainSpike, // * + HonoredDebuff, + AchievePerfection, + DivineFury, //* + EnemyOfOne, //* + HidingAndOrStealth, //* + ActiveMeditation, //* + BloodOathCaster, //* + BloodOathCurse, //* + CorpseSkin, //* + Mindrot, //* + PainSpike, //* Strangle, - GiftOfRenewal, // * - AttuneWeapon, // * - Thunderstorm, // * - EssenceOfWind, // * - EtherealVoyage, // * - GiftOfLife, // * - ArcaneEmpowerment, // * + GiftOfRenewal, //* + AttuneWeapon, //* + Thunderstorm, //* + EssenceOfWind, //* + EtherealVoyage, //* + GiftOfLife, //* + ArcaneEmpowerment, //* MortalStrike, - ReactiveArmor, // * - Protection, // * + ReactiveArmor, //* + Protection, //* ArchProtection, - MagicReflection, // * - Incognito, // * + MagicReflection, //* + Incognito, //* Disguised, AnimalForm, Polymorph, - Invisibility, // * - Paralyze, // * + Invisibility, //* + Paralyze, //* Poison, Bleed, - Clumsy, // * - FeebleMind, // * - Weaken, // * - Curse, // * + Clumsy, //* + FeebleMind, //* + Weaken, //* + Curse, //* MassCurse, - Agility, // * - Cunning, // * - Strength, // * - Bless, // * + Agility, //* + Cunning, //* + Strength, //* + Bless, //* Sleep, StoneForm, SpellPlague, - SpellTrigger, - NetherBolt, - Fly + Berserk, + MassSleep, + Fly, + Inspire, + Invigorate, + Resilience, + Perseverance, + TribulationTarget, + DespairTarget, + FishPie = 0x426, + HitLowerAttack, + HitLowerDefense, + DualWield, + Block, + DefenseMastery, + DespairCaster, + Healing, + SpellFocusingBuff, + SpellFocusingDebuff, + RageFocusingDebuff, + RageFocusingBuff, + Warding, + TribulationCaster, + ForceArrow, + Disarm, + Surge, + Feint, + TalonStrike, + PsychicAttack, + ConsecrateWeapon, + GrapesOfWrath, + EnemyOfOneDebuff, + HorrificBeast, + LichForm, + VampiricEmbrace, + CurseWeapon, + ReaperForm, + ImmolatingWeapon, + Enchant, + HonorableExecution, + Confidence, + Evasion, + CounterAttack, + LightningStrike, + MomentumStrike, + OrangePetals, + RoseOfTrinsic, + PoisonImmunity, + Veterinary, + Perfection, + Honored, + ManaPhase, + FanDancerFanFire, + Rage, + Webbing, + MedusaStone, + TrueFear, + AuraOfNausea, + HowlOfCacophony, + GazeDespair, + HiryuPhysicalResistance, + RuneBeetleCorruption, + BloodwormAnemia, + RotwormBloodDisease, + SkillUseDelay, + FactionStatLoss, + HeatOfBattleStatus, + CriminalStatus, + ArmorPierce, + SplinteringEffect, + SwingSpeedDebuff, + WraithForm, + CityTradeDeal = 0x466, + HumilityDebuff = 0x467, + Spirituality, + Humility, + // Skill Masteries + Rampage, + Stagger, // Debuff + Toughness, + Thrust, + Pierce, // Debuff + PlayingTheOdds, + FocusedEye, + Onslaught, // Debuff + ElementalFury, + ElementalFuryDebuff, // Debuff + CalledShot, + Knockout, + SavingThrow, + Conduit, + EtherealBurst, + MysticWeapon, + ManaShield, + AnticipateHit, + Warcry, + Shadow, + WhiteTigerForm, + Bodyguard, + HeightenedSenses, + Tolerance, + DeathRay, + DeathRayDebuff, + Intuition, + EnchantedSummoning, + ShieldBash, + Whispering, + CombatTraining, + InjectedStrikeDebuff, + InjectedStrike, + UnknownTomato, + PlayingTheOddsDebuff, + DragonTurtleDebuff, + Boarding, + Potency, + ThrustDebuff, + FistsOfFury, // 1169 + BarrabHemolymphConcentrate, + JukariBurnPoiltice, + KurakAmbushersEssence, + BarakoDraftOfMight, + UraliTranceTonic, + SakkhraProphylaxis, // 1175 + Sparks, + Swarm, + BoneBreaker, + Unknown2, + SwarmImmune, + BoneBreakerImmune, + UnknownGoblin, + UnknownRedDrop, + UnknownStar, + FeintDebuff, + CaddelliteInfused, + PotionGloriousFortune, + MysticalPolymorphTotem, + UnknownDebuff, } }