diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 6c61e4b96..0ef9c036c 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1062,6 +1062,9 @@ public static class Utility [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool RandomBool() => RandomSources.Source.NextBool(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan RandomMinMax(TimeSpan min, TimeSpan max) => new(RandomMinMax(min.Ticks, max.Ticks)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double RandomMinMax(double min, double max) { @@ -1540,4 +1543,67 @@ public static class Utility return combined; } + + public static Point3D GetValidLocation(Map map, Point3D center, int range, int retries = 10) + { + if (map == null) + { + return center; + } + + var loc = new Point3D(center.Z, center.Y, center.Z); + + for (var i = 0; i < retries; i++) + { + loc.X = center.X + (Random(range * 2 + 1) - range); + loc.Y = center.Y + (Random(range * 2 + 1) - range); + loc.Z = center.Z; + + if (map.CanSpawnMobile(loc)) + { + return loc; + } + + loc.Z = map.GetAverageZ(loc.X, loc.Y); + + if (map.CanSpawnMobile(loc)) + { + return loc; + } + } + + return center; + } + + public static Point3D GetValidLocationInLOS(Map map, Mobile from, int range, int retries = 10) + { + if (map == null) + { + return from.Location; + } + + var center = from.Location; + var loc = center; + + for (var i = 0; i < retries; i++) + { + loc.X = center.X + (Random(range * 2 + 1) - range); + loc.Y = center.Y + (Random(range * 2 + 1) - range); + loc.Z = center.Z; + + if (map.CanSpawnMobile(loc) && map.LineOfSight(from, loc)) + { + return loc; + } + + loc.Z = map.GetAverageZ(loc.X, loc.Y); + + if (map.CanSpawnMobile(loc) && map.LineOfSight(from, loc)) + { + return loc; + } + } + + return center; + } } diff --git a/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs b/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs index ec13266a9..33d6aeb85 100644 --- a/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs +++ b/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs @@ -39,32 +39,26 @@ namespace Server.Mobiles ControlSlots = 1; } - public UnholySteed(Serial serial) - : base(serial) + public UnholySteed(Serial serial) : base(serial) { } public override string CorpseName => "an unholy corpse"; public override bool IsDispellable => false; public override bool IsBondable => false; - - public override bool HasBreath => true; - public override bool CanBreath => true; - public override FoodType FavoriteFood => FoodType.FruitsAndVegies | FoodType.GrainsAndHay; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override string ApplyNameSuffix(string suffix) { if (suffix.Length == 0) { - suffix = Ethic.Evil.Definition.Adjunct.String; - } - else - { - suffix = $"{suffix} {Ethic.Evil.Definition.Adjunct.String}"; + return base.ApplyNameSuffix(Ethic.Evil.Definition.Adjunct.String); } - return base.ApplyNameSuffix(suffix); + return base.ApplyNameSuffix($"{suffix} {Ethic.Evil.Definition.Adjunct.String}"); } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs b/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs index da52600f8..ada515e9d 100644 --- a/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs +++ b/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs @@ -39,32 +39,26 @@ namespace Server.Mobiles ControlSlots = 1; } - public HolySteed(Serial serial) - : base(serial) + public HolySteed(Serial serial) : base(serial) { } public override string CorpseName => "a holy corpse"; public override bool IsDispellable => false; public override bool IsBondable => false; - - public override bool HasBreath => true; - public override bool CanBreath => true; - public override FoodType FavoriteFood => FoodType.FruitsAndVegies | FoodType.GrainsAndHay; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override string ApplyNameSuffix(string suffix) { if (suffix.Length == 0) { - suffix = Ethic.Hero.Definition.Adjunct.String; - } - else - { - suffix = $"{suffix} {Ethic.Hero.Definition.Adjunct.String}"; + return base.ApplyNameSuffix(Ethic.Hero.Definition.Adjunct.String); } - return base.ApplyNameSuffix(suffix); + return base.ApplyNameSuffix($"{suffix} {Ethic.Hero.Definition.Adjunct.String}"); } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs index 86e97b7f0..3e58605d6 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -247,7 +247,7 @@ namespace Server.Items public static bool IsMageryCreature(BaseCreature bc) => bc?.AI == AIType.AI_Mage && bc.Skills.Magery.Base > 5.0; - public static bool IsFireBreathingCreature(BaseCreature bc) => bc?.HasBreath == true; + public static bool IsFireBreathingCreature(BaseCreature bc) => bc?.HasAbility(MonsterAbilityType.FireBreath) == true; public static bool IsPoisonImmune(BaseCreature bc) => bc?.PoisonImmune != null; diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index 800e83955..498cb6e2f 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -83,8 +83,7 @@ namespace Server.Items public static readonly WeaponAbility InfusedThrow = Abilities[30]; public static readonly WeaponAbility MysticArc = Abilities[31]; - private static readonly Dictionary m_PlayersTable = - new(); + private static readonly Dictionary m_PlayersTable = new(); public virtual int BaseMana => 0; diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 4fafd73c9..01f5300da 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -2085,8 +2085,8 @@ namespace Server.Items } } - bcAtt?.OnGaveMeleeAttack(defender); - bcDef?.OnGotMeleeAttack(attacker); + bcAtt?.OnGaveMeleeAttack(defender, damage); + bcDef?.OnGotMeleeAttack(attacker, damage); a?.OnHit(attacker, defender, damage); move?.OnHit(attacker, defender, damage); diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index b52879118..0085bdd9b 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -37,14 +37,12 @@ namespace Server public static int Damage( Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy, int chaos - ) => - Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, chaos); + ) => Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, chaos); public static int Damage( Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy, bool keepAlive - ) => - Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, 0, 0, keepAlive); + ) => Damage(m, from, damage, false, phys, fire, cold, pois, nrgy, 0, 0, keepAlive); public static int Damage( Mobile m, Mobile from, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois, @@ -80,20 +78,30 @@ namespace Server switch (Utility.Random(5)) { case 0: - phys += chaos; - break; + { + phys += chaos; + break; + } case 1: - fire += chaos; - break; + { + fire += chaos; + break; + } case 2: - cold += chaos; - break; + { + cold += chaos; + break; + } case 3: - pois += chaos; - break; + { + pois += chaos; + break; + } case 4: - nrgy += chaos; - break; + { + nrgy += chaos; + break; + } } } diff --git a/Projects/UOContent/Mobiles/Abilities/ChaosBreath.cs b/Projects/UOContent/Mobiles/Abilities/ChaosBreath.cs new file mode 100644 index 000000000..fa6dc8847 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/ChaosBreath.cs @@ -0,0 +1,8 @@ +namespace Server.Mobiles; + +public class ChaosBreath : FireBreath +{ + public override int BreathChaosDamage => 100; + + public override int BreathFireDamage => 0; +} diff --git a/Projects/UOContent/Mobiles/Abilities/ColdBreath.cs b/Projects/UOContent/Mobiles/Abilities/ColdBreath.cs new file mode 100644 index 000000000..8e6066b66 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/ColdBreath.cs @@ -0,0 +1,8 @@ +namespace Server.Mobiles; + +public class ColdBreath : FireBreath +{ + public override int BreathColdDamage => 100; + public override int BreathFireDamage => 0; + public override int BreathEffectHue => 0x480; +} diff --git a/Projects/UOContent/Mobiles/Abilities/FireBreath.cs b/Projects/UOContent/Mobiles/Abilities/FireBreath.cs new file mode 100644 index 000000000..f5038dbb6 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/FireBreath.cs @@ -0,0 +1,218 @@ +using System; +using Server.Spells.Bushido; + +namespace Server.Mobiles; + +public class FireBreath : MonsterAbility +{ + public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.Action; + public override MonsterAbilityType AbilityType => MonsterAbilityType.FireBreath; + public override double ChanceToTrigger => 0.5; + + // Min/max seconds until next breath + public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(30.0); + public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(45.0); + + // Base damage given is: CurrentHitPoints * BreathDamageScalar + public virtual double BreathDamageScalar => Core.AOS ? 0.16 : 0.05; + + // Creature stops moving for 1.0 seconds while breathing + public virtual double BreathStallTime => 1.0; + + // Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played + public virtual double BreathEffectDelay => 1.3; + + // Damage is given 1.0 seconds after effect is sent + public virtual double BreathDamageDelay => 1.0; + public virtual int BreathRange(BaseCreature source) => source.RangePerception; + + // Damage Types + public virtual int BreathChaosDamage => 0; + public virtual int BreathPhysicalDamage => 0; + public virtual int BreathFireDamage => 100; + public virtual int BreathColdDamage => 0; + public virtual int BreathPoisonDamage => 0; + public virtual int BreathEnergyDamage => 0; + + // Effect details and sound + public virtual int BreathEffectItemID => 0x36D4; + public virtual int BreathEffectSpeed => 5; + public virtual int BreathEffectDuration => 0; + public virtual bool BreathEffectExplodes => false; + public virtual bool BreathEffectFixedDir => false; + public virtual int BreathEffectHue => 0; + public virtual int BreathEffectRenderMode => 0; + + public virtual int BreathEffectSound => 0x227; + + public override bool CanTrigger(BaseCreature source) => !source.Summoned && base.CanTrigger(source); + + public override void Trigger(BaseCreature source, Mobile target) + { + if (CanFireBreathTarget(source, target)) + { + BreathStallMovement(source); + BreathPlayAngerSound(source); + BreathPlayAngerAnimation(source); + + source.Direction = source.GetDirectionTo(target); + + Timer.StartTimer(TimeSpan.FromSeconds(BreathEffectDelay), () => BreathEffect_Callback(source, target)); + + base.Trigger(source, target); + } + } + + public virtual bool CanFireBreathTarget(BaseCreature source, Mobile target) + { + var range = BreathRange(source); + + return !source.IsDeadBondedPet && !source.BardPacified && target is { Alive: true, IsDeadBondedPet: false } && + target.Map == source.Map && source.CanBeHarmful(target) && target.InRange(source, range) && + source.InLOS(target); + } + + // Anger sound/animations + public virtual int BreathAngerSound(BaseCreature source) => source.GetAngerSound(); + public virtual int BreathAngerAnimation => 12; + + public virtual void BreathStallMovement(BaseCreature source) + { + if (source.AIObject != null) + { + source.AIObject.NextMove = Core.TickCount + (int)(BreathStallTime * 1000); + } + } + + public virtual void BreathPlayAngerSound(BaseCreature source) + { + var sound = BreathAngerSound(source); + source.PlaySound(sound); + } + + public virtual void BreathPlayAngerAnimation(BaseCreature source) + { + source.Animate(BreathAngerAnimation, 5, 1, true, false, 0); + } + + public virtual void BreathEffect_Callback(BaseCreature source, Mobile target) + { + if (!target.Alive || !source.CanBeHarmful(target)) + { + return; + } + + BreathPlayEffectSound(source); + BreathPlayEffect(source, target); + + Timer.StartTimer(TimeSpan.FromSeconds(BreathDamageDelay), () => BreathDamage_Callback(source, target)); + } + + public virtual void BreathPlayEffectSound(BaseCreature source) + { + source.PlaySound(BreathEffectSound); + } + + public virtual void BreathPlayEffect(BaseCreature source, Mobile target) + { + Effects.SendMovingEffect( + source, + target, + BreathEffectItemID, + BreathEffectSpeed, + BreathEffectDuration, + BreathEffectFixedDir, + BreathEffectExplodes, + BreathEffectHue, + BreathEffectRenderMode + ); + } + + public virtual void BreathDamage_Callback(BaseCreature source, Mobile target) + { + if (target is BaseCreature creature && creature.BreathImmune) + { + return; + } + + if (source.CanBeHarmful(target)) + { + source.DoHarmful(target); + BreathDealDamage(source, target); + } + } + + public virtual void BreathDealDamage(BaseCreature source, Mobile target) + { + if (!Evasion.CheckSpellEvasion(target)) + { + var physDamage = BreathPhysicalDamage; + var fireDamage = BreathFireDamage; + var coldDamage = BreathColdDamage; + var poisDamage = BreathPoisonDamage; + var nrgyDamage = BreathEnergyDamage; + + if (BreathChaosDamage > 0) + { + switch (Utility.Random(5)) + { + case 0: + { + physDamage += BreathChaosDamage; + break; + } + case 1: + { + fireDamage += BreathChaosDamage; + break; + } + case 2: + { + coldDamage += BreathChaosDamage; + break; + } + case 3: + { + poisDamage += BreathChaosDamage; + break; + } + case 4: + { + nrgyDamage += BreathChaosDamage; + break; + } + } + } + + if (physDamage == 0 && fireDamage == 0 && coldDamage == 0 && poisDamage == 0 && nrgyDamage == 0) + { + target.Damage(BreathComputeDamage(source), source); // Unresistable damage even in AOS + } + else + { + AOS.Damage( + target, + source, + BreathComputeDamage(source), + physDamage, + fireDamage, + coldDamage, + poisDamage, + nrgyDamage + ); + } + } + } + + public virtual int BreathComputeDamage(BaseCreature source) + { + var damage = (int)(source.Hits * BreathDamageScalar); + + if (source.IsParagon) + { + damage = (int)(damage / Paragon.HitsBuff); + } + + return Math.Min(damage, 200); + } +} diff --git a/Projects/UOContent/Mobiles/Abilities/GraspingClaw.cs b/Projects/UOContent/Mobiles/Abilities/GraspingClaw.cs new file mode 100644 index 000000000..42b9256fc --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/GraspingClaw.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace Server.Mobiles; + +public class GraspingClaw : MonsterAbility +{ + private Dictionary _table; + + public override MonsterAbilityType AbilityType => MonsterAbilityType.GraspingClaw; + public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.GiveMeleeDamage; + public override double ChanceToTrigger => 0.10; + + public override void Trigger(BaseCreature source, Mobile target) + { + if (_table.Remove(target, out var timer)) + { + timer.DoExpire(); + target.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state. + } + else + { + // The blow from the creature's claws has made you more susceptible to physical attacks. + target.SendLocalizedMessage(1070836); + } + + /** + * Grasping Claw + * Start cliloc: 1070836 + * Effect: Physical resistance -15% for 5 seconds + * Refresh Cliloc: 1070837 + * End cliloc: 1070838 + * Effect: + * - Type: "3" + * - From: Player + * - To: 0x0 + * - ItemId: "0x37B9" + * - ItemIdName: "glow" + * - FromLocation: "(1149 808, 32)" + * - ToLocation: "(1149 808, 32)" + * - Speed: "10" - Duration: "5" + * - FixedDirection: "True" + * - Explode: "False" + */ + var effect = -(target.PhysicalResistance * 15 / 100); + + var mod = new ResistanceMod(ResistanceType.Physical, effect); + + target.FixedEffect(0x37B9, 10, 5); + target.AddResistanceMod(mod); + + timer = new ExpireTimer(this, target, mod, TimeSpan.FromSeconds(5.0)); + timer.Start(); + + _table ??= new Dictionary(); + _table[target] = timer; + + base.Trigger(source, target); + } + + private class ExpireTimer : Timer + { + private GraspingClaw _graspingClaw; + private Mobile _mobile; + private ResistanceMod _mod; + + public ExpireTimer(GraspingClaw claw, Mobile m, ResistanceMod mod, TimeSpan delay) : base(delay) + { + _mobile = m; + _mod = mod; + _graspingClaw = claw; + } + + public void DoExpire() + { + _mobile.RemoveResistanceMod(_mod); + Stop(); + _graspingClaw._table?.Remove(_mobile); + } + + protected override void OnTick() + { + _mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned. + DoExpire(); + } + } +} diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs new file mode 100644 index 000000000..fd02f4c36 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs @@ -0,0 +1,15 @@ +namespace Server.Mobiles; + +public abstract partial class MonsterAbility +{ + // Fire Breath + public static FireBreath FireBreath => new(); + public static ChaosBreath ChaosBreath => new(); + public static ColdBreath ColdBreath => new(); + + public static GraspingClaw GraspingClaw => new(); + + // Summon Undead + public static SummonSkeletons SummonSkeletons => new(); + public static SummonLesserUndead SummonLesserUndead => new(); +} diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs new file mode 100644 index 000000000..82b6b9c36 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; + +namespace Server.Mobiles; + +/// +/// Abstract class used to build singletons for managing a specific monster ability. +/// +public abstract partial class MonsterAbility +{ + private Dictionary _nextTriggerTicks; + + public virtual MonsterAbilityType AbilityType => MonsterAbilityType.Generic; + + public virtual MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.None; + + public virtual double ChanceToTrigger => 1.0; + public virtual TimeSpan MinTriggerCooldown => TimeSpan.Zero; + public virtual TimeSpan MaxTriggerCooldown => TimeSpan.Zero; + + public bool WillTrigger(MonsterAbilityTrigger trigger) => (AbilityTrigger & trigger) != 0; + + /// + /// Returns true if ability is not on cooldown, and the change to trigger succeeds. + /// + /// The mobile this ability is attached to + /// Boolean indicating the ability can trigger. + public virtual bool CanTrigger(BaseCreature source) + { + if (_nextTriggerTicks?.TryGetValue(source, out var nextTrigger) == true && Core.TickCount < nextTrigger) + { + return false; + } + + var c = ChanceToTrigger; + + if (c >= 1) + { + return true; + } + + if (c <= 0) + { + return false; + } + + var rnd = Utility.RandomDouble(); + + return c > rnd; + } + + /// + /// Triggers the monster's ability. Override this and call `base.Trigger(source);` to make sure + /// the cooldown is tracked. + /// + /// + public virtual void Trigger(BaseCreature source, Mobile target) + { + if (MinTriggerCooldown <= TimeSpan.Zero && MaxTriggerCooldown <= TimeSpan.Zero) + { + return; + } + + var randomAmount = MinTriggerCooldown == MaxTriggerCooldown + ? MinTriggerCooldown + : Utility.RandomMinMax(MinTriggerCooldown, MaxTriggerCooldown); + + var nextTrigger = Core.TickCount + (long)randomAmount.TotalMilliseconds; + + _nextTriggerTicks ??= new Dictionary(); + _nextTriggerTicks[source] = nextTrigger; + } +} diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs new file mode 100644 index 000000000..6bd4a9455 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityTrigger.cs @@ -0,0 +1,16 @@ +using System; + +namespace Server.Mobiles; + +[Flags] +public enum MonsterAbilityTrigger +{ + None = 0x00000000, // Passive, or some custom event. + Action = 0x00000001, // On Think + TakeMeleeDamage = 0x00000002, + GiveMeleeDamage = 0x00000004, // Includes firing a bow + TakeSpellDamage = 0x00000008, + GiveSpellDamage = 0x00000010, + + TakeDamage = TakeMeleeDamage | TakeSpellDamage +} diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs new file mode 100644 index 000000000..422e6962f --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs @@ -0,0 +1,9 @@ +namespace Server.Mobiles; + +public enum MonsterAbilityType +{ + Generic, + FireBreath, + GraspingClaw, + SummonUndead +} diff --git a/Projects/UOContent/Mobiles/Abilities/SummonLesserUndead.cs b/Projects/UOContent/Mobiles/Abilities/SummonLesserUndead.cs new file mode 100644 index 000000000..2114960f7 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/SummonLesserUndead.cs @@ -0,0 +1,33 @@ +namespace Server.Mobiles; + +public class SummonLesserUndead : SummonUndead +{ + private int _group; + + public override void Trigger(BaseCreature source, Mobile target) + { + _group = Utility.Random(2); + base.Trigger(source, target); + } + + public override BaseCreature CreateSummon(BaseCreature source) + { + return _group switch + { + 0 => Utility.Random(4) switch + { + 0 => new Skeleton(), + 1 => new SkeletalMage(), + 2 => new Mummy(), + 3 => new Zombie(), + }, + _ => Utility.Random(4) switch + { + 0 => new Ghoul(), + 1 => new Spectre(), + 2 => new Shade(), + 3 => new Bogle() + } + }; + } +} diff --git a/Projects/UOContent/Mobiles/Abilities/SummonSkeletons.cs b/Projects/UOContent/Mobiles/Abilities/SummonSkeletons.cs new file mode 100644 index 000000000..187008428 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/SummonSkeletons.cs @@ -0,0 +1,6 @@ +namespace Server.Mobiles; + +public class SummonSkeletons : SummonUndead +{ + public override BaseCreature CreateSummon(BaseCreature source) => new Skeleton(); +} diff --git a/Projects/UOContent/Mobiles/Abilities/SummonUndead.cs b/Projects/UOContent/Mobiles/Abilities/SummonUndead.cs new file mode 100644 index 000000000..8d94b9759 --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/SummonUndead.cs @@ -0,0 +1,69 @@ +using System; + +namespace Server.Mobiles; + +public abstract class SummonUndead : MonsterAbility +{ + public override MonsterAbilityType AbilityType => MonsterAbilityType.SummonUndead; + public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.TakeDamage; + + public override double ChanceToTrigger => 0.05; + public override TimeSpan MinTriggerCooldown => TimeSpan.FromMinutes(5); + public override TimeSpan MaxTriggerCooldown => TimeSpan.FromMinutes(5); + + public virtual double ChanceToPolymorph => 0.25; + public virtual TimeSpan PolymorphDuration => TimeSpan.FromMinutes(5); + public virtual int GetTransformBodyValue() => Utility.RandomBool() ? 50 : 56; + public virtual int AmountToSummon => 4; + public virtual int SummonRange => 4; + + public abstract BaseCreature CreateSummon(BaseCreature source); + + public virtual bool CanTrigger(BaseCreature source) => source.Followers < AmountToSummon && CanTrigger(source); + + public override void Trigger(BaseCreature source, Mobile target) + { + var amount = AmountToSummon - source.Followers; + var distance = SummonRange; + + int willTransform = !source.Paralyzed && ChanceToPolymorph > 0 && ChanceToPolymorph > Utility.RandomDouble() + ? Utility.Random(amount) + : -1; + + for (var i = 0; i < amount; i++) + { + var loc = Utility.GetValidLocationInLOS(source.Map, source, distance); + BaseCreature summon; + if (i == willTransform) + { + summon = source; + source.BodyMod = GetTransformBodyValue(); + source.HueMod = 0; + // Name is not changed + Timer.DelayCall(PolymorphDuration, EndPolymorph, source); + } + else + { + summon = CreateSummon(source); + summon.Team = source.Team; + summon.Summoned = true; + summon.SummonMaster = source; + summon.FightMode = FightMode.Closest; + summon.Combatant = target; + } + + summon.MoveToWorld(loc, source.Map); + Effects.SendLocationEffect(summon.Location, summon.Map, 0x3728, 10); + summon.PlaySound(0x48F); + summon.PlaySound(summon.GetAttackSound()); + } + + base.Trigger(source, target); + } + + private static void EndPolymorph(BaseCreature source) + { + source.BodyMod = 0; + source.HueMod = -1; + } +} diff --git a/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs b/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs index 346b50747..bd707049f 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs @@ -46,12 +46,14 @@ namespace Server.Mobiles public override string CorpseName => "a hell cat corpse"; public override string DefaultName => "a hell cat"; - public override bool HasBreath => true; // fire breath enabled public override int Hides => 10; public override HideType HideType => HideType.Spined; public override FoodType FavoriteFood => FoodType.Meat; public override PackInstinct PackInstinct => PackInstinct.Feline; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Meager); diff --git a/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs b/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs index 612497376..fd08af802 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs @@ -44,13 +44,14 @@ namespace Server.Mobiles public override string CorpseName => "a hell cat corpse"; public override string DefaultName => "a hell cat"; - - public override bool HasBreath => true; // fire breath enabled public override int Hides => 10; public override HideType HideType => HideType.Spined; public override FoodType FavoriteFood => FoodType.Meat; public override PackInstinct PackInstinct => PackInstinct.Feline; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Average); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs b/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs index 5dfdd5e18..86b017f36 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs @@ -44,11 +44,12 @@ namespace Server.Mobiles } public override string CorpseName => "a fire steed corpse"; - - public override bool HasBreath => true; // fire breath enabled public override FoodType FavoriteFood => FoodType.Meat; public override PackInstinct PackInstinct => PackInstinct.Daemon | PackInstinct.Equine; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { PackItem(new SulfurousAsh(Utility.RandomMinMax(151, 300))); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs b/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs index 5d00d1ab3..094510075 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs @@ -13,10 +13,11 @@ namespace Server.Mobiles } public override string CorpseName => "a hellsteed corpse"; - public override bool HasBreath => true; - public override int BreathChaosDamage => 100; public override Poison PoisonImmune => Poison.Lethal; + private static MonsterAbility[] _abilities = { MonsterAbility.ChaosBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public static void SetStats(BaseCreature steed) { steed.SetStr(201, 210); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs index aea42872d..8b068a014 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs @@ -1,280 +1,161 @@ using System; -using System.Collections.Generic; using Server.Engines.Plants; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class Hiryu : BaseMount { - public class Hiryu : BaseMount + [Constructible] + public Hiryu() : base("a hiryu", 243, 0x3E94, AIType.AI_Melee) { - private static readonly Dictionary m_Table = new(); + Hue = GetHue(); - [Constructible] - public Hiryu() : base("a hiryu", 243, 0x3E94, AIType.AI_Melee) + SetStr(1201, 1410); + SetDex(171, 270); + SetInt(301, 325); + + SetHits(901, 1100); + SetMana(60); + + SetDamage(20, 30); + + SetDamageType(ResistanceType.Physical, 100); + + SetResistance(ResistanceType.Physical, 55, 70); + SetResistance(ResistanceType.Fire, 70, 90); + SetResistance(ResistanceType.Cold, 15, 25); + SetResistance(ResistanceType.Poison, 40, 50); + SetResistance(ResistanceType.Energy, 40, 50); + + SetSkill(SkillName.Anatomy, 75.1, 80.0); + SetSkill(SkillName.MagicResist, 85.1, 100.0); + SetSkill(SkillName.Tactics, 100.1, 110.0); + SetSkill(SkillName.Wrestling, 100.1, 120.0); + + Fame = 18000; + Karma = -18000; + + Tamable = true; + ControlSlots = 4; + MinTameSkill = 98.7; + + if (Utility.RandomDouble() < .33) { - Hue = GetHue(); - - SetStr(1201, 1410); - SetDex(171, 270); - SetInt(301, 325); - - SetHits(901, 1100); - SetMana(60); - - SetDamage(20, 30); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 55, 70); - SetResistance(ResistanceType.Fire, 70, 90); - SetResistance(ResistanceType.Cold, 15, 25); - SetResistance(ResistanceType.Poison, 40, 50); - SetResistance(ResistanceType.Energy, 40, 50); - - SetSkill(SkillName.Anatomy, 75.1, 80.0); - SetSkill(SkillName.MagicResist, 85.1, 100.0); - SetSkill(SkillName.Tactics, 100.1, 110.0); - SetSkill(SkillName.Wrestling, 100.1, 120.0); - - Fame = 18000; - Karma = -18000; - - Tamable = true; - ControlSlots = 4; - MinTameSkill = 98.7; - - if (Utility.RandomDouble() < .33) - { - PackItem(Seed.RandomBonsaiSeed()); - } - - if (Core.ML && Utility.RandomDouble() < .33) - { - PackItem(Seed.RandomPeculiarSeed(3)); - } + PackItem(Seed.RandomBonsaiSeed()); } - public Hiryu(Serial serial) - : base(serial) + if (Core.ML && Utility.RandomDouble() < .33) { + PackItem(Seed.RandomPeculiarSeed(3)); + } + } + + public Hiryu(Serial serial) : base(serial) + { + } + + public override string CorpseName => "a hiryu corpse"; + public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using per landed hit */ + + public override bool StatLossAfterTame => true; + + public override int TreasureMapLevel => 5; + public override int Meat => 16; + public override int Hides => 60; + public override FoodType FavoriteFood => FoodType.Meat; + public override bool CanAngerOnTame => true; + + private static MonsterAbility[] _abilities = { MonsterAbility.GraspingClaw }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + + public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount; + + private static int GetHue() + { + return Utility.Random(1075) switch + { + 1074 => 0x85C, // Strong Green 0.09% + 1073 => 0x490, // Strong Purple 0.09% + >= 1071 => 0x030, // Green 0.19% + >= 1069 => 0x037, // Strong Yellow 0.19% + >= 1066 => 0x295, // Light Green 0.28% + >= 1063 => 0x123, // Cyan 0.28% + >= 1058 => 0x482, // Ice Blue 0.47% + >= 1050 => 0x487, // Blue/Yellow 0.74% + >= 1040 => CraftResources.GetHue(CraftResource.Gold), // Gold 0.93% + >= 1030 => CraftResources.GetHue(CraftResource.Agapite), // Agapite 0.93% + >= 1020 => 0x495, // Strong Cyan 0.93% + >= 1010 => 0x48D, // Light Blue 0.93% + >= 1000 => 0x47F, // Ice Green 0.93% + _ => 0 // No Hue 93.02% + } | 0x8000; + } + + public override int GetAngerSound() => 0x4FE; + + public override int GetIdleSound() => 0x4FD; + + public override int GetAttackSound() => 0x4FC; + + public override int GetHurtSound() => 0x4FF; + + public override int GetDeathSound() => 0x4FB; + + public override void GenerateLoot() + { + AddLoot(LootPack.FilthyRich, 3); + AddLoot(LootPack.Gems, 4); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(2); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + + if (version <= 1) + { + Timer.StartTimer(() => Fix(version)); } - public override string CorpseName => "a hiryu corpse"; - public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using per landed hit */ - - public override bool StatLossAfterTame => true; - - public override int TreasureMapLevel => 5; - public override int Meat => 16; - public override int Hides => 60; - public override FoodType FavoriteFood => FoodType.Meat; - public override bool CanAngerOnTame => true; - - public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount; - - private static int GetHue() + if (version < 2) { - var rand = Utility.Random(1075); - - if (rand <= 0) + for (var i = 0; i < Skills.Length; ++i) { - return 0x855C; - } + Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9); - if (rand <= 1) - { - return 0x8490; - } - - if (rand <= 3) - { - return 0x8030; - } - - if (rand <= 5) - { - return 0x8037; - } - - if (rand <= 8) - { - return 0x8295; - } - - if (rand <= 11) - { - return 0x8123; - } - - if (rand <= 16) - { - return 0x8482; - } - - if (rand <= 24) - { - return 0x8487; - } - - if (rand <= 34) - { - return 0x8032; - } - - if (rand <= 44) - { - return 0x8899; - } - - if (rand <= 54) - { - return 0x8495; - } - - if (rand <= 64) - { - return 0x848D; - } - - if (rand <= 74) - { - return 0x847F; - } - - return 0; - } - - public override int GetAngerSound() => 0x4FE; - - public override int GetIdleSound() => 0x4FD; - - public override int GetAttackSound() => 0x4FC; - - public override int GetHurtSound() => 0x4FF; - - public override int GetDeathSound() => 0x4FB; - - public override void GenerateLoot() - { - AddLoot(LootPack.FilthyRich, 3); - AddLoot(LootPack.Gems, 4); - } - - public override void OnGaveMeleeAttack(Mobile defender) - { - base.OnGaveMeleeAttack(defender); - - if (Utility.RandomDouble() >= 0.1) - { - return; - } - - /* Grasping Claw - * Start cliloc: 1070836 - * Effect: Physical resistance -15% for 5 seconds - * End cliloc: 1070838 - * Effect: Type: "3" - From: "0x57D4F5B" (player) - To: "0x0" - ItemId: "0x37B9" - ItemIdName: "glow" - FromLocation: "(1149 808, 32)" - ToLocation: "(1149 808, 32)" - Speed: "10" - Duration: "5" - FixedDirection: "True" - Explode: "False" - */ - - if (m_Table.TryGetValue(defender, out var timer)) - { - timer.DoExpire(); - defender.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state. - } - else - { - defender.SendLocalizedMessage( - 1070836 - ); // The blow from the creature's claws has made you more susceptible to physical attacks. - } - - var effect = -(defender.PhysicalResistance * 15 / 100); - - var mod = new ResistanceMod(ResistanceType.Physical, effect); - - defender.FixedEffect(0x37B9, 10, 5); - defender.AddResistanceMod(mod); - - timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(5.0)); - timer.Start(); - m_Table[defender] = timer; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(2); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - if (version <= 1) - { - Timer.StartTimer(() => Fix(version)); - } - - if (version < 2) - { - for (var i = 0; i < Skills.Length; ++i) + if (Skills[i].Base > Skills[i].Cap) { - Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9); - - if (Skills[i].Base > Skills[i].Cap) - { - Skills[i].Base = Skills[i].Cap; - } + Skills[i].Base = Skills[i].Cap; } } } + } - private void Fix(int version) + private void Fix(int version) + { + switch (version) { - switch (version) - { - case 1: + case 1: + { + if (InternalItem != null) { - if (InternalItem != null) - { - InternalItem.Hue = Hue; - } - - goto case 0; + InternalItem.Hue = Hue; } - case 0: - { - Hue = GetHue(); - break; - } - } - } - private class ExpireTimer : Timer - { - private readonly Mobile m_Mobile; - private readonly ResistanceMod m_Mod; - - public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay) - : base(delay) - { - m_Mobile = m; - m_Mod = mod; - } - - public void DoExpire() - { - m_Mobile.RemoveResistanceMod(m_Mod); - Stop(); - m_Table.Remove(m_Mobile); - } - - protected override void OnTick() - { - m_Mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned. - DoExpire(); - } + goto case 0; + } + case 0: + { + Hue = GetHue(); + break; + } } } } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs b/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs index f6c0167fc..b188156e4 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs @@ -1,282 +1,176 @@ using System; -using System.Collections.Generic; using Server.Engines.Plants; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class LesserHiryu : BaseMount { - public class LesserHiryu : BaseMount + [Constructible] + public LesserHiryu() : base("a lesser hiryu", 243, 0x3E94, AIType.AI_Melee) { - private static readonly Dictionary m_Table = new(); + Hue = GetHue(); - [Constructible] - public LesserHiryu() : base("a lesser hiryu", 243, 0x3E94, AIType.AI_Melee) + SetStr(301, 410); + SetDex(171, 270); + SetInt(301, 325); + + SetHits(401, 600); + SetMana(60); + + SetDamage(18, 23); + + SetDamageType(ResistanceType.Physical, 100); + + SetResistance(ResistanceType.Physical, 45, 70); + SetResistance(ResistanceType.Fire, 60, 80); + SetResistance(ResistanceType.Cold, 5, 15); + SetResistance(ResistanceType.Poison, 30, 40); + SetResistance(ResistanceType.Energy, 30, 40); + + SetSkill(SkillName.Anatomy, 75.1, 80.0); + SetSkill(SkillName.MagicResist, 85.1, 100.0); + SetSkill(SkillName.Tactics, 100.1, 110.0); + SetSkill(SkillName.Wrestling, 100.1, 120.0); + + Fame = 10000; + Karma = -10000; + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 98.7; + + if (Utility.RandomDouble() < .33) { - Hue = GetHue(); + PackItem(Seed.RandomBonsaiSeed()); + } + } - SetStr(301, 410); - SetDex(171, 270); - SetInt(301, 325); + public LesserHiryu(Serial serial) : base(serial) + { + } - SetHits(401, 600); - SetMana(60); + public override string CorpseName => "a hiryu corpse"; + public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using; 1 in 5 chance of success */ - SetDamage(18, 23); + public override bool StatLossAfterTame => true; - SetDamageType(ResistanceType.Physical, 100); + public override int TreasureMapLevel => 3; + public override int Meat => 16; + public override int Hides => 60; + public override FoodType FavoriteFood => FoodType.Meat; + public override bool CanAngerOnTame => true; - SetResistance(ResistanceType.Physical, 45, 70); - SetResistance(ResistanceType.Fire, 60, 80); - SetResistance(ResistanceType.Cold, 5, 15); - SetResistance(ResistanceType.Poison, 30, 40); - SetResistance(ResistanceType.Energy, 30, 40); + public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount; - SetSkill(SkillName.Anatomy, 75.1, 80.0); - SetSkill(SkillName.MagicResist, 85.1, 100.0); - SetSkill(SkillName.Tactics, 100.1, 110.0); - SetSkill(SkillName.Wrestling, 100.1, 120.0); + private static MonsterAbility[] _abilities = { MonsterAbility.GraspingClaw }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; - Fame = 10000; - Karma = -10000; + private static int GetHue() + { + return Utility.Random(525) switch + { + 524 => 0x258, // Midnight Blue 0.19% + 523 => CraftResources.GetHue(CraftResource.Valorite), // Valorite 0.19% + >= 520 => 0x7D4, // Dark Green 0.57% + >= 510 => 0x163, // Green 1.90% + >= 500 => 0x295, // Green 1.90% + _ => 0 // No Hue 95.24% + } | 0x8000; + } - Tamable = true; - ControlSlots = 3; - MinTameSkill = 98.7; + public override bool OverrideBondingReqs() => ControlMaster.Skills.Bushido.Base >= 90.0; - if (Utility.RandomDouble() < .33) - { - PackItem(Seed.RandomBonsaiSeed()); - } + public override int GetAngerSound() => 0x4FE; + + public override int GetIdleSound() => 0x4FD; + + public override int GetAttackSound() => 0x4FC; + + public override int GetHurtSound() => 0x4FF; + + public override int GetDeathSound() => 0x4FB; + + public override void GenerateLoot() + { + AddLoot(LootPack.FilthyRich, 2); + AddLoot(LootPack.Gems, 4); + } + + public override double GetControlChance(Mobile m, bool useBaseSkill = false) + { + var tamingChance = base.GetControlChance(m, useBaseSkill); + + if (tamingChance >= 0.95) + { + return tamingChance; } - public LesserHiryu(Serial serial) - : base(serial) + var skill = useBaseSkill ? m.Skills.Bushido.Base : m.Skills.Bushido.Value; + + if (skill < 90.0) { + return tamingChance; } - public override string CorpseName => "a hiryu corpse"; - public override double WeaponAbilityChance => 0.07; /* 1 in 15 chance of using; 1 in 5 chance of success */ + var bushidoChance = (skill - 30.0) / 100; - public override bool StatLossAfterTame => true; - - public override int TreasureMapLevel => 3; - public override int Meat => 16; - public override int Hides => 60; - public override FoodType FavoriteFood => FoodType.Meat; - public override bool CanAngerOnTame => true; - - public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount; - - private static int GetHue() + if (m.Skills.Bushido.Base >= 120) { - var rand = Utility.Random(527); - - /* - - 500 527 No Hue Color 94.88% 0 - 10 527 Green 1.90% 0x8295 - 10 527 Green 1.90% 0x8163 (Very Close to Above Green) //this one is an approximation - 5 527 Dark Green 0.95% 0x87D4 - 1 527 Valorite 0.19% 0x88AB - 1 527 Midnight Blue 0.19% 0x8258 - - * */ - - if (rand <= 0) - { - return 0x8258; - } - - if (rand <= 1) - { - return 0x88AB; - } - - if (rand <= 6) - { - return 0x87D4; - } - - if (rand <= 16) - { - return 0x8163; - } - - if (rand <= 26) - { - return 0x8295; - } - - return 0; + bushidoChance += 0.05; } - public override bool OverrideBondingReqs() - { - if (ControlMaster.Skills.Bushido.Base >= 90.0) - { - return true; - } + return bushidoChance > tamingChance ? bushidoChance : tamingChance; + } - return false; + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(2); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + + if (version <= 1) + { + Timer.StartTimer(() => Fix(version)); } - public override int GetAngerSound() => 0x4FE; - - public override int GetIdleSound() => 0x4FD; - - public override int GetAttackSound() => 0x4FC; - - public override int GetHurtSound() => 0x4FF; - - public override int GetDeathSound() => 0x4FB; - - public override void GenerateLoot() + if (version < 2) { - AddLoot(LootPack.FilthyRich, 2); - AddLoot(LootPack.Gems, 4); - } - - public override double GetControlChance(Mobile m, bool useBaseSkill = false) - { - var tamingChance = base.GetControlChance(m, useBaseSkill); - - if (tamingChance >= 0.95) + for (var i = 0; i < Skills.Length; ++i) { - return tamingChance; - } + Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9); - var skill = useBaseSkill ? m.Skills.Bushido.Base : m.Skills.Bushido.Value; - - if (skill < 90.0) - { - return tamingChance; - } - - var bushidoChance = (skill - 30.0) / 100; - - if (m.Skills.Bushido.Base >= 120) - { - bushidoChance += 0.05; - } - - return bushidoChance > tamingChance ? bushidoChance : tamingChance; - } - - public override void OnGaveMeleeAttack(Mobile defender) - { - base.OnGaveMeleeAttack(defender); - - if (Utility.RandomDouble() >= 0.1) - { - return; - } - - /* Grasping Claw - * Start cliloc: 1070836 - * Effect: Physical resistance -15% for 5 seconds - * End cliloc: 1070838 - * Effect: Type: "3" - From: "0x57D4F5B" (player) - To: "0x0" - ItemId: "0x37B9" - ItemIdName: "glow" - FromLocation: "(1149 808, 32)" - ToLocation: "(1149 808, 32)" - Speed: "10" - Duration: "5" - FixedDirection: "True" - Explode: "False" - */ - - if (m_Table.TryGetValue(defender, out var timer)) - { - timer.DoExpire(); - defender.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state. - } - else - { - defender.SendLocalizedMessage( - 1070836 - ); // The blow from the creature's claws has made you more susceptible to physical attacks. - } - - var effect = -(defender.PhysicalResistance * 15 / 100); - - var mod = new ResistanceMod(ResistanceType.Physical, effect); - - defender.FixedEffect(0x37B9, 10, 5); - defender.AddResistanceMod(mod); - - timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(5.0)); - timer.Start(); - m_Table[defender] = timer; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(2); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - if (version <= 1) - { - Timer.StartTimer(() => Fix(version)); - } - - if (version < 2) - { - for (var i = 0; i < Skills.Length; ++i) + if (Skills[i].Base > Skills[i].Cap) { - Skills[i].Cap = Math.Max(100.0, Skills[i].Cap * 0.9); - - if (Skills[i].Base > Skills[i].Cap) - { - Skills[i].Base = Skills[i].Cap; - } + Skills[i].Base = Skills[i].Cap; } } } + } - private void Fix(int version) + private void Fix(int version) + { + switch (version) { - switch (version) - { - case 1: + case 1: + { + if (InternalItem != null) { - if (InternalItem != null) - { - InternalItem.Hue = Hue; - } - - goto case 0; + InternalItem.Hue = Hue; } - case 0: - { - Hue = GetHue(); - break; - } - } - } - private class ExpireTimer : Timer - { - private readonly Mobile m_Mobile; - private readonly ResistanceMod m_Mod; - - public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay) - : base(delay) - { - m_Mobile = m; - m_Mod = mod; - } - - public void DoExpire() - { - m_Mobile.RemoveResistanceMod(m_Mod); - Stop(); - m_Table.Remove(m_Mobile); - } - - protected override void OnTick() - { - m_Mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned. - DoExpire(); - } + goto case 0; + } + case 0: + { + Hue = GetHue(); + break; + } } } } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs index e186bec7c..721a031ed 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs @@ -95,14 +95,15 @@ namespace Server.Mobiles } public override string CorpseName => "a nightmare corpse"; - - public override bool HasBreath => true; // fire breath enabled public override int Meat => 5; public override int Hides => 10; public override HideType HideType => HideType.Barbed; public override FoodType FavoriteFood => FoodType.Meat; public override bool CanAngerOnTame => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Rich); diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs index caff4953f..41c64e24f 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs @@ -50,11 +50,12 @@ namespace Server.Mobiles public override string CorpseName => "a lava lizard corpse"; public override string DefaultName => "a lava lizard"; - - public override bool HasBreath => true; // fire breath enabled public override int Hides => 12; public override HideType HideType => HideType.Spined; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Meager); diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs index 5e963937c..8a6610fbf 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs @@ -50,12 +50,13 @@ namespace Server.Mobiles public override string DefaultName => "a lava serpent"; public override bool DeathAdderCharmable => true; - - public override bool HasBreath => true; // fire breath enabled public override int Meat => 4; public override int Hides => 15; public override HideType HideType => HideType.Spined; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Average); diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs index 3b7cb2609..22e40f010 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs @@ -48,10 +48,11 @@ namespace Server.Mobiles public override string DefaultName => "a lava snake"; public override bool DeathAdderCharmable => true; - - public override bool HasBreath => true; // fire breath enabled public override int Meat => 1; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Poor); diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index c22c2a9e6..1fecd1fbc 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -292,7 +292,6 @@ namespace Server.Mobiles private long m_NextAura; - private long m_NextBreathTime; private long m_NextHealOwnerTime = Core.TickCount; private long m_NextHealTime = Core.TickCount; @@ -866,7 +865,6 @@ namespace Server.Mobiles public static bool Summoning { get; set; } - public virtual bool CanBreath => HasBreath && !Summoned; public virtual bool IsDispellable => Summoned && !IsAnimatedDead; // If they are following a waypoint, they'll continue to follow it even if players aren't around @@ -1033,53 +1031,9 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public int DirectDamage { get; set; } - // Must be overridden in subclass to enable - public virtual bool HasBreath => false; - - // Base damage given is: CurrentHitPoints * BreathDamageScalar - public virtual double BreathDamageScalar => Core.AOS ? 0.16 : 0.05; - - // Min/max seconds until next breath - public virtual double BreathMinDelay => 30.0; - public virtual double BreathMaxDelay => 45.0; - - // Creature stops moving for 1.0 seconds while breathing - public virtual double BreathStallTime => 1.0; - - // Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played - public virtual double BreathEffectDelay => 1.3; - - // Damage is given 1.0 seconds after effect is sent - public virtual double BreathDamageDelay => 1.0; - - public virtual int BreathRange => RangePerception; - - // Damage types - public virtual int BreathChaosDamage => 0; - public virtual int BreathPhysicalDamage => 0; - public virtual int BreathFireDamage => 100; - public virtual int BreathColdDamage => 0; - public virtual int BreathPoisonDamage => 0; - public virtual int BreathEnergyDamage => 0; - // Is immune to breath damages public virtual bool BreathImmune => false; - // Effect details and sound - public virtual int BreathEffectItemID => 0x36D4; - public virtual int BreathEffectSpeed => 5; - public virtual int BreathEffectDuration => 0; - public virtual bool BreathEffectExplodes => false; - public virtual bool BreathEffectFixedDir => false; - public virtual int BreathEffectHue => 0; - public virtual int BreathEffectRenderMode => 0; - - public virtual int BreathEffectSound => 0x227; - - // Anger sound/animations - public virtual int BreathAngerSound => GetAngerSound(); - public virtual int BreathAngerAnimation => 12; - public virtual bool CanFlee => !m_Paragon; public DateTime EndFleeTime { get; set; } @@ -1162,6 +1116,67 @@ namespace Server.Mobiles } } + public virtual MonsterAbility[] GetMonsterAbilities() => null; + + public virtual bool HasAbility(MonsterAbilityType type) + { + var abilities = GetMonsterAbilities(); + + if (abilities == null) + { + return false; + } + + for (var i = 0; i < abilities.Length; i++) + { + if (abilities[i].AbilityType == type) + { + return true; + } + } + + return false; + } + + public virtual bool HasAbility(MonsterAbility ability) + { + var abilities = GetMonsterAbilities(); + + if (abilities == null) + { + return false; + } + + for (var i = 0; i < abilities.Length; i++) + { + if (abilities[i] == ability) + { + return true; + } + } + + return false; + } + + public virtual void TriggerAbility(MonsterAbilityTrigger trigger, Mobile defender) + { + var abilities = GetMonsterAbilities(); + + if (abilities == null) + { + return; + } + + for (var i = 0; i < abilities.Length; i++) + { + var ability = abilities[i]; + if (ability.WillTrigger(trigger) && ability.CanTrigger(this)) + { + ability.Trigger(this, defender); + } + } + } + public virtual WeaponAbility GetWeaponAbility() => null; public virtual bool IsEnemy(Mobile m) @@ -1478,12 +1493,19 @@ namespace Server.Mobiles base.OnDamage(amount, from, willKill); } - public virtual void OnDamagedBySpell(Mobile from) + public virtual void OnDamagedBySpell(Mobile from, int damage) { if (CanBeDistracted && ControlOrder == OrderType.Follow) { CheckDistracted(from); } + + TriggerAbility(MonsterAbilityTrigger.TakeSpellDamage, from); + } + + public virtual void OnDamageSpell(Mobile defender, int damage) + { + TriggerAbility(MonsterAbilityTrigger.GiveSpellDamage, defender); } public virtual void OnHarmfulSpell(Mobile from) @@ -2199,13 +2221,15 @@ namespace Server.Mobiles } } - public virtual void OnGotMeleeAttack(Mobile attacker) + public virtual void OnGotMeleeAttack(Mobile attacker, int damage) { if (AutoDispel && attacker is BaseCreature creature && creature.IsDispellable && AutoDispelChance > Utility.RandomDouble()) { Dispel(creature); } + + TriggerAbility(MonsterAbilityTrigger.TakeMeleeDamage, attacker); } public virtual void Dispel(Mobile m) @@ -2222,7 +2246,7 @@ namespace Server.Mobiles m.Delete(); } - public virtual void OnGaveMeleeAttack(Mobile defender) + public virtual void OnGaveMeleeAttack(Mobile defender, int damage) { var p = m_Paragon ? PoisonImpl.IncreaseLevel(HitPoison) : HitPoison; @@ -2241,6 +2265,8 @@ namespace Server.Mobiles { Dispel(creature); } + + TriggerAbility(MonsterAbilityTrigger.GiveMeleeDamage, defender); } public override void OnAfterDelete() @@ -3488,24 +3514,8 @@ namespace Server.Mobiles m_NextRummageTime = tc + (int)TimeSpan.FromMinutes(delay).TotalMilliseconds; } - // tested: controlled dragons do breath fire, what about summoned skeletal dragons? - if (CanBreath && tc - m_NextBreathTime >= 0) - { - var target = Combatant; - - if (target?.Alive == true && !target.IsDeadBondedPet && CanBeHarmful(target) && target.Map == Map && - !IsDeadBondedPet && target.InRange(this, BreathRange) && InLOS(target) && !BardPacified) - { - if (Core.TickCount - m_NextBreathTime < 30000 && Utility.RandomBool()) - { - BreathStart(target); - } - - m_NextBreathTime = tc + (int)TimeSpan - .FromSeconds(BreathMinDelay + Utility.RandomDouble() * (BreathMaxDelay - BreathMinDelay)) - .TotalMilliseconds; - } - } + // Fire breath, etc. + TriggerAbility(MonsterAbilityTrigger.Action, Combatant); if ((CanHeal || CanHealOwner) && Alive && !IsHealing && !BardPacified) { @@ -3893,161 +3903,6 @@ namespace Server.Mobiles } } - public virtual void BreathStart(Mobile target) - { - BreathStallMovement(); - BreathPlayAngerSound(); - BreathPlayAngerAnimation(); - - Direction = GetDirectionTo(target); - - Timer.StartTimer(TimeSpan.FromSeconds(BreathEffectDelay), () => BreathEffect_Callback(target)); - } - - public virtual void BreathStallMovement() - { - if (AIObject != null) - { - AIObject.NextMove = Core.TickCount + (int)(BreathStallTime * 1000); - } - } - - public virtual void BreathPlayAngerSound() - { - PlaySound(BreathAngerSound); - } - - public virtual void BreathPlayAngerAnimation() - { - Animate(BreathAngerAnimation, 5, 1, true, false, 0); - } - - public virtual void BreathEffect_Callback(Mobile target) - { - if (!target.Alive || !CanBeHarmful(target)) - { - return; - } - - BreathPlayEffectSound(); - BreathPlayEffect(target); - - Timer.StartTimer(TimeSpan.FromSeconds(BreathDamageDelay), () => BreathDamage_Callback(target)); - } - - public virtual void BreathPlayEffectSound() - { - PlaySound(BreathEffectSound); - } - - public virtual void BreathPlayEffect(Mobile target) - { - Effects.SendMovingEffect( - this, - target, - BreathEffectItemID, - BreathEffectSpeed, - BreathEffectDuration, - BreathEffectFixedDir, - BreathEffectExplodes, - BreathEffectHue, - BreathEffectRenderMode - ); - } - - public virtual void BreathDamage_Callback(Mobile target) - { - if (target is BaseCreature creature && creature.BreathImmune) - { - return; - } - - if (CanBeHarmful(target)) - { - DoHarmful(target); - BreathDealDamage(target); - } - } - - public virtual void BreathDealDamage(Mobile target) - { - if (!Evasion.CheckSpellEvasion(target)) - { - var physDamage = BreathPhysicalDamage; - var fireDamage = BreathFireDamage; - var coldDamage = BreathColdDamage; - var poisDamage = BreathPoisonDamage; - var nrgyDamage = BreathEnergyDamage; - - if (BreathChaosDamage > 0) - { - switch (Utility.Random(5)) - { - case 0: - { - physDamage += BreathChaosDamage; - break; - } - case 1: - { - fireDamage += BreathChaosDamage; - break; - } - case 2: - { - coldDamage += BreathChaosDamage; - break; - } - case 3: - { - poisDamage += BreathChaosDamage; - break; - } - case 4: - { - nrgyDamage += BreathChaosDamage; - break; - } - } - } - - if (physDamage == 0 && fireDamage == 0 && coldDamage == 0 && poisDamage == 0 && nrgyDamage == 0) - { - target.Damage(BreathComputeDamage(), this); // Unresistable damage even in AOS - } - else - { - AOS.Damage( - target, - this, - BreathComputeDamage(), - physDamage, - fireDamage, - coldDamage, - poisDamage, - nrgyDamage - ); - } - } - } - - public virtual int BreathComputeDamage() - { - var damage = (int)(Hits * BreathDamageScalar); - - if (IsParagon) - { - damage = (int)(damage / Paragon.HitsBuff); - } - - if (damage > 200) - { - damage = 200; - } - - return damage; - } - public void SpillAcid(int amount) { SpillAcid(null, amount); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs index be0856d36..79ec5e21c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs @@ -73,6 +73,9 @@ namespace Server.Mobiles AddLoot(LootPack.MedScrolls, 2); } + private static MonsterAbility[] _abilities = { MonsterAbility.SummonLesserUndead }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void Serialize(IGenericWriter writer) { base.Serialize(writer); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs index d65b6bb5f..f37095325 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs @@ -106,9 +106,9 @@ namespace Server.Mobiles AddLoot(LootPack.Gems, 1); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (!m_Stunning && Utility.RandomDouble() < 0.3) { diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs index 8595d6001..de159d11d 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs @@ -45,11 +45,13 @@ namespace Server.Mobiles public override string CorpseName => "a charred corpse"; - public override bool HasBreath => true; // fire breath enabled public override int TreasureMapLevel => 1; public override int Meat => 1; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Rich); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs index 24b4bd053..8caddf440 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs @@ -65,7 +65,7 @@ namespace Server.Mobiles AddLoot(LootPack.Gems, 2); } - public override void OnDamagedBySpell(Mobile from) + public override void OnDamagedBySpell(Mobile from, int damage) { if (from?.Alive == true && Utility.RandomDouble() < 0.4) { @@ -73,9 +73,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (attacker?.Alive == true && attacker.Weapon is BaseRanged && Utility.RandomDouble() < 0.4) { diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs index 3d29c14f9..cd25ef79e 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs @@ -59,6 +59,9 @@ namespace Server.Mobiles public override Poison PoisonImmune => Poison.Lethal; public override int TreasureMapLevel => 4; + private static MonsterAbility[] _abilities = { MonsterAbility.SummonSkeletons }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs index 9952033a3..cdc3744dc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs @@ -116,9 +116,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs index 0b43262fe..639d316b5 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs @@ -83,9 +83,9 @@ namespace Server.Mobiles eable.Free(); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() <= 0.1) { @@ -93,9 +93,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() <= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs index f241746f5..0a72928ab 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs @@ -103,9 +103,9 @@ namespace Server.Mobiles public override int GetHurtSound() => 0x140; - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (!m_Stunning && Utility.RandomDouble() < 0.3) { diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs index 69deb37ae..f29754476 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs @@ -99,7 +99,7 @@ namespace Server.Mobiles } } - public override void OnDamagedBySpell(Mobile caster) + public override void OnDamagedBySpell(Mobile caster, int damage) { if (caster == this) { diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs index 9d8ff90ef..87ef12254 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs @@ -38,20 +38,30 @@ namespace Server.Mobiles switch (Utility.Random(5)) { case 0: - PackItem(new BoneArms()); - break; + { + PackItem(new BoneArms()); + break; + } case 1: - PackItem(new BoneChest()); - break; + { + PackItem(new BoneChest()); + break; + } case 2: - PackItem(new BoneGloves()); - break; + { + PackItem(new BoneGloves()); + break; + } case 3: - PackItem(new BoneLegs()); - break; + { + PackItem(new BoneLegs()); + break; + } case 4: - PackItem(new BoneHelm()); - break; + { + PackItem(new BoneHelm()); + break; + } } } diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs index f70c42bb2..2e34264ce 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs @@ -102,7 +102,7 @@ namespace Server.Mobiles } } - public override void OnDamagedBySpell(Mobile from) + public override void OnDamagedBySpell(Mobile from, int damage) { if (from?.Alive == true && Utility.RandomDouble() < 0.4) { @@ -123,9 +123,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (FieldActive) { diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs index 18aeed356..48e34d632 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs @@ -96,7 +96,7 @@ namespace Server.Mobiles } } - public override void OnDamagedBySpell(Mobile from) + public override void OnDamagedBySpell(Mobile from, int damage) { if (from?.Alive == true && Utility.RandomDouble() < 0.4) { @@ -117,9 +117,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (FieldActive) { diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs index fb3b38a7c..0a1a3020f 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs @@ -96,13 +96,15 @@ namespace Server.Mobiles public override string CorpseName => "a chaos dragoon corpse"; public override string DefaultName => "a chaos dragoon"; - public override bool HasBreath => true; public override bool AutoDispel => true; public override bool BardImmune => !Core.AOS; public override bool CanRummageCorpses => true; public override bool AlwaysMurderer => true; public override bool ShowFameTitle => false; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override int GetIdleSound() => 0x2CE; public override int GetDeathSound() => 0x2CC; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs index 5e6bf3b5e..66f9443d1 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs @@ -40,9 +40,7 @@ namespace Server.Mobiles Fame = 8000; Karma = -8000; - CraftResource res; - - res = Utility.Random(6) switch + var res = Utility.Random(6) switch { 0 => CraftResource.BlackScales, 1 => CraftResource.RedScales, @@ -111,14 +109,15 @@ namespace Server.Mobiles public override string CorpseName => "a chaos dragoon elite corpse"; public override string DefaultName => "a chaos dragoon elite"; - - public override bool HasBreath => true; public override bool AutoDispel => true; public override bool BardImmune => !Core.AOS; public override bool CanRummageCorpses => true; public override bool AlwaysMurderer => true; public override bool ShowFameTitle => false; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override int GetIdleSound() => 0x2CE; public override int GetDeathSound() => 0x2CC; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs index 22e9160b1..0bf5800af 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs @@ -71,9 +71,9 @@ namespace Server.Mobiles public override int GetDeathSound() => 0x28D; - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() > 0.2) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs index 92f402904..6ebd6be98 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs @@ -69,9 +69,9 @@ namespace Server.Mobiles AddLoot(LootPack.UltraRich, 3); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/RedDeath.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/RedDeath.cs index dc6f278a0..60afe619a 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/RedDeath.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/RedDeath.cs @@ -5,8 +5,7 @@ namespace Server.Mobiles public class RedDeath : SkeletalMount { [Constructible] - public RedDeath() - : base("Red Death") + public RedDeath() : base("Red Death") { IsParagon = true; @@ -52,8 +51,7 @@ namespace Server.Mobiles } } - public RedDeath(Serial serial) - : base(serial) + public RedDeath(Serial serial) : base(serial) { } @@ -61,9 +59,9 @@ namespace Server.Mobiles public override bool GivesMLMinorArtifact => true; public override bool AlwaysMurderer => true; - public override bool HasBreath => true; - public override int BreathChaosDamage => 100; - public override int BreathFireDamage => 0; + + private static MonsterAbility[] _abilities = { MonsterAbility.ChaosBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; public override void GenerateLoot() { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs index 98aaa1f2c..127285241 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs @@ -50,10 +50,10 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.15) c.DropItem( new DisintegratingThesisNotes() ); - + if (Utility.RandomDouble() < 0.05) c.DropItem( new AssassinChest() ); } @@ -66,9 +66,9 @@ namespace Server.Mobiles AddLoot(LootPack.UltraRich, 2); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.1) { @@ -76,9 +76,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs b/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs index a9c563b0b..13c1304de 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs @@ -38,19 +38,19 @@ namespace Server.Mobiles // TODO: Fame/Karma } - public Hydra(Serial serial) - : base(serial) + public Hydra(Serial serial) : base(serial) { } public override string CorpseName => "a hydra corpse"; public override string DefaultName => "a hydra"; - - public override bool HasBreath => true; public override int Hides => 40; public override int Meat => 19; public override int TreasureMapLevel => 5; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.UltraRich, 3); diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs index 671c1925f..573958c81 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs @@ -54,9 +54,7 @@ namespace Server.Mobiles public override string CorpseName => "a dragon corpse"; public override bool StatLossAfterTame => true; public override string DefaultName => "a greater dragon"; - public override bool ReacquireOnMovement => !Controlled; - public override bool HasBreath => true; // fire breath enabled public override bool AutoDispel => !Controlled; public override int TreasureMapLevel => 5; public override int Meat => 19; @@ -68,6 +66,9 @@ namespace Server.Mobiles public override bool CanAngerOnTame => true; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 4); diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs index fe27e5c56..8fb9b180b 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs @@ -46,12 +46,14 @@ namespace Server.Mobiles public override int TreasureMapLevel => 5; public override int Meat => 5; public override int Hides => 10; - public override bool CanBreath => true; public override bool CanAngerOnTame => true; public override bool StatLossAfterTame => true; public override FoodType FavoriteFood => FoodType.Meat; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.AosUltraRich, 3); diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs index 20f7859ef..44eb30657 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs @@ -72,9 +72,9 @@ namespace Server.Mobiles AddLoot(LootPack.Gems); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.1) { @@ -82,9 +82,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs index 4d8218697..989622938 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs @@ -172,9 +172,9 @@ namespace Server.Mobiles } } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.25) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs index 62995b8ff..81caffce0 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs @@ -163,9 +163,9 @@ namespace Server.Mobiles public override int GetDeathSound() => 0x59c; - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() <= 0.2) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Twaulo.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Twaulo.cs index 5108acfbc..a3588f794 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Twaulo.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Twaulo.cs @@ -109,18 +109,18 @@ namespace Server.Mobiles } } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); defender.Damage(Utility.Random(20, 10), this); defender.Stam -= Utility.Random(20, 10); defender.Mana -= Utility.Random(20, 10); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() <= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Guile.cs b/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Guile.cs index da785c6a8..01c6dd6e3 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Guile.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Guile.cs @@ -49,9 +49,9 @@ namespace Server.Mobiles public override bool GivesMLMinorArtifact => true; - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomBool()) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Swoop.cs b/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Swoop.cs index bb78f3c18..60b16bbb0 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Swoop.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Swoop.cs @@ -96,9 +96,9 @@ namespace Server.Mobiles } // TODO: Put this attack shared with Hiryu and Lesser Hiryu in one place - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs index 9c9942d45..60d8cfb31 100644 --- a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs +++ b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs @@ -44,12 +44,13 @@ namespace Server.Mobiles public override string CorpseName => "a hell hound corpse"; public override string DefaultName => "a hell hound"; - - public override bool HasBreath => true; // fire breath enabled public override int Meat => 1; public override FoodType FavoriteFood => FoodType.Meat; public override PackInstinct PackInstinct => PackInstinct.Canine; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Average); diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs index f09582850..a255e3910 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs @@ -97,18 +97,18 @@ namespace Server.Mobiles public override int GetDeathSound() => 0x2F7; - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); defender.Damage(Utility.Random(10, 10), this); defender.Stam -= Utility.Random(10, 10); defender.Mana -= Utility.Random(10, 10); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); attacker.Damage(Utility.Random(10, 10), this); attacker.Stam -= Utility.Random(10, 10); diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs index 7bd41f3f4..42f76389f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs @@ -169,9 +169,9 @@ namespace Server.Mobiles return base.GetHurtSound(); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (!m_Stunning && Utility.RandomDouble() < 0.3) { diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs index aec74b455..4ca4ff7f4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs @@ -115,16 +115,16 @@ namespace Server.Mobiles // TODO: dungeon chest, healthy gland } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); defender.ApplyPoison(this, IsParagon ? Poison.Lethal : Poison.Deadly); defender.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist); defender.PlaySound(0x1CB); } - public override void OnDamagedBySpell(Mobile caster) + public override void OnDamagedBySpell(Mobile caster, int damage) { if (Map != null && caster != this && Utility.RandomDouble() < 0.25) { @@ -137,10 +137,10 @@ namespace Server.Mobiles Say(1053034); // * The plague beast creates another beast from its flesh! * } - base.OnDamagedBySpell(caster); + base.OnDamagedBySpell(caster, damage); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { if (Map != null && attacker != this && Utility.RandomDouble() < 0.25) { @@ -153,7 +153,7 @@ namespace Server.Mobiles Say(1053034); // * The plague beast creates another beast from its flesh! * } - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); } public override int GetIdleSound() => 0x1BF; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs index b2b1cbc93..4cf5179f6 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs @@ -118,9 +118,9 @@ namespace Server.Mobiles eable.Free(); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Hits > HitsMax / 4) { diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs index d98ed9fb7..9a3085fdb 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs @@ -46,7 +46,6 @@ namespace Server.Mobiles public override string DefaultName => "an ancient wyrm"; public override bool ReacquireOnMovement => true; - public override bool HasBreath => true; // fire breath enabled public override bool AutoDispel => true; public override HideType HideType => HideType.Barbed; public override int Hides => 40; @@ -58,6 +57,9 @@ namespace Server.Mobiles public override int TreasureMapLevel => 5; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 3); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs index 6d4d2dc50..d3185222a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs @@ -57,12 +57,13 @@ namespace Server.Mobiles public override string CorpseName => "a deep sea serpents corpse"; public override string DefaultName => "a deep sea serpent"; - - public override bool HasBreath => true; public override int Meat => 1; public override int Scales => 8; public override ScaleType ScaleType => ScaleType.Blue; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Meager); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs index 9847941cc..54656f74a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs @@ -48,7 +48,6 @@ namespace Server.Mobiles public override string DefaultName => "a dragon"; public override bool ReacquireOnMovement => !Controlled; - public override bool HasBreath => true; // fire breath enabled public override bool AutoDispel => !Controlled; public override int TreasureMapLevel => 4; public override int Meat => 19; @@ -60,6 +59,9 @@ namespace Server.Mobiles public override bool CanAngerOnTame => true; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 2); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs index 272b8a85c..bf944cc15 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs @@ -49,14 +49,8 @@ namespace Server.Mobiles CantWalk = true; PackItem(new MessageInABottle()); - - var rope = new Rope(); - rope.ItemID = 0x14F8; - PackItem(rope); - - rope = new Rope(); - rope.ItemID = 0x14FA; - PackItem(rope); + PackItem(new Rope { ItemID = 0x14F8 }); + PackItem(new Rope { ItemID = 0x14FA }); } public Leviathan(Serial serial) : base(serial) @@ -69,15 +63,6 @@ namespace Server.Mobiles public override string DefaultName => "a leviathan"; - public override bool HasBreath => true; - public override int BreathPhysicalDamage => 70; // TODO: Verify damage type - public override int BreathColdDamage => 30; - public override int BreathFireDamage => 0; - public override int BreathEffectHue => 0x1ED; - public override double BreathDamageScalar => 0.05; - public override double BreathMinDelay => 5.0; - public override double BreathMaxDelay => 7.5; - public override int TreasureMapLevel => 5; public static Type[] Artifacts { get; } = @@ -109,6 +94,9 @@ namespace Server.Mobiles typeof(VioletCourage) }; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 5); @@ -176,5 +164,18 @@ namespace Server.Mobiles Fisher = null; } + + private static MonsterAbility _ability = new LevianthanBreath(); + + public class LevianthanBreath : FireBreath + { + public override int BreathPhysicalDamage => 70; + public override int BreathColdDamage => 30; + public override int BreathFireDamage => 0; + public override int BreathEffectHue => 0x1ED; + public override double BreathDamageScalar => 0.05; + public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0); + public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(7.5); + } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs index 8fe8eca12..9ee164381 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs @@ -60,8 +60,6 @@ namespace Server.Mobiles public override string CorpseName => "a sea serpents corpse"; public override string DefaultName => "a sea serpent"; - - public override bool HasBreath => true; public override int TreasureMapLevel => 2; public override int Hides => 10; @@ -69,6 +67,9 @@ namespace Server.Mobiles public override int Scales => 8; public override ScaleType ScaleType => ScaleType.Blue; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Meager); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs index 0da6c0d54..27c7c9d5c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs @@ -51,9 +51,7 @@ namespace Server.Mobiles public override string CorpseName => "a dragon corpse"; public override string DefaultName => "a serpentine dragon"; - public override bool ReacquireOnMovement => true; - public override bool HasBreath => true; // fire breath enabled public override double BonusPetDamageScalar => Core.SE ? 3.0 : 1.0; public override bool AutoDispel => true; @@ -64,6 +62,9 @@ namespace Server.Mobiles public override ScaleType ScaleType => Utility.RandomBool() ? ScaleType.Black : ScaleType.White; public override int TreasureMapLevel => 4; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 2); @@ -80,9 +81,9 @@ namespace Server.Mobiles public override int GetHurtSound() => 0x2C3; - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (!Core.SE && Utility.RandomDouble() < 0.2 && attacker is BaseCreature c && c.Controlled && c.ControlMaster != null) diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs index dae5c0e0b..10aac995b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs @@ -44,9 +44,7 @@ namespace Server.Mobiles public override string CorpseName => "a shadow wyrm corpse"; public override string DefaultName => "a shadow wyrm"; - public override bool ReacquireOnMovement => true; - public override bool HasBreath => true; // fire breath enabled public override bool AutoDispel => true; public override Poison PoisonImmune => Poison.Deadly; public override Poison HitPoison => Poison.Deadly; @@ -59,6 +57,9 @@ namespace Server.Mobiles public override HideType HideType => HideType.Barbed; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 3); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs index 646aafd59..774bf438c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Server.Mobiles { public class SkeletalDragon : BaseCreature @@ -47,10 +49,6 @@ namespace Server.Mobiles public override string DefaultName => "a skeletal dragon"; public override bool ReacquireOnMovement => true; - public override bool HasBreath => true; // fire breath enabled - public override int BreathFireDamage => 0; - public override int BreathColdDamage => 100; - public override int BreathEffectHue => 0x480; public override double BonusPetDamageScalar => Core.SE ? 3.0 : 1.0; // TODO: Undead summoning? @@ -62,6 +60,9 @@ namespace Server.Mobiles public override int Hides => 20; public override HideType HideType => HideType.Barbed; + private static MonsterAbility[] _abilities = { MonsterAbility.ColdBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.FilthyRich, 4); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs index ff14ed9c0..7b4348d38 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs @@ -49,7 +49,6 @@ namespace Server.Mobiles public override string DefaultName => "a drake"; public override bool ReacquireOnMovement => true; - public override bool HasBreath => true; // fire breath enabled public override int TreasureMapLevel => 2; public override int Meat => 10; public override int Hides => 20; @@ -59,6 +58,9 @@ namespace Server.Mobiles public override FoodType FavoriteFood => FoodType.Meat | FoodType.Fish; public override bool CanFly => true; + private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + public override void GenerateLoot() { AddLoot(LootPack.Rich); diff --git a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs index 0bc626613..4da10e301 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs @@ -89,9 +89,9 @@ namespace Server.Mobiles return base.OnBeforeDeath(); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs b/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs index 637af3571..9b6b6bf55 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs @@ -101,21 +101,21 @@ namespace Server.Mobiles AOS.Damage(to, this, Utility.RandomMinMax(50, 65), 100, 0, 0, 0, 0); } - public override void OnDamagedBySpell(Mobile attacker) + public override void OnDamagedBySpell(Mobile attacker, int damage) { - base.OnDamagedBySpell(attacker); + base.OnDamagedBySpell(attacker, damage); ThrowFan(attacker); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); ThrowFan(attacker); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (!IsFanned(defender) && Utility.RandomDouble() < 0.05) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs b/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs index f10e68ba1..ee5746f9f 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs @@ -85,9 +85,9 @@ namespace Server.Mobiles public override int GetDeathSound() => 0x508; - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomBool()) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs b/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs index d4810186c..15361db1f 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs @@ -59,9 +59,9 @@ namespace Server.Mobiles AddLoot(LootPack.Rich, 3); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs b/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs index 46d841731..c6f9976f6 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs @@ -73,9 +73,9 @@ namespace Server.Mobiles // TODO: Snowball - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs b/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs index 73019d70c..9aad525db 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs @@ -56,9 +56,9 @@ namespace Server.Mobiles AddLoot(LootPack.Gems, 2); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.1 || m_Table.Contains(defender)) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs b/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs index 8825b753d..0dfc427e6 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs @@ -95,9 +95,9 @@ namespace Server.Mobiles AddLoot(LootPack.MedScrolls, 1); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.05) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs b/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs index 6ff33c378..62efb2d0a 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs @@ -78,9 +78,9 @@ namespace Server.Mobiles AddLoot(LootPack.Rich); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() >= 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs b/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs index b475c769c..7fe1c0701 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs @@ -68,16 +68,16 @@ namespace Server.Mobiles AddLoot(LootPack.Gems, 6); } - public override void OnDamagedBySpell(Mobile attacker) + public override void OnDamagedBySpell(Mobile attacker, int damage) { - base.OnDamagedBySpell(attacker); + base.OnDamagedBySpell(attacker, damage); DoCounter(attacker); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); DoCounter(attacker); } diff --git a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs index 4a6a2e4c6..f6e6bd5c3 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs @@ -89,9 +89,9 @@ namespace Server.Mobiles // TODO: Axe Throw - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs index 00dd628c6..dd9f7a86a 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs @@ -97,9 +97,9 @@ namespace Server.Mobiles // TODO: Body Transformation - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs index 22973b4aa..9a89f1ae0 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs @@ -95,9 +95,9 @@ namespace Server.Mobiles // TODO: Throwing Dagger - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() < 0.1) { diff --git a/Projects/UOContent/Mobiles/Special/Barracoon.cs b/Projects/UOContent/Mobiles/Special/Barracoon.cs index 53e88897c..4b10286aa 100644 --- a/Projects/UOContent/Mobiles/Special/Barracoon.cs +++ b/Projects/UOContent/Mobiles/Special/Barracoon.cs @@ -201,16 +201,16 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); DoSpecialAbility(attacker); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); DoSpecialAbility(defender); } diff --git a/Projects/UOContent/Mobiles/Special/LordOaks.cs b/Projects/UOContent/Mobiles/Special/LordOaks.cs index 4c914f959..031b73fef 100644 --- a/Projects/UOContent/Mobiles/Special/LordOaks.cs +++ b/Projects/UOContent/Mobiles/Special/LordOaks.cs @@ -158,18 +158,18 @@ namespace Server.Mobiles } } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); defender.Damage(Utility.Random(20, 10), this); defender.Stam -= Utility.Random(20, 10); defender.Mana -= Utility.Random(20, 10); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); CheckQueen(); diff --git a/Projects/UOContent/Mobiles/Special/Mephitis.cs b/Projects/UOContent/Mobiles/Special/Mephitis.cs index f6af38c11..84575b1ab 100644 --- a/Projects/UOContent/Mobiles/Special/Mephitis.cs +++ b/Projects/UOContent/Mobiles/Special/Mephitis.cs @@ -68,9 +68,9 @@ namespace Server.Mobiles AddLoot(LootPack.UltraRich, 4); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); // TODO: Web ability } diff --git a/Projects/UOContent/Mobiles/Special/Neira.cs b/Projects/UOContent/Mobiles/Special/Neira.cs index 5ae41f26f..0707588e9 100644 --- a/Projects/UOContent/Mobiles/Special/Neira.cs +++ b/Projects/UOContent/Mobiles/Special/Neira.cs @@ -140,9 +140,9 @@ namespace Server.Mobiles } } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() <= 0.1) // 10% chance to drop or throw an unholy bone { @@ -152,9 +152,9 @@ namespace Server.Mobiles CheckSpeedBoost(); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() <= 0.1) // 10% chance to drop or throw an unholy bone { diff --git a/Projects/UOContent/Mobiles/Special/Rikktor.cs b/Projects/UOContent/Mobiles/Special/Rikktor.cs index 47a8327fe..d242fba93 100644 --- a/Projects/UOContent/Mobiles/Special/Rikktor.cs +++ b/Projects/UOContent/Mobiles/Special/Rikktor.cs @@ -79,9 +79,9 @@ namespace Server.Mobiles AddLoot(LootPack.UltraRich, 4); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() <= 0.2) { diff --git a/Projects/UOContent/Mobiles/Special/Semidar.cs b/Projects/UOContent/Mobiles/Special/Semidar.cs index e6092bd43..76f445f52 100644 --- a/Projects/UOContent/Mobiles/Special/Semidar.cs +++ b/Projects/UOContent/Mobiles/Special/Semidar.cs @@ -122,9 +122,9 @@ namespace Server.Mobiles eable.Free(); } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); if (Utility.RandomDouble() <= 0.25) { @@ -132,9 +132,9 @@ namespace Server.Mobiles } } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() <= 0.25) { diff --git a/Projects/UOContent/Mobiles/Special/Serado.cs b/Projects/UOContent/Mobiles/Special/Serado.cs index 39d204493..b399a3d5d 100644 --- a/Projects/UOContent/Mobiles/Special/Serado.cs +++ b/Projects/UOContent/Mobiles/Special/Serado.cs @@ -89,17 +89,17 @@ namespace Server.Mobiles // TODO: Hit Lightning Area - public override void OnDamagedBySpell(Mobile attacker) + public override void OnDamagedBySpell(Mobile attacker, int damage) { - base.OnDamagedBySpell(attacker); + base.OnDamagedBySpell(attacker, damage); ScaleResistances(); DoCounter(attacker); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); ScaleResistances(); DoCounter(attacker); diff --git a/Projects/UOContent/Mobiles/Special/Silvani.cs b/Projects/UOContent/Mobiles/Special/Silvani.cs index c103169e8..8aae6dbcf 100644 --- a/Projects/UOContent/Mobiles/Special/Silvani.cs +++ b/Projects/UOContent/Mobiles/Special/Silvani.cs @@ -84,18 +84,18 @@ namespace Server.Mobiles } } - public override void OnGaveMeleeAttack(Mobile defender) + public override void OnGaveMeleeAttack(Mobile defender, int damage) { - base.OnGaveMeleeAttack(defender); + base.OnGaveMeleeAttack(defender, damage); defender.Damage(Utility.Random(20, 10), this); defender.Stam -= Utility.Random(20, 10); defender.Mana -= Utility.Random(20, 10); } - public override void OnGotMeleeAttack(Mobile attacker) + public override void OnGotMeleeAttack(Mobile attacker, int damage) { - base.OnGotMeleeAttack(attacker); + base.OnGotMeleeAttack(attacker, damage); if (Utility.RandomDouble() <= 0.1) { diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index 2452dff8d..2d701e78d 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -935,26 +935,29 @@ namespace Server.Spells public static void Damage(Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage) { - var iDamage = (int)damage; + var damageGiven = (int)damage; if (delay == TimeSpan.Zero) { - (from as BaseCreature)?.AlterSpellDamageTo(target, ref iDamage); - + var bcFrom = from as BaseCreature; var bcTarget = target as BaseCreature; - bcTarget?.AlterSpellDamageFrom(from, ref iDamage); - target.Damage(iDamage, from); + bcFrom?.AlterSpellDamageTo(target, ref damageGiven); + bcTarget?.AlterSpellDamageFrom(from, ref damageGiven); + + target.Damage(damageGiven, from); + + bcFrom?.OnDamageSpell(target, damageGiven); if (from != null) { bcTarget?.OnHarmfulSpell(from); - bcTarget?.OnDamagedBySpell(from); + bcTarget?.OnDamagedBySpell(from, damageGiven); } } else { - new SpellDamageTimer(spell, target, from, iDamage, delay).Start(); + new SpellDamageTimer(spell, target, from, damageGiven, delay).Start(); } } @@ -1004,26 +1007,30 @@ namespace Server.Spells if (delay == TimeSpan.Zero) { - (from as BaseCreature)?.AlterSpellDamageTo(target, ref dmg); + var bcFrom = from as BaseCreature; + var bcTarget = target as BaseCreature; + bcFrom?.AlterSpellDamageTo(target, ref dmg); - (target as BaseCreature)?.AlterSpellDamageFrom(from, ref dmg); + bcTarget?.AlterSpellDamageFrom(from, ref dmg); WeightOverloading.DFA = dfa; var damageGiven = AOS.Damage(target, from, dmg, phys, fire, cold, pois, nrgy, chaos); WeightOverloading.DFA = DFAlgorithm.Standard; + + bcFrom?.OnDamageSpell(target, damageGiven); + + if (bcTarget != null && from != null && delay == TimeSpan.Zero) + { + bcTarget.OnHarmfulSpell(from); + bcTarget.OnDamagedBySpell(from, damageGiven); + } } else { new SpellDamageTimerAOS(spell, delay, target, from, dmg, phys, fire, cold, pois, nrgy, chaos, dfa).Start(); } - - if (target is BaseCreature c && from != null && delay == TimeSpan.Zero) - { - c.OnHarmfulSpell(from); - c.OnDamagedBySpell(from); - } } public static void DoLeech(int damageGiven, Mobile from, Mobile target) @@ -1127,16 +1134,17 @@ namespace Server.Spells protected override void OnTick() { + var bcFrom = m_From as BaseCreature; var bcTarg = m_Target as BaseCreature; - if (m_From is BaseCreature bcFrom && m_Target != null) + if (m_Target != null) { - bcFrom.AlterSpellDamageTo(m_Target, ref m_Damage); + bcFrom?.AlterSpellDamageTo(m_Target, ref m_Damage); } - if (bcTarg != null && m_From != null) + if (m_From != null) { - bcTarg.AlterSpellDamageFrom(m_From, ref m_Damage); + bcTarg?.AlterSpellDamageFrom(m_From, ref m_Damage); } WeightOverloading.DFA = m_DFA; @@ -1145,10 +1153,12 @@ namespace Server.Spells WeightOverloading.DFA = DFAlgorithm.Standard; - if (bcTarg != null && m_From != null) + bcFrom?.OnDamageSpell(m_Target, damageGiven); + + if (m_From != null) { - bcTarg.OnHarmfulSpell(m_From); - bcTarg.OnDamagedBySpell(m_From); + bcTarg?.OnHarmfulSpell(m_From); + bcTarg?.OnDamagedBySpell(m_From, damageGiven); } m_Spell?.RemoveDelayedDamageContext(m_Target);