From e468f79e18235088ac37d9eb6989585cf1fdfd45 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 15 May 2025 22:14:52 -0700 Subject: [PATCH] feat: Adds SpecialAttack monster ability trigger. Fixes fanning fire. (#2186) --- .../UOContent/Items/Weapons/BaseWeapon.cs | 7 +- .../Items/Weapons/Ranged/BaseRanged.cs | 5 + .../Mobiles/Abilities/FanningFire.cs | 29 ++- .../Mobiles/Abilities/MonsterAbilities.cs | 2 +- .../Monsters/ML/Bedlam/LadyJennifyr.cs | 165 ++++++------------ 5 files changed, 89 insertions(+), 119 deletions(-) diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 85489edf3..c75b6bbcf 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -890,6 +890,11 @@ public abstract partial class BaseWeapon if (attacker is BaseCreature bc) { + if (bc.TriggerAbility(MonsterAbilityTrigger.CombatAction, defender)) + { + return GetDelay(attacker); + } + // Only change direction if they are not a player. attacker.Direction = attacker.GetDirectionTo(defender); var ab = bc.GetWeaponAbility(); @@ -1350,7 +1355,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) { diff --git a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs index bbe80daf6..b50a7b353 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs @@ -70,6 +70,11 @@ namespace Server.Items attacker.DisruptiveAction(); attacker.NetState.SendSwing(attacker.Serial, defender.Serial); + if (attacker is BaseCreature bc && bc.TriggerAbility(MonsterAbilityTrigger.CombatAction, defender)) + { + return GetDelay(attacker); + } + if (OnFired(attacker, defender)) { if (CheckHit(attacker, defender)) diff --git a/Projects/UOContent/Mobiles/Abilities/FanningFire.cs b/Projects/UOContent/Mobiles/Abilities/FanningFire.cs index a06bb561f..99e65f5c6 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.CombatAction; + + 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/Monsters/ML/Bedlam/LadyJennifyr.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs index 6b7a89005..f09b345ca 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs @@ -1,122 +1,71 @@ 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 + [Constructible] + public LadyJennifyr() { - private static readonly Dictionary m_Table = new(); + IsParagon = true; - [Constructible] - public LadyJennifyr() - { - IsParagon = true; + Hue = 0x76D; - Hue = 0x76D; + SetStr(208, 309); + SetDex(91, 118); + SetInt(44, 101); - SetStr(208, 309); - SetDex(91, 118); - SetInt(44, 101); + SetHits(1113, 1285); - SetHits(1113, 1285); + SetDamage(15, 25); - SetDamage(15, 25); + SetDamageType(ResistanceType.Physical, 40); + SetDamageType(ResistanceType.Cold, 60); - 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); - 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); - 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); - } - - public override void OnGaveMeleeAttack(Mobile defender, int damage) - { - 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); - } - } + 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; }