diff --git a/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs b/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs index 06400c0e3..d49c795d0 100644 --- a/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs +++ b/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs @@ -1,40 +1,14 @@ -using System; -using Server.Collections; +using Server.Collections; namespace Server.Mobiles; public abstract class AreaEffectMonsterAbility : MonsterAbility { - public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.OnCombatAction; - public override MonsterAbilityType AbilityType => MonsterAbilityType.MassPoison; - - public override double ChanceToTrigger => 0.5; - public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0); - public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(30.0); - - public virtual int CombatantRange => 3; - public virtual int AreaRange => 3; - public override bool CanTrigger(BaseCreature source) - { - if (!base.CanTrigger(source)) - { - return false; - } - - var combatant = source.Combatant; - - // Only do the mass ability as a side-effect of combating a specific target - return combatant?.Deleted == false && combatant.Map == source.Map && source.InRange(combatant.Location, CombatantRange) && - source.CanBeHarmful(combatant) && source.InLOS(combatant); - } - public override void Trigger(BaseCreature source, Mobile target) { - // target is null for OnCombatAction triggers - - var eable = source.GetMobilesInRange(2); + var eable = source.GetMobilesInRange(AreaRange); using var queue = PooledRefQueue.Create(); foreach (var m in eable) { @@ -54,7 +28,9 @@ public abstract class AreaEffectMonsterAbility : MonsterAbility } protected virtual bool CanEffectTarget(BaseCreature source, Mobile defender) => - source != defender && defender.Alive && source.IsEnemy(defender) && source.CanBeHarmful(defender); + source != defender && defender.Alive && source.CanBeHarmful(defender) + && (defender.Player || defender is BaseCreature bc && (bc.Team == source.Team || bc.Controlled || bc.Summoned)) + && (!Core.AOS || source.InLOS(defender)); protected abstract void DoEffectTarget(BaseCreature source, Mobile defender); } diff --git a/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs b/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs index 6ba629a2f..0449bc469 100644 --- a/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs +++ b/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs @@ -5,6 +5,7 @@ namespace Server.Mobiles; public class DeathExplosion : AreaEffectMonsterAbility { public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.OnDeath; + public override MonsterAbilityType AbilityType => MonsterAbilityType.DeathExplosion; public override double ChanceToTrigger => 1.0; public override TimeSpan MinTriggerCooldown => TimeSpan.Zero; diff --git a/Projects/UOContent/Mobiles/Abilities/DrainLifeAreaAttack.cs b/Projects/UOContent/Mobiles/Abilities/DrainLifeAreaAttack.cs new file mode 100644 index 000000000..618253c8f --- /dev/null +++ b/Projects/UOContent/Mobiles/Abilities/DrainLifeAreaAttack.cs @@ -0,0 +1,33 @@ +namespace Server.Mobiles; + +public class DrainLifeAreaAttack : AreaEffectMonsterAbility +{ + public override MonsterAbilityTrigger AbilityTrigger => + MonsterAbilityTrigger.GiveMeleeDamage | MonsterAbilityTrigger.TakeMeleeDamage; + + public override MonsterAbilityType AbilityType => MonsterAbilityType.MassDrainLife; + + public override double ChanceToTrigger => 0.1; + public override int AreaRange => 2; + + public virtual int MinDamage => 10; + public virtual int MaxDamage => 40; + + protected virtual void DrainLife(BaseCreature source, Mobile defender) + { + var toDrain = Utility.RandomMinMax(MinDamage, MaxDamage); + + source.Hits += toDrain; + source.DoHarmful(defender); + defender.Damage(toDrain, source); + } + + protected override void DoEffectTarget(BaseCreature source, Mobile defender) + { + defender.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist); + defender.PlaySound(0x231); + + defender.SendMessage("You feel the life drain out of you!"); + DrainLife(source, defender); + } +} diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs index e828473b1..621cd796c 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.Abilities.cs @@ -26,4 +26,6 @@ public abstract partial class MonsterAbility public static ThrowHatchetCounter ThrowHatchetCounter => new(); public static DestroyEquipment DestroyEquipment => new(); + + public static DrainLifeAreaAttack DrainLifeAreaAttack => new(); } diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs index d26848b71..e1dbe8250 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs @@ -10,5 +10,7 @@ public enum MonsterAbilityType Poison, MassPoison, ThrowWeapon, - DestroyEquipment + DestroyEquipment, + DeathExplosion, + MassDrainLife } diff --git a/Projects/UOContent/Mobiles/Abilities/PoisonGasAreaAttack.cs b/Projects/UOContent/Mobiles/Abilities/PoisonGasAreaAttack.cs index fac4b792f..a6e2acedc 100644 --- a/Projects/UOContent/Mobiles/Abilities/PoisonGasAreaAttack.cs +++ b/Projects/UOContent/Mobiles/Abilities/PoisonGasAreaAttack.cs @@ -1,9 +1,32 @@ -namespace Server.Mobiles; +using System; + +namespace Server.Mobiles; public class PoisonGasAreaAttack : AreaEffectMonsterAbility { + public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.OnCombatAction; public override MonsterAbilityType AbilityType => MonsterAbilityType.MassPoison; + public override double ChanceToTrigger => 0.5; + public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0); + public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(30.0); + + public virtual int CombatantRange => 3; + + public override bool CanTrigger(BaseCreature source) + { + if (!base.CanTrigger(source)) + { + return false; + } + + var combatant = source.Combatant; + + // Only do the mass ability as a side-effect of combating a specific target + return combatant?.Deleted == false && combatant.Map == source.Map && source.InRange(combatant.Location, CombatantRange) && + source.CanBeHarmful(combatant) && source.InLOS(combatant); + } + public override void Trigger(BaseCreature source, Mobile target) { source.FixedParticles(0x376A, 9, 32, 0x2539, EffectLayer.LeftHand); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs index 639d316b5..2cf3ff74c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs @@ -54,54 +54,8 @@ namespace Server.Mobiles AddLoot(LootPack.MedScrolls, 2); } - public void DrainLife() - { - var eable = GetMobilesInRange(2); - - foreach (var m in eable) - { - if (m == this || !CanBeHarmful(m) || - !(m.Player || m is BaseCreature creature && - (creature.Controlled || creature.Summoned || creature.Team != Team))) - { - continue; - } - - DoHarmful(m); - - m.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist); - m.PlaySound(0x231); - - // m.SendMessage( "You feel the life drain out of you!" ); - - var toDrain = Utility.RandomMinMax(10, 40); - - Hits += toDrain; - m.Damage(toDrain, this); - } - - eable.Free(); - } - - public override void OnGaveMeleeAttack(Mobile defender, int damage) - { - base.OnGaveMeleeAttack(defender, damage); - - if (Utility.RandomDouble() <= 0.1) - { - DrainLife(); - } - } - - public override void OnGotMeleeAttack(Mobile attacker, int damage) - { - base.OnGotMeleeAttack(attacker, damage); - - if (Utility.RandomDouble() <= 0.1) - { - DrainLife(); - } - } + private static MonsterAbility[] _abilities = { MonsterAbility.DrainLifeAreaAttack }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; public override void Serialize(IGenericWriter writer) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs index 127285241..96c2780aa 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/SirPatrick.cs @@ -1,141 +1,96 @@ -using System.Collections.Generic; +namespace Server.Mobiles; -namespace Server.Mobiles +public class SirPatrick : SkeletalKnight { - public class SirPatrick : SkeletalKnight + [Constructible] + public SirPatrick() { - [Constructible] - public SirPatrick() + IsParagon = true; + + Hue = 0x47E; + + SetStr(208, 319); + SetDex(98, 132); + SetInt(45, 91); + + SetHits(616, 884); + + SetDamage(15, 25); + + SetDamageType(ResistanceType.Physical, 40); + SetDamageType(ResistanceType.Cold, 60); + + SetResistance(ResistanceType.Physical, 55, 62); + SetResistance(ResistanceType.Fire, 40, 48); + SetResistance(ResistanceType.Cold, 71, 80); + SetResistance(ResistanceType.Poison, 40, 50); + SetResistance(ResistanceType.Energy, 50, 60); + + SetSkill(SkillName.Wrestling, 126.3, 136.5); + SetSkill(SkillName.Tactics, 128.5, 143.8); + SetSkill(SkillName.MagicResist, 102.8, 117.9); + SetSkill(SkillName.Anatomy, 127.5, 137.2); + + Fame = 18000; + Karma = -18000; + } + + public SirPatrick(Serial serial) : base(serial) + { + } + + public override string CorpseName => "a Sir Patrick corpse"; + public override string DefaultName => "Sir Patrick"; + + /* + // TODO: uncomment once added + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if (Utility.RandomDouble() < 0.15) + c.DropItem( new DisintegratingThesisNotes() ); + + if (Utility.RandomDouble() < 0.05) + c.DropItem( new AssassinChest() ); + } + */ + + public override bool GivesMLMinorArtifact => true; + + private static MonsterAbility[] _abilities = { new SirPatrickDrainLife() }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 2); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + + private class SirPatrickDrainLife : DrainLifeAreaAttack + { + public override int MinDamage => 14; + public override int MaxDamage => 30; + + protected override void DoEffectTarget(BaseCreature source, Mobile defender) { - IsParagon = true; + defender.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist); + defender.PlaySound(0x1EA); - Hue = 0x47E; - - SetStr(208, 319); - SetDex(98, 132); - SetInt(45, 91); - - SetHits(616, 884); - - SetDamage(15, 25); - - SetDamageType(ResistanceType.Physical, 40); - SetDamageType(ResistanceType.Cold, 60); - - SetResistance(ResistanceType.Physical, 55, 62); - SetResistance(ResistanceType.Fire, 40, 48); - SetResistance(ResistanceType.Cold, 71, 80); - SetResistance(ResistanceType.Poison, 40, 50); - SetResistance(ResistanceType.Energy, 50, 60); - - SetSkill(SkillName.Wrestling, 126.3, 136.5); - SetSkill(SkillName.Tactics, 128.5, 143.8); - SetSkill(SkillName.MagicResist, 102.8, 117.9); - SetSkill(SkillName.Anatomy, 127.5, 137.2); - - Fame = 18000; - Karma = -18000; - } - - public SirPatrick(Serial serial) - : base(serial) - { - } - - public override string CorpseName => "a Sir Patrick corpse"; - public override string DefaultName => "Sir Patrick"; - - /* - // TODO: uncomment once added - public override void OnDeath( Container c ) - { - base.OnDeath( c ); - - if (Utility.RandomDouble() < 0.15) - c.DropItem( new DisintegratingThesisNotes() ); - - if (Utility.RandomDouble() < 0.05) - c.DropItem( new AssassinChest() ); - } - */ - - public override bool GivesMLMinorArtifact => true; - - public override void GenerateLoot() - { - AddLoot(LootPack.UltraRich, 2); - } - - public override void OnGaveMeleeAttack(Mobile defender, int damage) - { - base.OnGaveMeleeAttack(defender, damage); - - if (Utility.RandomDouble() < 0.1) - { - DrainLife(); - } - } - - public override void OnGotMeleeAttack(Mobile attacker, int damage) - { - base.OnGotMeleeAttack(attacker, damage); - - if (Utility.RandomDouble() < 0.1) - { - DrainLife(); - } - } - - public virtual void DrainLife() - { - var list = new List(); - - foreach (var m in GetMobilesInRange(2)) - { - if (m == this || !CanBeHarmful(m, false) || Core.AOS && !InLOS(m)) - { - continue; - } - - if (m is BaseCreature bc) - { - if (bc.Controlled || bc.Summoned || bc.Team != Team) - { - list.Add(bc); - } - } - else if (m.Player) - { - list.Add(m); - } - } - - foreach (var m in list) - { - DoHarmful(m); - - m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist); - m.PlaySound(0x1EA); - - var drain = Utility.RandomMinMax(14, 30); - - Hits += drain; - m.Damage(drain, this); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); + DrainLife(source, defender); } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs index 14cb23a25..7f601fa83 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs @@ -57,6 +57,12 @@ public class CopperElemental : BaseCreature AddLoot(LootPack.Gems, 2); } + public override void AlterMeleeDamageFrom(Mobile from, ref int damage) + { + base.AlterMeleeDamageFrom(from, ref damage); + damage /= 2; // 50% melee damage + } + public override void CheckReflect(Mobile caster, ref bool reflect) { reflect = true; // Every spell is reflected back to the caster diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs index 2a4895805..396f0d799 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs @@ -65,12 +65,13 @@ public class ValoriteElemental : BaseCreature public override void AlterMeleeDamageFrom(Mobile from, ref int damage) { - if (from is BaseCreature bc) + if (from is BaseCreature bc && (bc.Controlled || bc.BardTarget == this)) { - if (bc.Controlled || bc.BardTarget == this) - { - damage = 0; // Immune to pets and provoked creatures - } + damage = 0; // Immune to pets and provoked creatures + } + else + { + damage /= 2; // 50% melee damage } } diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs index 3b6d9c991..0016d5fcb 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs @@ -1,181 +1,178 @@ using System; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class Leviathan : BaseCreature { - public class Leviathan : BaseCreature + [Constructible] + public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage) { - [Constructible] - public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage) + Fisher = fisher; + + // May not be OSI accurate; mostly copied from krakens + Body = 77; + BaseSoundID = 353; + + Hue = 0x481; + + SetStr(1000); + SetDex(501, 520); + SetInt(501, 515); + + SetHits(1500); + + SetDamage(25, 33); + + SetDamageType(ResistanceType.Physical, 70); + SetDamageType(ResistanceType.Cold, 30); + + SetResistance(ResistanceType.Physical, 55, 65); + SetResistance(ResistanceType.Fire, 45, 55); + SetResistance(ResistanceType.Cold, 45, 55); + SetResistance(ResistanceType.Poison, 35, 45); + SetResistance(ResistanceType.Energy, 25, 35); + + SetSkill(SkillName.EvalInt, 97.6, 107.5); + SetSkill(SkillName.Magery, 97.6, 107.5); + SetSkill(SkillName.MagicResist, 97.6, 107.5); + SetSkill(SkillName.Meditation, 97.6, 107.5); + SetSkill(SkillName.Tactics, 97.6, 107.5); + SetSkill(SkillName.Wrestling, 97.6, 107.5); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 50; + + CanSwim = true; + CantWalk = true; + + PackItem(new MessageInABottle()); + PackItem(new Rope { ItemID = 0x14F8 }); + PackItem(new Rope { ItemID = 0x14FA }); + } + + public Leviathan(Serial serial) : base(serial) + { + } + + public override string CorpseName => "a leviathan corpse"; + + public Mobile Fisher { get; set; } + + public override string DefaultName => "a leviathan"; + + public override int TreasureMapLevel => 5; + + public static Type[] Artifacts { get; } = + { + // Decorations + typeof(CandelabraOfSouls), + typeof(GhostShipAnchor), + typeof(GoldBricks), + typeof(PhillipsWoodenSteed), + typeof(SeahorseStatuette), + typeof(ShipModelOfTheHMSCape), + typeof(AdmiralsHeartyRum), + + // Equipment + typeof(AlchemistsBauble), + typeof(ArcticDeathDealer), + typeof(BlazeOfDeath), + typeof(BurglarsBandana), + typeof(CaptainQuacklebushsCutlass), + typeof(CavortingClub), + typeof(DreadPirateHat), + typeof(EnchantedTitanLegBone), + typeof(GwennosHarp), + typeof(IolosLute), + typeof(LunaLance), + typeof(NightsKiss), + typeof(NoxRangersHeavyCrossbow), + typeof(PolarBearMask), + typeof(VioletCourage) + }; + + private static MonsterAbility[] _abilities = { new LeviathanBreath() }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + + public override void GenerateLoot() + { + AddLoot(LootPack.FilthyRich, 5); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + + public static void GiveArtifactTo(Mobile m) + { + var item = Loot.Construct(Artifacts); + + if (item == null) { - Fisher = fisher; - - // May not be OSI accurate; mostly copied from krakens - Body = 77; - BaseSoundID = 353; - - Hue = 0x481; - - SetStr(1000); - SetDex(501, 520); - SetInt(501, 515); - - SetHits(1500); - - SetDamage(25, 33); - - SetDamageType(ResistanceType.Physical, 70); - SetDamageType(ResistanceType.Cold, 30); - - SetResistance(ResistanceType.Physical, 55, 65); - SetResistance(ResistanceType.Fire, 45, 55); - SetResistance(ResistanceType.Cold, 45, 55); - SetResistance(ResistanceType.Poison, 35, 45); - SetResistance(ResistanceType.Energy, 25, 35); - - SetSkill(SkillName.EvalInt, 97.6, 107.5); - SetSkill(SkillName.Magery, 97.6, 107.5); - SetSkill(SkillName.MagicResist, 97.6, 107.5); - SetSkill(SkillName.Meditation, 97.6, 107.5); - SetSkill(SkillName.Tactics, 97.6, 107.5); - SetSkill(SkillName.Wrestling, 97.6, 107.5); - - Fame = 24000; - Karma = -24000; - - VirtualArmor = 50; - - CanSwim = true; - CantWalk = true; - - PackItem(new MessageInABottle()); - PackItem(new Rope { ItemID = 0x14F8 }); - PackItem(new Rope { ItemID = 0x14FA }); + return; } - public Leviathan(Serial serial) : base(serial) + // TODO: Confirm messages + if (m.AddToBackpack(item)) { + m.SendMessage("As a reward for slaying the mighty leviathan, an artifact has been placed in your backpack."); } - - public override string CorpseName => "a leviathan corpse"; - - public Mobile Fisher { get; set; } - - public override string DefaultName => "a leviathan"; - - public override int TreasureMapLevel => 5; - - public static Type[] Artifacts { get; } = + else { - // Decorations - typeof(CandelabraOfSouls), - typeof(GhostShipAnchor), - typeof(GoldBricks), - typeof(PhillipsWoodenSteed), - typeof(SeahorseStatuette), - typeof(ShipModelOfTheHMSCape), - typeof(AdmiralsHeartyRum), - - // Equipment - typeof(AlchemistsBauble), - typeof(ArcticDeathDealer), - typeof(BlazeOfDeath), - typeof(BurglarsBandana), - typeof(CaptainQuacklebushsCutlass), - typeof(CavortingClub), - typeof(DreadPirateHat), - typeof(EnchantedTitanLegBone), - typeof(GwennosHarp), - typeof(IolosLute), - typeof(LunaLance), - typeof(NightsKiss), - typeof(NoxRangersHeavyCrossbow), - typeof(PolarBearMask), - typeof(VioletCourage) - }; - - private static MonsterAbility[] _abilities = { MonsterAbility.FireBreath }; - public override MonsterAbility[] GetMonsterAbilities() => _abilities; - - public override void GenerateLoot() - { - AddLoot(LootPack.FilthyRich, 5); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public static void GiveArtifactTo(Mobile m) - { - var item = Loot.Construct(Artifacts); - - if (item == null) - { - return; - } - - // TODO: Confirm messages - if (m.AddToBackpack(item)) - { - m.SendMessage("As a reward for slaying the mighty leviathan, an artifact has been placed in your backpack."); - } - else - { - m.SendMessage( - "As your backpack is full, your reward for destroying the legendary leviathan has been placed at your feet." - ); - } - } - - public override void OnKilledBy(Mobile mob) - { - base.OnKilledBy(mob); - - if (Paragon.CheckArtifactChance(mob, this)) - { - GiveArtifactTo(mob); - - if (mob == Fisher) - { - Fisher = null; - } - } - } - - public override void OnDeath(Container c) - { - base.OnDeath(c); - - if (Fisher != null && Utility.Random(100) < 25) - { - GiveArtifactTo(Fisher); - } - - Fisher = null; - } - - private static MonsterAbility _ability = new LevianthanBreath(); - - public class LevianthanBreath : FireBreath - { - public override int PhysicalDamage => 70; - public override int ColdDamage => 30; - public override int FireDamage => 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); + m.SendMessage( + "As your backpack is full, your reward for destroying the legendary leviathan has been placed at your feet." + ); } } + + public override void OnKilledBy(Mobile mob) + { + base.OnKilledBy(mob); + + if (Paragon.CheckArtifactChance(mob, this)) + { + GiveArtifactTo(mob); + + if (mob == Fisher) + { + Fisher = null; + } + } + } + + public override void OnDeath(Container c) + { + base.OnDeath(c); + + if (Fisher != null && Utility.Random(100) < 25) + { + GiveArtifactTo(Fisher); + } + + Fisher = null; + } + + private class LeviathanBreath : FireBreath + { + public override int PhysicalDamage => 70; + public override int ColdDamage => 30; + public override int FireDamage => 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/Special/Semidar.cs b/Projects/UOContent/Mobiles/Special/Semidar.cs index 76f445f52..dae2cab39 100644 --- a/Projects/UOContent/Mobiles/Special/Semidar.cs +++ b/Projects/UOContent/Mobiles/Special/Semidar.cs @@ -2,156 +2,106 @@ using System; using Server.Engines.CannedEvil; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class Semidar : BaseChampion { - public class Semidar : BaseChampion + [Constructible] + public Semidar() : base(AIType.AI_Mage) { - [Constructible] - public Semidar() : base(AIType.AI_Mage) + Body = 174; + BaseSoundID = 0x4B0; + + SetStr(502, 600); + SetDex(102, 200); + SetInt(601, 750); + + SetHits(1500); + SetStam(103, 250); + + SetDamage(29, 35); + + SetDamageType(ResistanceType.Physical, 75); + SetDamageType(ResistanceType.Fire, 25); + + SetResistance(ResistanceType.Physical, 20, 30); + SetResistance(ResistanceType.Fire, 50, 60); + SetResistance(ResistanceType.Cold, 20, 30); + SetResistance(ResistanceType.Poison, 20, 30); + SetResistance(ResistanceType.Energy, 10, 20); + + SetSkill(SkillName.EvalInt, 95.1, 100.0); + SetSkill(SkillName.Magery, 90.1, 105.0); + SetSkill(SkillName.Meditation, 95.1, 100.0); + SetSkill(SkillName.MagicResist, 120.2, 140.0); + SetSkill(SkillName.Tactics, 90.1, 105.0); + SetSkill(SkillName.Wrestling, 90.1, 105.0); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 20; + } + + public Semidar(Serial serial) : base(serial) + { + } + + public override ChampionSkullType SkullType => ChampionSkullType.Pain; + + public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) }; + + public override Type[] SharedList => new[] + { typeof(RoyalGuardSurvivalKnife), typeof(ANecromancerShroud), typeof(LieutenantOfTheBritannianRoyalGuard) }; + + public override Type[] DecorativeList => new[] { typeof(LavaTile), typeof(DemonSkull) }; + + public override MonsterStatuetteType[] StatueTypes => Array.Empty(); + + public override string DefaultName => "Semidar"; + + public override bool Unprovokable => true; + public override Poison PoisonImmune => Poison.Lethal; + + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 4); + AddLoot(LootPack.FilthyRich); + } + + public override void CheckReflect(Mobile caster, ref bool reflect) + { + if (caster.Body.IsMale) { - Body = 174; - BaseSoundID = 0x4B0; - - SetStr(502, 600); - SetDex(102, 200); - SetInt(601, 750); - - SetHits(1500); - SetStam(103, 250); - - SetDamage(29, 35); - - SetDamageType(ResistanceType.Physical, 75); - SetDamageType(ResistanceType.Fire, 25); - - SetResistance(ResistanceType.Physical, 20, 30); - SetResistance(ResistanceType.Fire, 50, 60); - SetResistance(ResistanceType.Cold, 20, 30); - SetResistance(ResistanceType.Poison, 20, 30); - SetResistance(ResistanceType.Energy, 10, 20); - - SetSkill(SkillName.EvalInt, 95.1, 100.0); - SetSkill(SkillName.Magery, 90.1, 105.0); - SetSkill(SkillName.Meditation, 95.1, 100.0); - SetSkill(SkillName.MagicResist, 120.2, 140.0); - SetSkill(SkillName.Tactics, 90.1, 105.0); - SetSkill(SkillName.Wrestling, 90.1, 105.0); - - Fame = 24000; - Karma = -24000; - - VirtualArmor = 20; - } - - public Semidar(Serial serial) : base(serial) - { - } - - public override ChampionSkullType SkullType => ChampionSkullType.Pain; - - public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) }; - - public override Type[] SharedList => new[] - { typeof(RoyalGuardSurvivalKnife), typeof(ANecromancerShroud), typeof(LieutenantOfTheBritannianRoyalGuard) }; - - public override Type[] DecorativeList => new[] { typeof(LavaTile), typeof(DemonSkull) }; - - public override MonsterStatuetteType[] StatueTypes => Array.Empty(); - - public override string DefaultName => "Semidar"; - - public override bool Unprovokable => true; - public override Poison PoisonImmune => Poison.Lethal; - - public override void GenerateLoot() - { - AddLoot(LootPack.UltraRich, 4); - AddLoot(LootPack.FilthyRich); - } - - public override void CheckReflect(Mobile caster, ref bool reflect) - { - if (caster.Body.IsMale) - { - reflect = true; // Always reflect if caster isn't female - } - } - - public override void AlterDamageScalarFrom(Mobile caster, ref double scalar) - { - if (caster.Body.IsMale) - { - scalar = 20; // Male bodies always reflect.. damage scaled 20x - } - } - - public void DrainLife() - { - if (Map == null) - { - return; - } - - var eable = GetMobilesInRange(2); - - foreach (var m in eable) - { - if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive)) - { - continue; - } - - if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team)) - { - continue; - } - - DoHarmful(m); - - m.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist); - m.PlaySound(0x231); - - m.SendMessage("You feel the life drain out of you!"); - - var toDrain = Utility.RandomMinMax(10, 40); - - Hits += toDrain; - m.Damage(toDrain, this); - } - - eable.Free(); - } - - public override void OnGaveMeleeAttack(Mobile defender, int damage) - { - base.OnGaveMeleeAttack(defender, damage); - - if (Utility.RandomDouble() <= 0.25) - { - DrainLife(); - } - } - - public override void OnGotMeleeAttack(Mobile attacker, int damage) - { - base.OnGotMeleeAttack(attacker, damage); - - if (Utility.RandomDouble() <= 0.25) - { - DrainLife(); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); + reflect = true; // Always reflect if caster isn't female } } + + public override void AlterDamageScalarFrom(Mobile caster, ref double scalar) + { + if (caster.Body.IsMale) + { + scalar = 20; // Male bodies always reflect.. damage scaled 20x + } + } + + private static MonsterAbility[] _abilities = { new SemidarDrainLife() }; + public override MonsterAbility[] GetMonsterAbilities() => _abilities; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + + private class SemidarDrainLife : DrainLifeAreaAttack + { + public override double ChanceToTrigger => 0.25; + } }