diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 85489edf3..f1d093d5c 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -1350,7 +1350,7 @@ public abstract partial class BaseWeapon theirValue = Math.Max(0.1, defValue + 50.0); } - var chance = ourValue / (theirValue * 2.0) * 1.0 + (double)bonus / 100;; + var chance = ourValue / (theirValue * 2.0) * 1.0 + (double)bonus / 100; if (Core.AOS && chance < 0.02) { @@ -1997,6 +1997,11 @@ public abstract partial class BaseWeapon Bladeweave.BladeWeaving(attacker, out var bladeweavingAbi) && bladeweavingAbi is ArmorIgnore; + if (bcAtt?.TriggerAbilitySpecialAttack(defender) != true) + { + return; + } + var damageGiven = AOS.Damage( defender, attacker, diff --git a/Projects/UOContent/Mobiles/Abilities/FanningFire.cs b/Projects/UOContent/Mobiles/Abilities/FanningFire.cs index a06bb561f..2bf12722b 100644 --- a/Projects/UOContent/Mobiles/Abilities/FanningFire.cs +++ b/Projects/UOContent/Mobiles/Abilities/FanningFire.cs @@ -5,8 +5,23 @@ namespace Server.Mobiles; public class FanningFire : MonsterAbilitySingleTargetDoT { public override MonsterAbilityType AbilityType => MonsterAbilityType.FanningFire; - public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.GiveDamage; - public override double ChanceToTrigger => 0.05; + public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.SpecialAttack; + + public FanningFire(double chanceToTrigger, int fireResistMod, int minDamage, int maxDamage) + { + ChanceToTrigger = chanceToTrigger; + FireResistMod = fireResistMod; + MinDamage = minDamage; + MaxDamage = maxDamage; + } + + public sealed override double ChanceToTrigger { get; } + + public int FireResistMod { get; } + + public int MinDamage { get; } + + public int MaxDamage { get; } public const string Name = "FanningFire"; @@ -52,16 +67,12 @@ public class FanningFire : MonsterAbilitySingleTargetDoT */ source.DoHarmful(defender); - var effect = -(defender.FireResistance / 10); + defender.AddResistanceMod(new ResistanceMod(ResistanceType.Fire, Name, FireResistMod)); - var mod = new ResistanceMod(ResistanceType.Fire, Name, effect); - defender.AddResistanceMod(mod); - - defender.FixedParticles(0x37B9, 10, 30, 0x34, EffectLayer.RightFoot); + defender.FixedParticles(0x3709, 10, 30, 0x34, EffectLayer.RightFoot); defender.PlaySound(0x208); - // TODO: Trigger replaces a normal attack. - AOS.Damage(defender, source, Utility.RandomMinMax(35, 45), 0, 100, 0, 0, 0); + AOS.Damage(defender, source, Utility.RandomMinMax(MinDamage, MaxDamage), 0, 100, 0, 0, 0); } protected override void EffectTick(BaseCreature source, Mobile defender, ref TimeSpan nextDelay) diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs index 9df7ffd0a..652bc5b05 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs @@ -14,7 +14,7 @@ public static class MonsterAbilities // Resistance Debuffs public static GraspingClaw GraspingClaw => new(); public static RuneCorruption RuneCorruption => new(); - public static FanningFire FanningFire => new(); + public static FanningFire FanningFire => new(0.05, -10, 35, 45); // Summon Undead public static SummonSkeletonsCounter SummonSkeletonsCounter => new(); diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs index d30ed4413..96e2a8429 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs @@ -14,6 +14,7 @@ public enum MonsterAbilityTrigger : ulong CombatAction = 0x0000000000000020, Death = 0x0000000000000040, Movement = 0x0000000000000080, + SpecialAttack = 0x0000000000000100, // Triggers instead of a regular attack GiveDamage = GiveMeleeDamage | GiveSpellDamage, TakeDamage = TakeMeleeDamage | TakeSpellDamage diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index 22c43ebc4..bca21bdda 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -1313,6 +1313,28 @@ namespace Server.Mobiles } } + public virtual bool TriggerAbilitySpecialAttack(Mobile target) + { + var abilities = GetMonsterAbilities(); + + if (abilities == null) + { + return false; + } + + for (var i = 0; i < abilities.Length; i++) + { + var ability = abilities[i]; + if (ability.CanTrigger(this, MonsterAbilityTrigger.SpecialAttack)) + { + ability.Trigger(MonsterAbilityTrigger.SpecialAttack, this, target); + return true; + } + } + + return false; + } + public virtual WeaponAbility GetWeaponAbility() => null; public virtual bool IsEnemy(Mobile m) diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs index 6b7a89005..e284737de 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs @@ -2,121 +2,100 @@ using ModernUO.Serialization; using System; using System.Collections.Generic; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class LadyJennifyr : SkeletalKnight { - [SerializationGenerator(0, false)] - public partial class LadyJennifyr : SkeletalKnight + private static readonly Dictionary _table = new(); + + [Constructible] + public LadyJennifyr() { - private static readonly Dictionary m_Table = new(); + IsParagon = true; - [Constructible] - public LadyJennifyr() + Hue = 0x76D; + + SetStr(208, 309); + SetDex(91, 118); + SetInt(44, 101); + + SetHits(1113, 1285); + + SetDamage(15, 25); + + SetDamageType(ResistanceType.Physical, 40); + SetDamageType(ResistanceType.Cold, 60); + + SetResistance(ResistanceType.Physical, 56, 65); + SetResistance(ResistanceType.Fire, 41, 49); + SetResistance(ResistanceType.Cold, 71, 80); + SetResistance(ResistanceType.Poison, 41, 50); + SetResistance(ResistanceType.Energy, 50, 58); + + SetSkill(SkillName.Wrestling, 127.9, 137.1); + SetSkill(SkillName.Tactics, 128.4, 141.9); + SetSkill(SkillName.MagicResist, 102.1, 119.5); + SetSkill(SkillName.Anatomy, 129.0, 137.5); + + Fame = 18000; + Karma = -18000; + } + + public override string CorpseName => "a Lady Jennifyr corpse"; + public override string DefaultName => "Lady Jennifyr"; + + /* + // TODO: Uncomment once added + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if (Utility.RandomDouble() < 0.15) + c.DropItem( new DisintegratingThesisNotes() ); + + if (Utility.RandomDouble() < 0.1) + c.DropItem( new ParrotItem() ); + } + */ + + public override bool GivesMLMinorArtifact => true; + + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 3); + } + + private static readonly MonsterAbility[] _abilities = + [ + new FanningFire(0.10, -10, 35, 45) + ]; + + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + + private class ExpireTimer : Timer + { + private readonly Mobile _mobile; + private readonly ResistanceMod _mod; + + public ExpireTimer(Mobile m, ResistanceMod mod) : base(TimeSpan.FromSeconds(10)) { - IsParagon = true; - - Hue = 0x76D; - - SetStr(208, 309); - SetDex(91, 118); - SetInt(44, 101); - - SetHits(1113, 1285); - - SetDamage(15, 25); - - SetDamageType(ResistanceType.Physical, 40); - SetDamageType(ResistanceType.Cold, 60); - - SetResistance(ResistanceType.Physical, 56, 65); - SetResistance(ResistanceType.Fire, 41, 49); - SetResistance(ResistanceType.Cold, 71, 80); - SetResistance(ResistanceType.Poison, 41, 50); - SetResistance(ResistanceType.Energy, 50, 58); - - SetSkill(SkillName.Wrestling, 127.9, 137.1); - SetSkill(SkillName.Tactics, 128.4, 141.9); - SetSkill(SkillName.MagicResist, 102.1, 119.5); - SetSkill(SkillName.Anatomy, 129.0, 137.5); - - Fame = 18000; - Karma = -18000; + _mobile = m; + _mod = mod; } - public override string CorpseName => "a Lady Jennifyr corpse"; - public override string DefaultName => "Lady Jennifyr"; - - /* - // TODO: Uncomment once added - public override void OnDeath( Container c ) + public void DoExpire() { - base.OnDeath( c ); + _mobile.RemoveResistanceMod(_mod); - if (Utility.RandomDouble() < 0.15) - c.DropItem( new DisintegratingThesisNotes() ); - - if (Utility.RandomDouble() < 0.1) - c.DropItem( new ParrotItem() ); - } - */ - - public override bool GivesMLMinorArtifact => true; - - public override void GenerateLoot() - { - AddLoot(LootPack.UltraRich, 3); + Stop(); } - public override void OnGaveMeleeAttack(Mobile defender, int damage) + protected override void OnTick() { - base.OnGaveMeleeAttack(defender, damage); - - if (Utility.RandomDouble() < 0.9) - { - return; - } - - if (m_Table.Remove(defender, out var timer)) - { - timer.DoExpire(); - } - - defender.FixedParticles(0x3709, 10, 30, 5052, EffectLayer.LeftFoot); - defender.PlaySound(0x208); - // The creature fans you with fire, reducing your resistance to fire attacks. - defender.SendLocalizedMessage(1070833); - - var mod = new ResistanceMod(ResistanceType.Fire, "FireResistFanningFire", -10); - defender.AddResistanceMod(mod); - - m_Table[defender] = timer = new ExpireTimer(defender, mod); - timer.Start(); - } - - private class ExpireTimer : Timer - { - private readonly Mobile m_Mobile; - private readonly ResistanceMod m_Mod; - - public ExpireTimer(Mobile m, ResistanceMod mod) - : base(TimeSpan.FromSeconds(10)) - { - m_Mobile = m; - m_Mod = mod; - } - - public void DoExpire() - { - m_Mobile.RemoveResistanceMod(m_Mod); - - Stop(); - } - - protected override void OnTick() - { - m_Mobile.SendLocalizedMessage(1070834); // Your resistance to fire attacks has returned. - DoExpire(); - m_Table.Remove(m_Mobile); - } + _mobile.SendLocalizedMessage(1070834); // Your resistance to fire attacks has returned. + DoExpire(); + _table.Remove(_mobile); } } }