From fc3aa13b8ef5a19715c2151bab0beb134b9160bd Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 25 Aug 2023 22:42:23 -0700 Subject: [PATCH] fix: Fixes memory leak in virtual mounts and codegens specials (#1476) --- .../Mobiles/Guards/BaseFactionGuard.cs | 47 +- .../Server.Factions.VirtualMountItem.v0.json | 11 + .../Server.Mobiles.Barracoon.v0.json | 4 + .../Server.Mobiles.BaseChampion.v0.json | 4 + .../Server.Mobiles.BaseShieldGuard.v0.json | 4 + ...Server.Mobiles.CapturedHordeMinion.v0.json | 4 + .../Server.Mobiles.ChaosGuard.v0.json | 4 + .../Server.Mobiles.DarkGuardian.v0.json | 4 + .../Server.Mobiles.Harrower.v1.json | 28 + .../Server.Mobiles.HarrowerTentacles.v0.json | 11 + .../Server.Mobiles.LordOaks.v0.json | 19 + .../Server.Mobiles.Mephitis.v0.json | 4 + ...ver.Mobiles.Neira.VirtualMountItem.v0.json | 11 + .../Migrations/Server.Mobiles.Neira.v1.json | 4 + .../Server.Mobiles.OrderGuard.v0.json | 4 + .../Migrations/Server.Mobiles.Rikktor.v0.json | 4 + .../Migrations/Server.Mobiles.Semidar.v0.json | 4 + .../Migrations/Server.Mobiles.Serado.v0.json | 4 + .../Server.Mobiles.ServantOfSemidar.v0.json | 4 + .../Migrations/Server.Mobiles.Silvani.v0.json | 4 + .../UOContent/Mobiles/Special/Barracoon.cs | 331 +++--- .../UOContent/Mobiles/Special/BaseChampion.cs | 507 +++++---- .../Mobiles/Special/BaseShieldGuard.cs | 223 ++-- .../Mobiles/Special/CapturedHordeMinion.cs | 34 +- .../UOContent/Mobiles/Special/ChaosGuard.cs | 43 +- .../UOContent/Mobiles/Special/DarkGuardian.cs | 105 +- .../UOContent/Mobiles/Special/Harrower.cs | 996 +++++++++--------- .../Mobiles/Special/HarrowerTentacles.cs | 238 ++--- .../UOContent/Mobiles/Special/LordOaks.cs | 55 +- .../UOContent/Mobiles/Special/Mephitis.cs | 115 +- Projects/UOContent/Mobiles/Special/Neira.cs | 412 ++++---- .../UOContent/Mobiles/Special/OrderGuard.cs | 43 +- Projects/UOContent/Mobiles/Special/Paragon.cs | 370 ++++--- Projects/UOContent/Mobiles/Special/Rikktor.cs | 219 ++-- Projects/UOContent/Mobiles/Special/Semidar.cs | 20 +- Projects/UOContent/Mobiles/Special/Serado.cs | 251 ++--- .../Mobiles/Special/ServantOfSemidar.cs | 46 +- Projects/UOContent/Mobiles/Special/Silvani.cs | 23 +- 38 files changed, 2029 insertions(+), 2185 deletions(-) create mode 100644 Projects/UOContent/Migrations/Server.Factions.VirtualMountItem.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Barracoon.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.BaseChampion.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.BaseShieldGuard.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.CapturedHordeMinion.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.ChaosGuard.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.DarkGuardian.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Harrower.v1.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Mephitis.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Neira.VirtualMountItem.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Neira.v1.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.OrderGuard.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Rikktor.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Semidar.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Serado.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.ServantOfSemidar.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.Silvani.v0.json diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs index 42463ba6e6..598ddb7732 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs @@ -1,4 +1,5 @@ using System; +using ModernUO.Serialization; using Server.Factions.AI; using Server.Items; using Server.Mobiles; @@ -485,45 +486,43 @@ public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill) } } - public class VirtualMountItem : Item, IMountItem + [SerializationGenerator(0, false)] + public partial class VirtualMountItem : Item, IMountItem { - private readonly VirtualMount m_Mount; + private VirtualMount _mount; + + [SerializableField(0)] + private Mobile _rider; public VirtualMountItem(Mobile mob) : base(0x3EA0) { Layer = Layer.Mount; Rider = mob; - m_Mount = new VirtualMount(this); + _mount = new VirtualMount(this); } - public VirtualMountItem(Serial serial) : base(serial) => m_Mount = new VirtualMount(this); - - public Mobile Rider { get; private set; } - - public IMount Mount => m_Mount; + public IMount Mount => _mount; - public override void Serialize(IGenericWriter writer) + [AfterDeserialization] + private void AfterDeserialization() { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(Rider); + if (_rider?.Deleted != false) + { + Delete(); + } + else + { + _mount = new VirtualMount(this); + } } - public override void Deserialize(IGenericReader reader) + public override DeathMoveResult OnParentDeath(Mobile parent) { - base.Deserialize(reader); + _mount = null; + Delete(); - var version = reader.ReadInt(); - - Rider = reader.ReadEntity(); - - if (Rider == null) - { - Delete(); - } + return DeathMoveResult.RemainEquipped; } } } diff --git a/Projects/UOContent/Migrations/Server.Factions.VirtualMountItem.v0.json b/Projects/UOContent/Migrations/Server.Factions.VirtualMountItem.v0.json new file mode 100644 index 0000000000..efaca184b7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Factions.VirtualMountItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Factions.VirtualMountItem", + "properties": [ + { + "name": "Rider", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Barracoon.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Barracoon.v0.json new file mode 100644 index 0000000000..b9c22f9304 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Barracoon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.Barracoon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseChampion.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseChampion.v0.json new file mode 100644 index 0000000000..80298b4043 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.BaseChampion.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.BaseChampion" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseShieldGuard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseShieldGuard.v0.json new file mode 100644 index 0000000000..152c2bc25a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.BaseShieldGuard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.BaseShieldGuard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.CapturedHordeMinion.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.CapturedHordeMinion.v0.json new file mode 100644 index 0000000000..a2a75d421c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.CapturedHordeMinion.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.CapturedHordeMinion" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.ChaosGuard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.ChaosGuard.v0.json new file mode 100644 index 0000000000..ba12dae06c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.ChaosGuard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.ChaosGuard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.DarkGuardian.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.DarkGuardian.v0.json new file mode 100644 index 0000000000..032eeab35d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.DarkGuardian.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.DarkGuardian" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Harrower.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.Harrower.v1.json new file mode 100644 index 0000000000..47fef73231 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Harrower.v1.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "type": "Server.Mobiles.Harrower", + "properties": [ + { + "name": "TrueForm", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "GateItem", + "type": "Server.Item", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Tentacles", + "type": "System.Collections.Generic.List\u003CServer.Mobiles.HarrowerTentacles\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Mobiles.HarrowerTentacles", + "SerializableInterfaceMigrationRule" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json new file mode 100644 index 0000000000..0866656e3e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Mobiles.HarrowerTentacles", + "properties": [ + { + "name": "Harrower", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json new file mode 100644 index 0000000000..435aa41217 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json @@ -0,0 +1,19 @@ +{ + "version": 0, + "type": "Server.Mobiles.LordOaks", + "properties": [ + { + "name": "Queen", + "type": "Server.Mobiles.BaseCreature", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "SpawnedQueen", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Mephitis.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Mephitis.v0.json new file mode 100644 index 0000000000..774396f2c0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Mephitis.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.Mephitis" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Neira.VirtualMountItem.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Neira.VirtualMountItem.v0.json new file mode 100644 index 0000000000..60674a74db --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Neira.VirtualMountItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Mobiles.Neira.VirtualMountItem", + "properties": [ + { + "name": "Rider", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Neira.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.Neira.v1.json new file mode 100644 index 0000000000..6096f83964 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Neira.v1.json @@ -0,0 +1,4 @@ +{ + "version": 1, + "type": "Server.Mobiles.Neira" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.OrderGuard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.OrderGuard.v0.json new file mode 100644 index 0000000000..e2ff45055a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.OrderGuard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.OrderGuard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Rikktor.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Rikktor.v0.json new file mode 100644 index 0000000000..19e49091ee --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Rikktor.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.Rikktor" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Semidar.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Semidar.v0.json new file mode 100644 index 0000000000..19f76f831a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Semidar.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.Semidar" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Serado.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Serado.v0.json new file mode 100644 index 0000000000..ce451c8787 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Serado.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.Serado" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.ServantOfSemidar.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.ServantOfSemidar.v0.json new file mode 100644 index 0000000000..93378bcf86 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.ServantOfSemidar.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.ServantOfSemidar" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Silvani.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Silvani.v0.json new file mode 100644 index 0000000000..faa85ce559 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Silvani.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.Silvani" +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/Special/Barracoon.cs b/Projects/UOContent/Mobiles/Special/Barracoon.cs index 422b5fe943..26e356c643 100644 --- a/Projects/UOContent/Mobiles/Special/Barracoon.cs +++ b/Projects/UOContent/Mobiles/Special/Barracoon.cs @@ -1,251 +1,234 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Items; using Server.Spells.Fifth; using Server.Spells.Seventh; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class Barracoon : BaseChampion { - public class Barracoon : BaseChampion + [Constructible] + public Barracoon() : base(AIType.AI_Melee) { - [Constructible] - public Barracoon() : base(AIType.AI_Melee) - { - Title = "the piper"; - Body = 0x190; - Hue = 0x83EC; + Title = "the piper"; + Body = 0x190; + Hue = 0x83EC; - SetStr(305, 425); - SetDex(72, 150); - SetInt(505, 750); + SetStr(305, 425); + SetDex(72, 150); + SetInt(505, 750); - SetHits(4200); - SetStam(102, 300); + SetHits(4200); + SetStam(102, 300); - SetDamage(25, 35); + SetDamage(25, 35); - SetDamageType(ResistanceType.Physical, 100); + SetDamageType(ResistanceType.Physical, 100); - SetResistance(ResistanceType.Physical, 60, 70); - SetResistance(ResistanceType.Fire, 50, 60); - SetResistance(ResistanceType.Cold, 50, 60); - SetResistance(ResistanceType.Poison, 40, 50); - SetResistance(ResistanceType.Energy, 40, 50); + SetResistance(ResistanceType.Physical, 60, 70); + SetResistance(ResistanceType.Fire, 50, 60); + SetResistance(ResistanceType.Cold, 50, 60); + SetResistance(ResistanceType.Poison, 40, 50); + SetResistance(ResistanceType.Energy, 40, 50); - SetSkill(SkillName.MagicResist, 100.0); - SetSkill(SkillName.Tactics, 97.6, 100.0); - SetSkill(SkillName.Wrestling, 97.6, 100.0); + SetSkill(SkillName.MagicResist, 100.0); + SetSkill(SkillName.Tactics, 97.6, 100.0); + SetSkill(SkillName.Wrestling, 97.6, 100.0); - Fame = 22500; - Karma = -22500; + Fame = 22500; + Karma = -22500; - VirtualArmor = 70; + VirtualArmor = 70; - AddItem(new FancyShirt(Utility.RandomGreenHue())); - AddItem(new LongPants(Utility.RandomYellowHue())); - AddItem(new JesterHat(Utility.RandomPinkHue())); - AddItem(new Cloak(Utility.RandomPinkHue())); - AddItem(new Sandals()); + AddItem(new FancyShirt(Utility.RandomGreenHue())); + AddItem(new LongPants(Utility.RandomYellowHue())); + AddItem(new JesterHat(Utility.RandomPinkHue())); + AddItem(new Cloak(Utility.RandomPinkHue())); + AddItem(new Sandals()); - HairItemID = 0x203B; // Short Hair - HairHue = 0x94; - } + HairItemID = 0x203B; // Short Hair + HairHue = 0x94; + } - public Barracoon(Serial serial) : base(serial) - { - } + public override ChampionSkullType SkullType => ChampionSkullType.Greed; - public override ChampionSkullType SkullType => ChampionSkullType.Greed; + public override Type[] UniqueList => new[] { typeof(FangOfRactus) }; - public override Type[] UniqueList => new[] { typeof(FangOfRactus) }; + public override Type[] SharedList => new[] + { + typeof(EmbroideredOakLeafCloak), + typeof(DjinnisRing), + typeof(DetectiveBoots), + typeof(GuantletsOfAnger) + }; - public override Type[] SharedList => new[] - { - typeof(EmbroideredOakLeafCloak), - typeof(DjinnisRing), - typeof(DetectiveBoots), - typeof(GuantletsOfAnger) - }; + public override Type[] DecorativeList => new[] { typeof(SwampTile), typeof(MonsterStatuette) }; - public override Type[] DecorativeList => new[] { typeof(SwampTile), typeof(MonsterStatuette) }; + public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Slime }; - public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Slime }; + public override string DefaultName => "Barracoon"; - public override string DefaultName => "Barracoon"; + public override bool AlwaysMurderer => true; + public override bool AutoDispel => true; + public override double AutoDispelChance => 1.0; + public override bool BardImmune => !Core.SE; + public override bool Unprovokable => Core.SE; + public override bool Uncalmable => Core.SE; + public override Poison PoisonImmune => Poison.Deadly; - public override bool AlwaysMurderer => true; - public override bool AutoDispel => true; - public override double AutoDispelChance => 1.0; - public override bool BardImmune => !Core.SE; - public override bool Unprovokable => Core.SE; - public override bool Uncalmable => Core.SE; - public override Poison PoisonImmune => Poison.Deadly; + public override bool ShowFameTitle => false; + public override bool ClickTitle => false; - public override bool ShowFameTitle => false; - public override bool ClickTitle => false; + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 3); + } - public override void GenerateLoot() + public void Polymorph(Mobile m) + { + if (!m.CanBeginAction() || !m.CanBeginAction() || m.IsBodyMod) { - AddLoot(LootPack.UltraRich, 3); + return; } - public void Polymorph(Mobile m) + var mount = m.Mount; + + if (mount != null) { - if (!m.CanBeginAction() || !m.CanBeginAction() || m.IsBodyMod) - { - return; - } + mount.Rider = null; + } - var mount = m.Mount; + if (m.Mounted) + { + return; + } - if (mount != null) - { - mount.Rider = null; - } + if (m.BeginAction()) + { + var disarm = m.FindItemOnLayer(Layer.OneHanded); - if (m.Mounted) + if (disarm?.Movable == true) { - return; + m.AddToBackpack(disarm); } - if (m.BeginAction()) - { - var disarm = m.FindItemOnLayer(Layer.OneHanded); - - if (disarm?.Movable == true) - { - m.AddToBackpack(disarm); - } - - disarm = m.FindItemOnLayer(Layer.TwoHanded); + disarm = m.FindItemOnLayer(Layer.TwoHanded); - if (disarm?.Movable == true) - { - m.AddToBackpack(disarm); - } + if (disarm?.Movable == true) + { + m.AddToBackpack(disarm); + } - m.BodyMod = 42; - m.HueMod = 0; + m.BodyMod = 42; + m.HueMod = 0; - new ExpirePolymorphTimer(m).Start(); - } + new ExpirePolymorphTimer(m).Start(); } + } - public void SpawnRatmen(Mobile target) - { - var map = Map; + public void SpawnRatmen(Mobile target) + { + var map = Map; - if (map == null) - { - return; - } + if (map == null) + { + return; + } - var eable = GetMobilesInRange(10); - var rats = 0; + var eable = GetMobilesInRange(10); + var rats = 0; - foreach (var m in eable) + foreach (var m in eable) + { + if (m is Ratman or RatmanArcher or RatmanMage) { - if (m is Ratman or RatmanArcher or RatmanMage) + rats++; + if (rats >= 16) { - rats++; - if (rats >= 16) - { - eable.Free(); - return; - } + eable.Free(); + return; } } + } - eable.Free(); + eable.Free(); - PlaySound(0x3D); + PlaySound(0x3D); - rats = Utility.RandomMinMax(3, 6); + rats = Utility.RandomMinMax(3, 6); - for (var i = 0; i < rats; ++i) + for (var i = 0; i < rats; ++i) + { + var rat = Utility.Random(5) switch { - var rat = Utility.Random(5) switch - { - 2 => (BaseCreature)new RatmanArcher(), - 3 => new RatmanArcher(), - 4 => new RatmanMage(), - _ => new Ratman() - }; - - rat.Team = Team; - rat.MoveToWorld(map.GetRandomNearbyLocation(Location), map); - rat.Combatant = target; - } + 2 => (BaseCreature)new RatmanArcher(), + 3 => new RatmanArcher(), + 4 => new RatmanMage(), + _ => new Ratman() + }; + + rat.Team = Team; + rat.MoveToWorld(map.GetRandomNearbyLocation(Location), map); + rat.Combatant = target; } + } - public void DoSpecialAbility(Mobile target) + public void DoSpecialAbility(Mobile target) + { + if (target?.Deleted != false) // sanity { - if (target?.Deleted != false) // sanity - { - return; - } - - if (Utility.RandomDouble() < 0.6) // 60% chance to polymorph attacker into a ratman - { - Polymorph(target); - } - - if (Utility.RandomDouble() < 0.2) // 20% chance to spawn more ratmen - { - SpawnRatmen(target); - } - - if (Hits < 500 && !IsBodyMod) // Baracoon is low on life, polymorph into a ratman - { - Polymorph(this); - } + return; } - public override void OnGotMeleeAttack(Mobile attacker, int damage) + if (Utility.RandomDouble() < 0.6) // 60% chance to polymorph attacker into a ratman { - base.OnGotMeleeAttack(attacker, damage); - - DoSpecialAbility(attacker); + Polymorph(target); } - public override void OnGaveMeleeAttack(Mobile defender, int damage) + if (Utility.RandomDouble() < 0.2) // 20% chance to spawn more ratmen { - base.OnGaveMeleeAttack(defender, damage); - - DoSpecialAbility(defender); + SpawnRatmen(target); } - public override void Serialize(IGenericWriter writer) + if (Hits < 500 && !IsBodyMod) // Baracoon is low on life, polymorph into a ratman { - base.Serialize(writer); - - writer.Write(0); // version + Polymorph(this); } + } - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override void OnGotMeleeAttack(Mobile attacker, int damage) + { + base.OnGotMeleeAttack(attacker, damage); - var version = reader.ReadInt(); - } + DoSpecialAbility(attacker); + } - private class ExpirePolymorphTimer : Timer - { - private readonly Mobile m_Owner; + public override void OnGaveMeleeAttack(Mobile defender, int damage) + { + base.OnGaveMeleeAttack(defender, damage); - public ExpirePolymorphTimer(Mobile owner) : base(TimeSpan.FromMinutes(3.0)) - { - m_Owner = owner; - } + DoSpecialAbility(defender); + } + + private class ExpirePolymorphTimer : Timer + { + private readonly Mobile m_Owner; - protected override void OnTick() + public ExpirePolymorphTimer(Mobile owner) : base(TimeSpan.FromMinutes(3.0)) + { + m_Owner = owner; + } + + protected override void OnTick() + { + if (!m_Owner.CanBeginAction()) { - if (!m_Owner.CanBeginAction()) - { - m_Owner.BodyMod = 0; - m_Owner.HueMod = -1; - m_Owner.EndAction(); - } + m_Owner.BodyMod = 0; + m_Owner.HueMod = -1; + m_Owner.EndAction(); } } } diff --git a/Projects/UOContent/Mobiles/Special/BaseChampion.cs b/Projects/UOContent/Mobiles/Special/BaseChampion.cs index e93565f07d..57ad2b290a 100644 --- a/Projects/UOContent/Mobiles/Special/BaseChampion.cs +++ b/Projects/UOContent/Mobiles/Special/BaseChampion.cs @@ -1,368 +1,351 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Engines.Virtues; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public abstract partial class BaseChampion : BaseCreature { - public abstract class BaseChampion : BaseCreature + public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18) { - public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18) + } + + public override bool CanMoveOverObstacles => true; + public override bool CanDestroyObstacles => true; + + public abstract ChampionSkullType SkullType { get; } + + public abstract Type[] UniqueList { get; } + public abstract Type[] SharedList { get; } + public abstract Type[] DecorativeList { get; } + public abstract MonsterStatuetteType[] StatueTypes { get; } + + public virtual bool NoGoodies => false; + + public Item GetArtifact() => + Utility.RandomDouble() switch { - } + < 0.05 => CreateArtifact(UniqueList), + < 0.15 => CreateArtifact(SharedList), + < 0.30 => CreateArtifact(DecorativeList), + _ => null + }; - public BaseChampion(Serial serial) : base(serial) + public Item CreateArtifact(Type[] list) + { + if (list.Length == 0) { + return null; } - public override bool CanMoveOverObstacles => true; - public override bool CanDestroyObstacles => true; + var type = list.RandomElement(); - public abstract ChampionSkullType SkullType { get; } + var artifact = Loot.Construct(type); - public abstract Type[] UniqueList { get; } - public abstract Type[] SharedList { get; } - public abstract Type[] DecorativeList { get; } - public abstract MonsterStatuetteType[] StatueTypes { get; } + if (StatueTypes.Length > 0 && artifact is MonsterStatuette statuette) + { + statuette.Type = StatueTypes.RandomElement(); + statuette.LootType = LootType.Regular; + } - public virtual bool NoGoodies => false; + return artifact; + } - public override void Serialize(IGenericWriter writer) + private static PowerScroll CreateRandomPowerScroll() + { + var level = Utility.RandomDouble() switch { - base.Serialize(writer); + < 0.05 => 20, + < 0.4 => 15, + _ => 10 + }; - writer.Write(0); // version - } + return PowerScroll.CreateRandomNoCraft(level, level); + } - public override void Deserialize(IGenericReader reader) + public void GivePowerScrolls() + { + if (Map != Map.Felucca) { - base.Deserialize(reader); - - var version = reader.ReadInt(); + return; } - public Item GetArtifact() => - Utility.RandomDouble() switch - { - < 0.05 => CreateArtifact(UniqueList), - < 0.15 => CreateArtifact(SharedList), - < 0.30 => CreateArtifact(DecorativeList), - _ => null - }; + var toGive = new List(); + var rights = GetLootingRights(DamageEntries, HitsMax); - public Item CreateArtifact(Type[] list) + for (var i = rights.Count - 1; i >= 0; --i) { - if (list.Length == 0) - { - return null; - } - - var type = list.RandomElement(); - - var artifact = Loot.Construct(type); + var ds = rights[i]; - if (StatueTypes.Length > 0 && artifact is MonsterStatuette statuette) + if (ds.m_HasRight) { - statuette.Type = StatueTypes.RandomElement(); - statuette.LootType = LootType.Regular; + toGive.Add(ds.m_Mobile); } - - return artifact; } - private static PowerScroll CreateRandomPowerScroll() + if (toGive.Count == 0) { - var level = Utility.RandomDouble() switch - { - < 0.05 => 20, - < 0.4 => 15, - _ => 10 - }; - - return PowerScroll.CreateRandomNoCraft(level, level); + return; } - public void GivePowerScrolls() + for (var i = 0; i < toGive.Count; i++) { - if (Map != Map.Felucca) - { - return; - } + var m = toGive[i]; - var toGive = new List(); - var rights = GetLootingRights(DamageEntries, HitsMax); - - for (var i = rights.Count - 1; i >= 0; --i) + if (m is not PlayerMobile pm) { - var ds = rights[i]; - - if (ds.m_HasRight) - { - toGive.Add(ds.m_Mobile); - } + continue; } - if (toGive.Count == 0) - { - return; - } + var gainedPath = false; - for (var i = 0; i < toGive.Count; i++) - { - var m = toGive[i]; + const int pointsToGain = 800; - if (m is not PlayerMobile pm) + if (VirtueSystem.Award(pm, VirtueName.Valor, pointsToGain, ref gainedPath)) + { + if (gainedPath) { - continue; + m.SendLocalizedMessage(1054032); // You have gained a path in Valor! } - - var gainedPath = false; - - const int pointsToGain = 800; - - if (VirtueSystem.Award(pm, VirtueName.Valor, pointsToGain, ref gainedPath)) + else { - if (gainedPath) - { - m.SendLocalizedMessage(1054032); // You have gained a path in Valor! - } - else - { - m.SendLocalizedMessage(1054030); // You have gained in Valor! - } - - // No delay on Valor gains + m.SendLocalizedMessage(1054030); // You have gained in Valor! } + + // No delay on Valor gains } + } - // Randomize - toGive.Shuffle(); + // Randomize + toGive.Shuffle(); - for (var i = 0; i < 6; ++i) - { - var m = toGive[i % toGive.Count]; + for (var i = 0; i < 6; ++i) + { + var m = toGive[i % toGive.Count]; - var ps = CreateRandomPowerScroll(); + var ps = CreateRandomPowerScroll(); - GivePowerScrollTo(m, ps); - } + GivePowerScrollTo(m, ps); } + } - public static void GivePowerScrollTo(Mobile m, PowerScroll ps) + public static void GivePowerScrollTo(Mobile m, PowerScroll ps) + { + if (ps == null || m == null) // sanity { - if (ps == null || m == null) // sanity - { - return; - } + return; + } - m.SendLocalizedMessage(1049524); // You have received a scroll of power! + m.SendLocalizedMessage(1049524); // You have received a scroll of power! - if (!Core.SE || m.Alive) + if (!Core.SE || m.Alive) + { + m.AddToBackpack(ps); + } + else + { + if (m.Corpse?.Deleted == false) { - m.AddToBackpack(ps); + m.Corpse.DropItem(ps); } else { - if (m.Corpse?.Deleted == false) - { - m.Corpse.DropItem(ps); - } - else - { - m.AddToBackpack(ps); - } + m.AddToBackpack(ps); } + } - if (m is not PlayerMobile pm) - { - return; - } + if (m is not PlayerMobile pm) + { + return; + } - var prot = JusticeVirtue.GetProtector(pm); + var prot = JusticeVirtue.GetProtector(pm); - if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || - !JusticeVirtue.CheckMapRegion(pm, prot)) - { - return; - } + if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || + !JusticeVirtue.CheckMapRegion(pm, prot)) + { + return; + } - var chance = VirtueSystem.GetLevel(prot, VirtueName.Justice) switch - { - VirtueLevel.Seeker => 60, - VirtueLevel.Follower => 80, - VirtueLevel.Knight => 100, - _ => 0 - }; + var chance = VirtueSystem.GetLevel(prot, VirtueName.Justice) switch + { + VirtueLevel.Seeker => 60, + VirtueLevel.Follower => 80, + VirtueLevel.Knight => 100, + _ => 0 + }; - if (chance > 0 && chance > Utility.Random(100)) - { - var powerScroll = new PowerScroll(ps.Skill, ps.Value); + if (chance > 0 && chance > Utility.Random(100)) + { + var powerScroll = new PowerScroll(ps.Skill, ps.Value); - prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice! + prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice! - if (!Core.SE || prot.Alive) - { - prot.AddToBackpack(powerScroll); - } - else if (prot.Corpse?.Deleted == false) - { - prot.Corpse.DropItem(powerScroll); - } - else - { - prot.AddToBackpack(powerScroll); - } + if (!Core.SE || prot.Alive) + { + prot.AddToBackpack(powerScroll); + } + else if (prot.Corpse?.Deleted == false) + { + prot.Corpse.DropItem(powerScroll); + } + else + { + prot.AddToBackpack(powerScroll); } } + } - public override bool OnBeforeDeath() + public override bool OnBeforeDeath() + { + if (!NoKillAwards) { - if (!NoKillAwards) - { - GivePowerScrolls(); + GivePowerScrolls(); - if (NoGoodies) - { - return base.OnBeforeDeath(); - } + if (NoGoodies) + { + return base.OnBeforeDeath(); + } - var map = Map; + var map = Map; - if (map != null) + if (map != null) + { + for (var x = -12; x <= 12; ++x) { - for (var x = -12; x <= 12; ++x) + for (var y = -12; y <= 12; ++y) { - for (var y = -12; y <= 12; ++y) - { - var dist = Math.Sqrt(x * x + y * y); + var dist = Math.Sqrt(x * x + y * y); - if (dist <= 12) - { - new GoodiesTimer(map, X + x, Y + y).Start(); - } + if (dist <= 12) + { + new GoodiesTimer(map, X + x, Y + y).Start(); } } } } - - return base.OnBeforeDeath(); } - public override void OnDeath(Container c) - { - if (Map == Map.Felucca) - { - // TODO: Confirm SE change or AoS one too? - var rights = GetLootingRights(DamageEntries, HitsMax); - var toGive = new List(); + return base.OnBeforeDeath(); + } - for (var i = rights.Count - 1; i >= 0; --i) - { - var ds = rights[i]; + public override void OnDeath(Container c) + { + if (Map == Map.Felucca) + { + // TODO: Confirm SE change or AoS one too? + var rights = GetLootingRights(DamageEntries, HitsMax); + var toGive = new List(); - if (ds.m_HasRight) - { - toGive.Add(ds.m_Mobile); - } - } + for (var i = rights.Count - 1; i >= 0; --i) + { + var ds = rights[i]; - if (toGive.Count > 0) - { - toGive.RandomElement().AddToBackpack(new ChampionSkull(SkullType)); - } - else + if (ds.m_HasRight) { - c.DropItem(new ChampionSkull(SkullType)); + toGive.Add(ds.m_Mobile); } } - base.OnDeath(c); + if (toGive.Count > 0) + { + toGive.RandomElement().AddToBackpack(new ChampionSkull(SkullType)); + } + else + { + c.DropItem(new ChampionSkull(SkullType)); + } } - private class GoodiesTimer : Timer + base.OnDeath(c); + } + + private class GoodiesTimer : Timer + { + private readonly Map m_Map; + private readonly int m_X; + private readonly int m_Y; + + public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0)) { - private readonly Map m_Map; - private readonly int m_X; - private readonly int m_Y; + m_Map = map; + m_X = x; + m_Y = y; + } - public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0)) - { - m_Map = map; - m_X = x; - m_Y = y; - } + protected override void OnTick() + { + var z = m_Map.GetAverageZ(m_X, m_Y); + var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false); - protected override void OnTick() + for (var i = -3; !canFit && i <= 3; ++i) { - var z = m_Map.GetAverageZ(m_X, m_Y); - var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false); + canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false); - for (var i = -3; !canFit && i <= 3; ++i) + if (canFit) { - canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false); - - if (canFit) - { - z += i; - } + z += i; } + } - if (!canFit) - { - return; - } + if (!canFit) + { + return; + } - var g = new Gold(500, 1000); + var g = new Gold(500, 1000); - g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map); + g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map); - if (Utility.RandomDouble() < 0.05) - { - return; - } + if (Utility.RandomDouble() < 0.05) + { + return; + } - switch (Utility.Random(3)) - { - case 0: // Fire column - { - Effects.SendLocationParticles( - EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), - 0x3709, - 10, - 30, - 5052 - ); - Effects.PlaySound(g, 0x208); - - break; - } - case 1: // Explosion - { - Effects.SendLocationParticles( - EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), - 0x36BD, - 20, - 10, - 5044 - ); - Effects.PlaySound(g, 0x307); - - break; - } - case 2: // Ball of fire - { - Effects.SendLocationParticles( - EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), - 0x36FE, - 10, - 10, - 5052 - ); - - break; - } - } + switch (Utility.Random(3)) + { + case 0: // Fire column + { + Effects.SendLocationParticles( + EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), + 0x3709, + 10, + 30, + 5052 + ); + Effects.PlaySound(g, 0x208); + + break; + } + case 1: // Explosion + { + Effects.SendLocationParticles( + EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), + 0x36BD, + 20, + 10, + 5044 + ); + Effects.PlaySound(g, 0x307); + + break; + } + case 2: // Ball of fire + { + Effects.SendLocationParticles( + EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), + 0x36FE, + 10, + 10, + 5052 + ); + + break; + } } } } diff --git a/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs b/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs index 3ca4c32325..c0511d5bfb 100644 --- a/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs +++ b/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs @@ -1,166 +1,163 @@ +using ModernUO.Serialization; using Server.Guilds; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public abstract partial class BaseShieldGuard : BaseCreature { - public abstract class BaseShieldGuard : BaseCreature + public BaseShieldGuard() : base(AIType.AI_Melee, FightMode.Aggressor, 14) { - public BaseShieldGuard() : base(AIType.AI_Melee, FightMode.Aggressor, 14) - { - InitStats(1000, 1000, 1000); - Title = "the guard"; + InitStats(1000, 1000, 1000); + Title = "the guard"; - SetSpeed(0.5, 2.0); - SpeechHue = Utility.RandomDyedHue(); - Hue = Race.Human.RandomSkinHue(); + SetSpeed(0.5, 2.0); + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); - if (Female = Utility.RandomBool()) - { - Body = 0x191; - Name = NameList.RandomName("female"); + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); - AddItem(new FemalePlateChest()); - AddItem(new PlateArms()); - AddItem(new PlateLegs()); + AddItem(new FemalePlateChest()); + AddItem(new PlateArms()); + AddItem(new PlateLegs()); - switch (Utility.Random(2)) - { - case 0: + switch (Utility.Random(2)) + { + case 0: + { AddItem(new Doublet(Utility.RandomNondyedHue())); break; - case 1: + } + case 1: + { AddItem(new BodySash(Utility.RandomNondyedHue())); break; - } + } + } - switch (Utility.Random(2)) - { - case 0: + switch (Utility.Random(2)) + { + case 0: + { AddItem(new Skirt(Utility.RandomNondyedHue())); break; - case 1: + } + case 1: + { AddItem(new Kilt(Utility.RandomNondyedHue())); break; - } + } } - else - { - Body = 0x190; - Name = NameList.RandomName("male"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); - AddItem(new PlateChest()); - AddItem(new PlateArms()); - AddItem(new PlateLegs()); + AddItem(new PlateChest()); + AddItem(new PlateArms()); + AddItem(new PlateLegs()); - switch (Utility.Random(3)) - { - case 0: + switch (Utility.Random(3)) + { + case 0: + { AddItem(new Doublet(Utility.RandomNondyedHue())); break; - case 1: + } + case 1: + { AddItem(new Tunic(Utility.RandomNondyedHue())); break; - case 2: + } + case 2: + { AddItem(new BodySash(Utility.RandomNondyedHue())); break; - } + } } + } - Utility.AssignRandomHair(this); - if (Utility.RandomBool()) - { - Utility.AssignRandomFacialHair(this, HairHue); - } + Utility.AssignRandomHair(this); + if (Utility.RandomBool()) + { + Utility.AssignRandomFacialHair(this, HairHue); + } - var weapon = new VikingSword(); - weapon.Movable = false; - AddItem(weapon); + var weapon = new VikingSword(); + weapon.Movable = false; + AddItem(weapon); - var shield = Shield; - shield.Movable = false; - AddItem(shield); + var shield = Shield; + shield.Movable = false; + AddItem(shield); - PackGold(250, 500); + PackGold(250, 500); - Skills.Anatomy.Base = 120.0; - Skills.Tactics.Base = 120.0; - Skills.Swords.Base = 120.0; - Skills.MagicResist.Base = 120.0; - Skills.DetectHidden.Base = 100.0; - } + Skills.Anatomy.Base = 120.0; + Skills.Tactics.Base = 120.0; + Skills.Swords.Base = 120.0; + Skills.MagicResist.Base = 120.0; + Skills.DetectHidden.Base = 100.0; + } - public BaseShieldGuard(Serial serial) : base(serial) + public abstract int Keyword { get; } + public abstract BaseShield Shield { get; } + public abstract int SignupNumber { get; } + public abstract GuildType Type { get; } + + public override bool HandlesOnSpeech(Mobile from) + { + if (from.InRange(Location, 2)) { + return true; } - public abstract int Keyword { get; } - public abstract BaseShield Shield { get; } - public abstract int SignupNumber { get; } - public abstract GuildType Type { get; } + return base.HandlesOnSpeech(from); + } - public override bool HandlesOnSpeech(Mobile from) + public override void OnSpeech(SpeechEventArgs e) + { + if (!e.Handled && e.HasKeyword(Keyword) && e.Mobile.InRange(Location, 2)) { - if (from.InRange(Location, 2)) - { - return true; - } + e.Handled = true; - return base.HandlesOnSpeech(from); - } + var from = e.Mobile; - public override void OnSpeech(SpeechEventArgs e) - { - if (!e.Handled && e.HasKeyword(Keyword) && e.Mobile.InRange(Location, 2)) + if (from.Guild is not Guild g || g.Type != Type) { - e.Handled = true; - - var from = e.Mobile; + Say(SignupNumber); + } + else + { + var pack = from.Backpack; + var shield = Shield; + var twoHanded = from.FindItemOnLayer(Layer.TwoHanded); - if (from.Guild is not Guild g || g.Type != Type) + if (pack?.FindItemByType(shield.GetType()) != null || + twoHanded != null && shield.GetType().IsInstanceOfType(twoHanded)) { - Say(SignupNumber); + Say(1007110); // Why dost thou ask about virtue guards when thou art one? + shield.Delete(); + } + else if (from.PlaceInBackpack(shield)) + { + Say(Utility.Random(1007101, 5)); + Say(1007139); // I see you are in need of our shield, Here you go. + from.AddToBackpack(shield); } else { - var pack = from.Backpack; - var shield = Shield; - var twoHanded = from.FindItemOnLayer(Layer.TwoHanded); - - if (pack?.FindItemByType(shield.GetType()) != null || - twoHanded != null && shield.GetType().IsInstanceOfType(twoHanded)) - { - Say(1007110); // Why dost thou ask about virtue guards when thou art one? - shield.Delete(); - } - else if (from.PlaceInBackpack(shield)) - { - Say(Utility.Random(1007101, 5)); - Say(1007139); // I see you are in need of our shield, Here you go. - from.AddToBackpack(shield); - } - else - { - from.SendLocalizedMessage(502868); // Your backpack is too full. - shield.Delete(); - } + from.SendLocalizedMessage(502868); // Your backpack is too full. + shield.Delete(); } } - - base.OnSpeech(e); - } - - 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(); - } + base.OnSpeech(e); } } diff --git a/Projects/UOContent/Mobiles/Special/CapturedHordeMinion.cs b/Projects/UOContent/Mobiles/Special/CapturedHordeMinion.cs index 97b2ab0fa0..299a608413 100644 --- a/Projects/UOContent/Mobiles/Special/CapturedHordeMinion.cs +++ b/Projects/UOContent/Mobiles/Special/CapturedHordeMinion.cs @@ -1,30 +1,14 @@ -namespace Server.Mobiles -{ - public class CapturedHordeMinion : HordeMinion - { - [Constructible] - public CapturedHordeMinion() => FightMode = FightMode.None; - - public CapturedHordeMinion(Serial serial) : base(serial) - { - } - - public override bool InitialInnocent => true; +using ModernUO.Serialization; - public override bool CanBeDamaged() => false; +namespace Server.Mobiles; - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } +[SerializationGenerator(0)] +public partial class CapturedHordeMinion : HordeMinion +{ + [Constructible] + public CapturedHordeMinion() => FightMode = FightMode.None; - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override bool InitialInnocent => true; - var version = reader.ReadEncodedInt(); - } - } + public override bool CanBeDamaged() => false; } diff --git a/Projects/UOContent/Mobiles/Special/ChaosGuard.cs b/Projects/UOContent/Mobiles/Special/ChaosGuard.cs index af6d75a0ab..02efbc5319 100644 --- a/Projects/UOContent/Mobiles/Special/ChaosGuard.cs +++ b/Projects/UOContent/Mobiles/Special/ChaosGuard.cs @@ -1,38 +1,21 @@ +using ModernUO.Serialization; using Server.Guilds; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class ChaosGuard : BaseShieldGuard { - public class ChaosGuard : BaseShieldGuard + [Constructible] + public ChaosGuard() { - [Constructible] - public ChaosGuard() - { - } - - public ChaosGuard(Serial serial) : base(serial) - { - } - - public override int Keyword => 0x22; // *chaos shield* - public override BaseShield Shield => new ChaosShield(); - public override int SignupNumber => 1007140; // Sign up with a guild of chaos if thou art interested. - public override GuildType Type => GuildType.Chaos; - - public override bool BardImmune => true; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } + } - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override int Keyword => 0x22; // *chaos shield* + public override BaseShield Shield => new ChaosShield(); + public override int SignupNumber => 1007140; // Sign up with a guild of chaos if thou art interested. + public override GuildType Type => GuildType.Chaos; - var version = reader.ReadInt(); - } - } + public override bool BardImmune => true; } diff --git a/Projects/UOContent/Mobiles/Special/DarkGuardian.cs b/Projects/UOContent/Mobiles/Special/DarkGuardian.cs index 6f23269733..c9280c7c2f 100644 --- a/Projects/UOContent/Mobiles/Special/DarkGuardian.cs +++ b/Projects/UOContent/Mobiles/Special/DarkGuardian.cs @@ -1,79 +1,62 @@ +using ModernUO.Serialization; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class DarkGuardian : BaseCreature { - public class DarkGuardian : BaseCreature + [Constructible] + public DarkGuardian() : base(AIType.AI_Mage) { - [Constructible] - public DarkGuardian() : base(AIType.AI_Mage) - { - Body = 78; - BaseSoundID = 0x3E9; - - SetStr(125, 150); - SetDex(100, 120); - SetInt(200, 235); - - SetHits(150, 180); - - SetDamage(43, 48); + Body = 78; + BaseSoundID = 0x3E9; - SetDamageType(ResistanceType.Physical, 10); - SetDamageType(ResistanceType.Cold, 40); - SetDamageType(ResistanceType.Energy, 50); + SetStr(125, 150); + SetDex(100, 120); + SetInt(200, 235); - SetResistance(ResistanceType.Physical, 40, 50); - SetResistance(ResistanceType.Fire, 20, 45); - SetResistance(ResistanceType.Cold, 50, 60); - SetResistance(ResistanceType.Poison, 20, 45); - SetResistance(ResistanceType.Energy, 30, 40); + SetHits(150, 180); - SetSkill(SkillName.EvalInt, 40.1, 50); - SetSkill(SkillName.Magery, 50.1, 60.0); - SetSkill(SkillName.Meditation, 85.1, 95.0); - SetSkill(SkillName.MagicResist, 50.1, 70.0); - SetSkill(SkillName.Tactics, 50.1, 70.0); + SetDamage(43, 48); - Fame = 5000; - Karma = -5000; + SetDamageType(ResistanceType.Physical, 10); + SetDamageType(ResistanceType.Cold, 40); + SetDamageType(ResistanceType.Energy, 50); - VirtualArmor = 50; - PackNecroReg(15, 25); - PackItem(new DaemonBone(30)); - } + SetResistance(ResistanceType.Physical, 40, 50); + SetResistance(ResistanceType.Fire, 20, 45); + SetResistance(ResistanceType.Cold, 50, 60); + SetResistance(ResistanceType.Poison, 20, 45); + SetResistance(ResistanceType.Energy, 30, 40); - public DarkGuardian(Serial serial) : base(serial) - { - } + SetSkill(SkillName.EvalInt, 40.1, 50); + SetSkill(SkillName.Magery, 50.1, 60.0); + SetSkill(SkillName.Meditation, 85.1, 95.0); + SetSkill(SkillName.MagicResist, 50.1, 70.0); + SetSkill(SkillName.Tactics, 50.1, 70.0); - public override string CorpseName => "a dark guardians' corpse"; - public override string DefaultName => "a dark guardian"; + Fame = 5000; + Karma = -5000; - public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead; - - public override int TreasureMapLevel => 2; - public override bool BleedImmune => true; - public override Poison PoisonImmune => Poison.Lethal; - public override bool Unprovokable => true; - - public override void GenerateLoot() - { - AddLoot(LootPack.Rich); - AddLoot(LootPack.MedScrolls, 2); - } + VirtualArmor = 50; + PackNecroReg(15, 25); + PackItem(new DaemonBone(30)); + } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + public override string CorpseName => "a dark guardians' corpse"; + public override string DefaultName => "a dark guardian"; - writer.WriteEncodedInt(0); // version - } + public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead; - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override int TreasureMapLevel => 2; + public override bool BleedImmune => true; + public override Poison PoisonImmune => Poison.Lethal; + public override bool Unprovokable => true; - var version = reader.ReadEncodedInt(); - } + public override void GenerateLoot() + { + AddLoot(LootPack.Rich); + AddLoot(LootPack.MedScrolls, 2); } } diff --git a/Projects/UOContent/Mobiles/Special/Harrower.cs b/Projects/UOContent/Mobiles/Special/Harrower.cs index 0f3fa3345a..5b18edbe5a 100644 --- a/Projects/UOContent/Mobiles/Special/Harrower.cs +++ b/Projects/UOContent/Mobiles/Special/Harrower.cs @@ -1,702 +1,688 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Engines.Virtues; using Server.Items; using Server.Spells; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(1, false)] +public partial class Harrower : BaseCreature { - public class Harrower : BaseCreature + private static readonly SpawnEntry[] m_Entries = { - private static readonly SpawnEntry[] m_Entries = - { - new(new Point3D(5242, 945, -40), new Point3D(1176, 2638, 0)), // Destard - new(new Point3D(5225, 798, 0), new Point3D(1176, 2638, 0)), // Destard - new(new Point3D(5556, 886, 30), new Point3D(1298, 1080, 0)), // Despise - new(new Point3D(5187, 615, 0), new Point3D(4111, 432, 5)), // Deceit - new(new Point3D(5319, 583, 0), new Point3D(4111, 432, 5)), // Deceit - new(new Point3D(5713, 1334, -1), new Point3D(2923, 3407, 8)), // Fire - new(new Point3D(5860, 1460, -2), new Point3D(2923, 3407, 8)), // Fire - new(new Point3D(5328, 1620, 0), new Point3D(5451, 3143, -60)), // Terathan Keep - new(new Point3D(5690, 538, 0), new Point3D(2042, 224, 14)), // Wrong - new(new Point3D(5609, 195, 0), new Point3D(514, 1561, 0)), // Shame - new(new Point3D(5475, 187, 0), new Point3D(514, 1561, 0)), // Shame - new(new Point3D(6085, 179, 0), new Point3D(4721, 3822, 0)), // Hythloth - new(new Point3D(6084, 66, 0), new Point3D(4721, 3822, 0)), // Hythloth - new(new Point3D(5499, 2003, 0), new Point3D(2499, 919, 0)), // Covetous - new(new Point3D(5579, 1858, 0), new Point3D(2499, 919, 0)) // Covetous - }; - - private static readonly double[] m_Offsets = - { - Math.Cos(000.0 / 180.0 * Math.PI), Math.Sin(000.0 / 180.0 * Math.PI), - Math.Cos(040.0 / 180.0 * Math.PI), Math.Sin(040.0 / 180.0 * Math.PI), - Math.Cos(080.0 / 180.0 * Math.PI), Math.Sin(080.0 / 180.0 * Math.PI), - Math.Cos(120.0 / 180.0 * Math.PI), Math.Sin(120.0 / 180.0 * Math.PI), - Math.Cos(160.0 / 180.0 * Math.PI), Math.Sin(160.0 / 180.0 * Math.PI), - Math.Cos(200.0 / 180.0 * Math.PI), Math.Sin(200.0 / 180.0 * Math.PI), - Math.Cos(240.0 / 180.0 * Math.PI), Math.Sin(240.0 / 180.0 * Math.PI), - Math.Cos(280.0 / 180.0 * Math.PI), Math.Sin(280.0 / 180.0 * Math.PI), - Math.Cos(320.0 / 180.0 * Math.PI), Math.Sin(320.0 / 180.0 * Math.PI) - }; - - private Dictionary m_DamageEntries; - private Item m_GateItem; - private List m_Tentacles; - private Timer m_Timer; - - private bool m_TrueForm; - - [Constructible] - public Harrower() : base(AIType.AI_Mage, FightMode.Closest, 18) - { - Instances.Add(this); - Body = 146; - - SetStr(900, 1000); - SetDex(125, 135); - SetInt(1000, 1200); + new(new Point3D(5242, 945, -40), new Point3D(1176, 2638, 0)), // Destard + new(new Point3D(5225, 798, 0), new Point3D(1176, 2638, 0)), // Destard + new(new Point3D(5556, 886, 30), new Point3D(1298, 1080, 0)), // Despise + new(new Point3D(5187, 615, 0), new Point3D(4111, 432, 5)), // Deceit + new(new Point3D(5319, 583, 0), new Point3D(4111, 432, 5)), // Deceit + new(new Point3D(5713, 1334, -1), new Point3D(2923, 3407, 8)), // Fire + new(new Point3D(5860, 1460, -2), new Point3D(2923, 3407, 8)), // Fire + new(new Point3D(5328, 1620, 0), new Point3D(5451, 3143, -60)), // Terathan Keep + new(new Point3D(5690, 538, 0), new Point3D(2042, 224, 14)), // Wrong + new(new Point3D(5609, 195, 0), new Point3D(514, 1561, 0)), // Shame + new(new Point3D(5475, 187, 0), new Point3D(514, 1561, 0)), // Shame + new(new Point3D(6085, 179, 0), new Point3D(4721, 3822, 0)), // Hythloth + new(new Point3D(6084, 66, 0), new Point3D(4721, 3822, 0)), // Hythloth + new(new Point3D(5499, 2003, 0), new Point3D(2499, 919, 0)), // Covetous + new(new Point3D(5579, 1858, 0), new Point3D(2499, 919, 0)) // Covetous + }; + + private static readonly double[] m_Offsets = + { + Math.Cos(000.0 / 180.0 * Math.PI), Math.Sin(000.0 / 180.0 * Math.PI), + Math.Cos(040.0 / 180.0 * Math.PI), Math.Sin(040.0 / 180.0 * Math.PI), + Math.Cos(080.0 / 180.0 * Math.PI), Math.Sin(080.0 / 180.0 * Math.PI), + Math.Cos(120.0 / 180.0 * Math.PI), Math.Sin(120.0 / 180.0 * Math.PI), + Math.Cos(160.0 / 180.0 * Math.PI), Math.Sin(160.0 / 180.0 * Math.PI), + Math.Cos(200.0 / 180.0 * Math.PI), Math.Sin(200.0 / 180.0 * Math.PI), + Math.Cos(240.0 / 180.0 * Math.PI), Math.Sin(240.0 / 180.0 * Math.PI), + Math.Cos(280.0 / 180.0 * Math.PI), Math.Sin(280.0 / 180.0 * Math.PI), + Math.Cos(320.0 / 180.0 * Math.PI), Math.Sin(320.0 / 180.0 * Math.PI) + }; + + private Dictionary m_DamageEntries; + private Timer m_Timer; + + [SerializableField(0, setter: "private")] + private bool _trueForm; + + [SerializableField(1, setter: "private")] + private Item _gateItem; + + [SerializableField(2, setter: "private")] + private List _tentacles; + + [Constructible] + public Harrower() : base(AIType.AI_Mage, FightMode.Closest, 18) + { + Instances.Add(this); + Body = 146; - Fame = 22500; - Karma = -22500; + SetStr(900, 1000); + SetDex(125, 135); + SetInt(1000, 1200); - VirtualArmor = 60; + Fame = 22500; + Karma = -22500; - SetDamageType(ResistanceType.Physical, 50); - SetDamageType(ResistanceType.Energy, 50); + VirtualArmor = 60; - SetResistance(ResistanceType.Physical, 55, 65); - SetResistance(ResistanceType.Fire, 60, 80); - SetResistance(ResistanceType.Cold, 60, 80); - SetResistance(ResistanceType.Poison, 60, 80); - SetResistance(ResistanceType.Energy, 60, 80); + SetDamageType(ResistanceType.Physical, 50); + SetDamageType(ResistanceType.Energy, 50); - SetSkill(SkillName.Wrestling, 90.1, 100.0); - SetSkill(SkillName.Tactics, 90.2, 110.0); - SetSkill(SkillName.MagicResist, 120.2, 160.0); - SetSkill(SkillName.Magery, 120.0); - SetSkill(SkillName.EvalInt, 120.0); - SetSkill(SkillName.Meditation, 120.0); + SetResistance(ResistanceType.Physical, 55, 65); + SetResistance(ResistanceType.Fire, 60, 80); + SetResistance(ResistanceType.Cold, 60, 80); + SetResistance(ResistanceType.Poison, 60, 80); + SetResistance(ResistanceType.Energy, 60, 80); - m_Tentacles = new List(); + SetSkill(SkillName.Wrestling, 90.1, 100.0); + SetSkill(SkillName.Tactics, 90.2, 110.0); + SetSkill(SkillName.MagicResist, 120.2, 160.0); + SetSkill(SkillName.Magery, 120.0); + SetSkill(SkillName.EvalInt, 120.0); + SetSkill(SkillName.Meditation, 120.0); - m_Timer = new TeleportTimer(this); - m_Timer.Start(); - } + m_Timer = new TeleportTimer(this); + m_Timer.Start(); + } - public Harrower(Serial serial) : base(serial) - { - Instances.Add(this); - } + public Harrower(Serial serial) : base(serial) + { + Instances.Add(this); + } - public static Type[] UniqueList => new[] { typeof(AcidProofRobe) }; - public static Type[] SharedList => new[] { typeof(TheRobeOfBritanniaAri) }; - public static Type[] DecorativeList => new[] { typeof(EvilIdolSkull), typeof(SkullPole) }; + public static Type[] UniqueList => new[] { typeof(AcidProofRobe) }; + public static Type[] SharedList => new[] { typeof(TheRobeOfBritanniaAri) }; + public static Type[] DecorativeList => new[] { typeof(EvilIdolSkull), typeof(SkullPole) }; - public static List Instances { get; } = new(); + public static List Instances { get; } = new(); - public static bool CanSpawn => Instances.Count == 0; + public static bool CanSpawn => Instances.Count == 0; - public override string DefaultName => "the harrower"; + public override string DefaultName => "the harrower"; - public override bool AutoDispel => true; - public override bool Unprovokable => true; - public override Poison PoisonImmune => Poison.Lethal; + public override bool AutoDispel => true; + public override bool Unprovokable => true; + public override Poison PoisonImmune => Poison.Lethal; - [CommandProperty(AccessLevel.GameMaster)] - public override int HitsMax => m_TrueForm ? 65000 : 30000; + [CommandProperty(AccessLevel.GameMaster)] + public override int HitsMax => _trueForm ? 65000 : 30000; - [CommandProperty(AccessLevel.GameMaster)] - public override int ManaMax => 5000; + [CommandProperty(AccessLevel.GameMaster)] + public override int ManaMax => 5000; - public override bool DisallowAllMoves => m_TrueForm; + public override bool DisallowAllMoves => _trueForm; - public static Harrower Spawn(Point3D platLoc, Map platMap) + public static Harrower Spawn(Point3D platLoc, Map platMap) + { + if (Instances.Count > 0) { - if (Instances.Count > 0) - { - return null; - } + return null; + } - var entry = m_Entries.RandomElement(); + var entry = m_Entries.RandomElement(); - var harrower = new Harrower(); + var harrower = new Harrower(); - harrower.MoveToWorld(entry.m_Location, Map.Felucca); + harrower.MoveToWorld(entry.m_Location, Map.Felucca); - harrower.m_GateItem = new HarrowerGate(harrower, platLoc, platMap, entry.m_Entrance, Map.Felucca); + harrower._gateItem = new HarrowerGate(harrower, platLoc, platMap, entry.m_Entrance, Map.Felucca); - return harrower; - } + return harrower; + } - public override void GenerateLoot() - { - AddLoot(LootPack.SuperBoss, 2); - AddLoot(LootPack.Meager); - } + public override void GenerateLoot() + { + AddLoot(LootPack.SuperBoss, 2); + AddLoot(LootPack.Meager); + } - public void Morph() + public void Morph() + { + if (_trueForm) { - if (m_TrueForm) - { - return; - } + return; + } - m_TrueForm = true; + _trueForm = true; - Name = "the true harrower"; - Body = 780; - Hue = 0x497; + Name = "the true harrower"; + Body = 780; + Hue = 0x497; - Hits = HitsMax; - Stam = StamMax; - Mana = ManaMax; + Hits = HitsMax; + Stam = StamMax; + Mana = ManaMax; - ProcessDelta(); + ProcessDelta(); - Say(1049499); // Behold my true form! + Say(1049499); // Behold my true form! - var map = Map; + var map = Map; - if (map != null) + if (map != null) + { + for (var i = 0; i < m_Offsets.Length; i += 2) { - for (var i = 0; i < m_Offsets.Length; i += 2) - { - var rx = m_Offsets[i]; - var ry = m_Offsets[i + 1]; + var rx = m_Offsets[i]; + var ry = m_Offsets[i + 1]; - var dist = 0; - var ok = false; - int x = 0, y = 0, z = 0; + var dist = 0; + var ok = false; + int x = 0, y = 0, z = 0; - while (!ok && dist < 10) - { - var rdist = 10 + dist; + while (!ok && dist < 10) + { + var rdist = 10 + dist; - x = X + (int)(rx * rdist); - y = Y + (int)(ry * rdist); - z = map.GetAverageZ(x, y); + x = X + (int)(rx * rdist); + y = Y + (int)(ry * rdist); + z = map.GetAverageZ(x, y); - if (!(ok = map.CanFit(x, y, Z, 16, false, false))) - { - ok = map.CanFit(x, y, z, 16, false, false); - } - - if (dist >= 0) - { - dist = -(dist + 1); - } - else - { - dist = -(dist - 1); - } + if (!(ok = map.CanFit(x, y, Z, 16, false, false))) + { + ok = map.CanFit(x, y, z, 16, false, false); } - if (!ok) + if (dist >= 0) + { + dist = -(dist + 1); + } + else { - continue; + dist = -(dist - 1); } + } - var spawn = new HarrowerTentacles(this) { Team = Team }; + if (!ok) + { + continue; + } - spawn.MoveToWorld(new Point3D(x, y, z), map); + var spawn = new HarrowerTentacles(this) { Team = Team }; - m_Tentacles.Add(spawn); - } + spawn.MoveToWorld(new Point3D(x, y, z), map); + + _tentacles ??= new List(); + _tentacles.Add(spawn); } } + } - public override void OnAfterDelete() - { - Instances.Remove(this); + public override void OnAfterDelete() + { + Instances.Remove(this); - base.OnAfterDelete(); - } + base.OnAfterDelete(); + } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + private void Deserialize(IGenericReader reader, int version) + { + _trueForm = reader.ReadBool(); + _gateItem = reader.ReadEntity(); + _tentacles = reader.ReadEntityList(); + } - writer.Write(0); // version + [AfterDeserialization] + private void AfterDeserialization() + { + m_Timer = new TeleportTimer(this); + m_Timer.Start(); + } - writer.Write(m_TrueForm); - writer.Write(m_GateItem); - writer.Write(m_Tentacles); - } + public void GivePowerScrolls() + { + var toGive = new List(); + var rights = GetLootingRights(DamageEntries, HitsMax); - public override void Deserialize(IGenericReader reader) + for (var i = rights.Count - 1; i >= 0; --i) { - base.Deserialize(reader); - - var version = reader.ReadInt(); + var ds = rights[i]; - switch (version) + if (ds.m_HasRight) { - case 0: - { - m_TrueForm = reader.ReadBool(); - m_GateItem = reader.ReadEntity(); - m_Tentacles = reader.ReadEntityList(); - - m_Timer = new TeleportTimer(this); - m_Timer.Start(); - - break; - } + toGive.Add(ds.m_Mobile); } } - public void GivePowerScrolls() + if (toGive.Count == 0) { - var toGive = new List(); - var rights = GetLootingRights(DamageEntries, HitsMax); + return; + } - for (var i = rights.Count - 1; i >= 0; --i) + toGive.Shuffle(); + + for (var i = 0; i < 16; ++i) + { + var level = Utility.RandomDouble() switch { - var ds = rights[i]; + < 0.1 => 25, + < 0.25 => 20, + < 0.45 => 15, + < 0.70 => 10, + _ => 5 + }; - if (ds.m_HasRight) - { - toGive.Add(ds.m_Mobile); - } - } + var m = toGive[i % toGive.Count]; - if (toGive.Count == 0) + m.SendLocalizedMessage(1049524); // You have received a scroll of power! + m.AddToBackpack(new StatCapScroll(225 + level)); + + if (m is not PlayerMobile pm) { - return; + continue; } - toGive.Shuffle(); + var prot = JusticeVirtue.GetProtector(pm); - for (var i = 0; i < 16; ++i) + if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || + !JusticeVirtue.CheckMapRegion(pm, prot)) { - var level = Utility.RandomDouble() switch - { - < 0.1 => 25, - < 0.25 => 20, - < 0.45 => 15, - < 0.70 => 10, - _ => 5 - }; - - var m = toGive[i % toGive.Count]; - - m.SendLocalizedMessage(1049524); // You have received a scroll of power! - m.AddToBackpack(new StatCapScroll(225 + level)); - - if (m is not PlayerMobile pm) - { - continue; - } - - var prot = JusticeVirtue.GetProtector(pm); - - if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || - !JusticeVirtue.CheckMapRegion(pm, prot)) - { - continue; - } + continue; + } - var chance = VirtueSystem.GetLevel(prot, VirtueName.Justice) switch - { - VirtueLevel.Seeker => 60, - VirtueLevel.Follower => 80, - VirtueLevel.Knight => 100, - _ => 0 - }; + var chance = VirtueSystem.GetLevel(prot, VirtueName.Justice) switch + { + VirtueLevel.Seeker => 60, + VirtueLevel.Follower => 80, + VirtueLevel.Knight => 100, + _ => 0 + }; - if (chance > 0 && chance > Utility.Random(100)) - { - prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice! - prot.AddToBackpack(new StatCapScroll(225 + level)); - } + if (chance > 0 && chance > Utility.Random(100)) + { + prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice! + prot.AddToBackpack(new StatCapScroll(225 + level)); } } + } - public override bool OnBeforeDeath() + public override bool OnBeforeDeath() + { + if (_trueForm) { - if (m_TrueForm) + var rights = GetLootingRights(DamageEntries, HitsMax); + + for (var i = rights.Count - 1; i >= 0; --i) { - var rights = GetLootingRights(DamageEntries, HitsMax); + var ds = rights[i]; - for (var i = rights.Count - 1; i >= 0; --i) + if (ds.m_HasRight && ds.m_Mobile is PlayerMobile mobile) { - var ds = rights[i]; - - if (ds.m_HasRight && ds.m_Mobile is PlayerMobile mobile) - { - ChampionTitleSystem.AwardHarrowerTitle(mobile); - } + ChampionTitleSystem.AwardHarrowerTitle(mobile); } + } - if (!NoKillAwards) - { - GivePowerScrolls(); + if (!NoKillAwards) + { + GivePowerScrolls(); - var map = Map; + var map = Map; - if (map != null) + if (map != null) + { + for (var x = -16; x <= 16; ++x) { - for (var x = -16; x <= 16; ++x) + for (var y = -16; y <= 16; ++y) { - for (var y = -16; y <= 16; ++y) - { - var dist = Math.Sqrt(x * x + y * y); + var dist = Math.Sqrt(x * x + y * y); - if (dist <= 16) - { - new GoodiesTimer(map, X + x, Y + y).Start(); - } + if (dist <= 16) + { + new GoodiesTimer(map, X + x, Y + y).Start(); } } } + } - m_DamageEntries = new Dictionary(); - - for (var i = 0; i < m_Tentacles.Count; ++i) - { - Mobile m = m_Tentacles[i]; + m_DamageEntries = new Dictionary(); - if (!m.Deleted) - { - m.Kill(); - } + for (var i = 0; i < _tentacles.Count; ++i) + { + Mobile m = _tentacles[i]; - RegisterDamageTo(m); + if (!m.Deleted) + { + m.Kill(); } - m_Tentacles.Clear(); + RegisterDamageTo(m); + } - RegisterDamageTo(this); - AwardArtifact(GetArtifact()); + _tentacles.Clear(); - m_GateItem?.Delete(); - } + RegisterDamageTo(this); + AwardArtifact(GetArtifact()); - return base.OnBeforeDeath(); + _gateItem?.Delete(); } - Morph(); - return false; + return base.OnBeforeDeath(); } - public virtual void RegisterDamageTo(Mobile m) - { - if (m == null) - { - return; - } + Morph(); + return false; + } - foreach (var de in m.DamageEntries) - { - var damager = de.Damager; + public virtual void RegisterDamageTo(Mobile m) + { + if (m == null) + { + return; + } - var master = damager.GetDamageMaster(m); + foreach (var de in m.DamageEntries) + { + var damager = de.Damager; - if (master != null) - { - damager = master; - } + var master = damager.GetDamageMaster(m); - RegisterDamage(damager, de.DamageGiven); + if (master != null) + { + damager = master; } + + RegisterDamage(damager, de.DamageGiven); } + } - public void RegisterDamage(Mobile from, int amount) + public void RegisterDamage(Mobile from, int amount) + { + if (from?.Player != true) { - if (from?.Player != true) - { - return; - } + return; + } + + m_DamageEntries[from] = amount + (m_DamageEntries.TryGetValue(from, out var value) ? value : 0); - m_DamageEntries[from] = amount + (m_DamageEntries.TryGetValue(from, out var value) ? value : 0); + from.SendMessage($"Total Damage: {m_DamageEntries[from]}"); + } - from.SendMessage($"Total Damage: {m_DamageEntries[from]}"); + public void AwardArtifact(Item artifact) + { + if (artifact == null) + { + return; } - public void AwardArtifact(Item artifact) + var totalDamage = 0; + + var validEntries = new Dictionary(); + + foreach (var kvp in m_DamageEntries) { - if (artifact == null) + if (IsEligible(kvp.Key, artifact)) { - return; + validEntries.Add(kvp.Key, kvp.Value); + totalDamage += kvp.Value; } + } + + var randomDamage = Utility.RandomMinMax(1, totalDamage); - var totalDamage = 0; + totalDamage = 0; - var validEntries = new Dictionary(); + foreach (var kvp in validEntries) + { + totalDamage += kvp.Value; - foreach (var kvp in m_DamageEntries) + if (totalDamage >= randomDamage) { - if (IsEligible(kvp.Key, artifact)) - { - validEntries.Add(kvp.Key, kvp.Value); - totalDamage += kvp.Value; - } + GiveArtifact(kvp.Key, artifact); + return; } + } - var randomDamage = Utility.RandomMinMax(1, totalDamage); - - totalDamage = 0; + artifact.Delete(); + } - foreach (var kvp in validEntries) - { - totalDamage += kvp.Value; + public void GiveArtifact(Mobile to, Item artifact) + { + if (to == null || artifact == null) + { + return; + } - if (totalDamage >= randomDamage) - { - GiveArtifact(kvp.Key, artifact); - return; - } - } + var pack = to.Backpack; + if (pack?.TryDropItem(to, artifact, false) != true) + { artifact.Delete(); } + else + { + // For your valor in combating the fallen beast, a special artifact has been bestowed on you. + to.SendLocalizedMessage(1062317); + } + } - public void GiveArtifact(Mobile to, Item artifact) + public bool IsEligible(Mobile m, Item artifact) => + m.Player && m.Alive && m.InRange(Location, 32) && + m.Backpack?.CheckHold(m, artifact, false) == true; + + public Item GetArtifact() => + Utility.RandomDouble() switch { - if (to == null || artifact == null) - { - return; - } + < 0.05 => CreateArtifact(UniqueList), + < 0.15 => CreateArtifact(SharedList), + < 0.30 => CreateArtifact(DecorativeList), + _ => null + }; - var pack = to.Backpack; + public Item CreateArtifact(Type[] list) => Loot.Construct(list.RandomElement()); - if (pack?.TryDropItem(to, artifact, false) != true) - { - artifact.Delete(); - } - else - { - // For your valor in combating the fallen beast, a special artifact has been bestowed on you. - to.SendLocalizedMessage(1062317); - } - } + private class SpawnEntry + { + public readonly Point3D m_Entrance; + public readonly Point3D m_Location; - public bool IsEligible(Mobile m, Item artifact) => - m.Player && m.Alive && m.InRange(Location, 32) && - m.Backpack?.CheckHold(m, artifact, false) == true; + public SpawnEntry(Point3D loc, Point3D ent) + { + m_Location = loc; + m_Entrance = ent; + } + } - public Item GetArtifact() => - Utility.RandomDouble() switch - { - < 0.05 => CreateArtifact(UniqueList), - < 0.15 => CreateArtifact(SharedList), - < 0.30 => CreateArtifact(DecorativeList), - _ => null - }; + private class TeleportTimer : Timer + { + private static readonly int[] m_Offsets = + { + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1 + }; - public Item CreateArtifact(Type[] list) => Loot.Construct(list.RandomElement()); + private readonly Mobile m_Owner; - private class SpawnEntry + public TeleportTimer(Mobile owner) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) { - public readonly Point3D m_Entrance; - public readonly Point3D m_Location; - public SpawnEntry(Point3D loc, Point3D ent) - { - m_Location = loc; - m_Entrance = ent; - } + m_Owner = owner; } - private class TeleportTimer : Timer + protected override void OnTick() { - private static readonly int[] m_Offsets = + if (m_Owner.Deleted) { - -1, -1, - -1, 0, - -1, 1, - 0, -1, - 0, 1, - 1, -1, - 1, 0, - 1, 1 - }; + Stop(); + return; + } - private readonly Mobile m_Owner; + var map = m_Owner.Map; - public TeleportTimer(Mobile owner) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) + if (map == null) { + return; + } - m_Owner = owner; + if (Utility.RandomDouble() < 0.75) + { + return; } - protected override void OnTick() + var eable = m_Owner.GetMobilesInRange(16); + Mobile toTeleport = null; + foreach (var m in eable) { - if (m_Owner.Deleted) + if (m != m_Owner && m.Player && m_Owner.CanBeHarmful(m) && m_Owner.CanSee(m)) { - Stop(); - return; + toTeleport = m; + break; } + } + eable.Free(); - var map = m_Owner.Map; + if (toTeleport == null) + { + return; + } - if (map == null) - { - return; - } + var offset = Utility.Random(8) * 2; - if (Utility.RandomDouble() < 0.75) - { - return; - } + var to = m_Owner.Location; - var eable = m_Owner.GetMobilesInRange(16); - Mobile toTeleport = null; - foreach (var m in eable) - { - if (m != m_Owner && m.Player && m_Owner.CanBeHarmful(m) && m_Owner.CanSee(m)) - { - toTeleport = m; - break; - } - } - eable.Free(); + for (var i = 0; i < m_Offsets.Length; i += 2) + { + var x = m_Owner.X + m_Offsets[(offset + i) % m_Offsets.Length]; + var y = m_Owner.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length]; - if (toTeleport == null) + if (map.CanSpawnMobile(x, y, m_Owner.Z)) { - return; + to = new Point3D(x, y, m_Owner.Z); + break; } - var offset = Utility.Random(8) * 2; - - var to = m_Owner.Location; + var z = map.GetAverageZ(x, y); - for (var i = 0; i < m_Offsets.Length; i += 2) + if (map.CanSpawnMobile(x, y, z)) { - var x = m_Owner.X + m_Offsets[(offset + i) % m_Offsets.Length]; - var y = m_Owner.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length]; + to = new Point3D(x, y, z); + break; + } + } - if (map.CanSpawnMobile(x, y, m_Owner.Z)) - { - to = new Point3D(x, y, m_Owner.Z); - break; - } + var from = toTeleport.Location; - var z = map.GetAverageZ(x, y); + toTeleport.Location = to; - if (map.CanSpawnMobile(x, y, z)) - { - to = new Point3D(x, y, z); - break; - } - } + SpellHelper.Turn(m_Owner, toTeleport); + SpellHelper.Turn(toTeleport, m_Owner); - var from = toTeleport.Location; + toTeleport.ProcessDelta(); - toTeleport.Location = to; + Effects.SendLocationParticles( + EffectItem.Create(from, toTeleport.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + Effects.SendLocationParticles( + EffectItem.Create(to, toTeleport.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 5023 + ); - SpellHelper.Turn(m_Owner, toTeleport); - SpellHelper.Turn(toTeleport, m_Owner); + toTeleport.PlaySound(0x1FE); - toTeleport.ProcessDelta(); + m_Owner.Combatant = toTeleport; + } + } - Effects.SendLocationParticles( - EffectItem.Create(from, toTeleport.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 2023 - ); - Effects.SendLocationParticles( - EffectItem.Create(to, toTeleport.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 5023 - ); + private class GoodiesTimer : Timer + { + private readonly Map m_Map; + private readonly int m_X; + private readonly int m_Y; - toTeleport.PlaySound(0x1FE); + public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0)) + { - m_Owner.Combatant = toTeleport; - } + m_Map = map; + m_X = x; + m_Y = y; } - private class GoodiesTimer : Timer + protected override void OnTick() { - private readonly Map m_Map; - private readonly int m_X; - private readonly int m_Y; + var z = m_Map.GetAverageZ(m_X, m_Y); + var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false); - public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0)) + for (var i = -3; !canFit && i <= 3; ++i) { + canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false); - m_Map = map; - m_X = x; - m_Y = y; + if (canFit) + { + z += i; + } } - protected override void OnTick() + if (!canFit) { - var z = m_Map.GetAverageZ(m_X, m_Y); - var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false); + return; + } - for (var i = -3; !canFit && i <= 3; ++i) - { - canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false); + var g = new Gold(750, 1250); - if (canFit) - { - z += i; - } - } + g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map); - if (!canFit) - { - return; - } + if (Utility.RandomDouble() < 0.05) + { + return; + } - var g = new Gold(750, 1250); + switch (Utility.Random(3)) + { + case 0: // Fire column + { + Effects.SendLocationParticles( + EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), + 0x3709, + 10, + 30, + 5052 + ); + Effects.PlaySound(g, 0x208); - g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map); + break; + } + case 1: // Explosion + { + Effects.SendLocationParticles( + EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), + 0x36BD, + 20, + 10, + 5044 + ); + Effects.PlaySound(g, 0x307); - if (Utility.RandomDouble() < 0.05) - { - return; - } + break; + } + case 2: // Ball of fire + { + Effects.SendLocationParticles( + EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), + 0x36FE, + 10, + 10, + 5052 + ); - switch (Utility.Random(3)) - { - case 0: // Fire column - { - Effects.SendLocationParticles( - EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), - 0x3709, - 10, - 30, - 5052 - ); - Effects.PlaySound(g, 0x208); - - break; - } - case 1: // Explosion - { - Effects.SendLocationParticles( - EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), - 0x36BD, - 20, - 10, - 5044 - ); - Effects.PlaySound(g, 0x307); - - break; - } - case 2: // Ball of fire - { - Effects.SendLocationParticles( - EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), - 0x36FE, - 10, - 10, - 5052 - ); - - break; - } - } + break; + } } } } diff --git a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs index 1ccebbfa11..8804ec2960 100644 --- a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs +++ b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs @@ -1,185 +1,161 @@ using System; +using ModernUO.Serialization; -namespace Server.Mobiles -{ - public class HarrowerTentacles : BaseCreature - { - private DrainTimer m_Timer; - - [Constructible] - public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee) - { - Harrower = harrower; - Body = 129; - - SetStr(901, 1000); - SetDex(126, 140); - SetInt(1001, 1200); - - SetHits(541, 600); +namespace Server.Mobiles; - SetDamage(13, 20); +[SerializationGenerator(0, false)] +public partial class HarrowerTentacles : BaseCreature +{ + private DrainTimer _timer; - SetDamageType(ResistanceType.Physical, 20); - SetDamageType(ResistanceType.Fire, 20); - SetDamageType(ResistanceType.Cold, 20); - SetDamageType(ResistanceType.Poison, 20); - SetDamageType(ResistanceType.Energy, 20); + [SerializableField(0)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private Mobile _harrower; - SetResistance(ResistanceType.Physical, 55, 65); - SetResistance(ResistanceType.Fire, 35, 45); - SetResistance(ResistanceType.Cold, 35, 45); - SetResistance(ResistanceType.Poison, 35, 45); - SetResistance(ResistanceType.Energy, 35, 45); + [Constructible] + public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee) + { + _harrower = harrower; + Body = 129; - SetSkill(SkillName.Meditation, 100.0); - SetSkill(SkillName.MagicResist, 120.1, 140.0); - SetSkill(SkillName.Swords, 90.1, 100.0); - SetSkill(SkillName.Tactics, 90.1, 100.0); - SetSkill(SkillName.Wrestling, 90.1, 100.0); + SetStr(901, 1000); + SetDex(126, 140); + SetInt(1001, 1200); - Fame = 15000; - Karma = -15000; + SetHits(541, 600); - VirtualArmor = 60; + SetDamage(13, 20); - m_Timer = new DrainTimer(this); - m_Timer.Start(); + SetDamageType(ResistanceType.Physical, 20); + SetDamageType(ResistanceType.Fire, 20); + SetDamageType(ResistanceType.Cold, 20); + SetDamageType(ResistanceType.Poison, 20); + SetDamageType(ResistanceType.Energy, 20); - PackReg(50); - PackNecroReg(15, 75); - } - - public HarrowerTentacles(Serial serial) : base(serial) - { - } + SetResistance(ResistanceType.Physical, 55, 65); + SetResistance(ResistanceType.Fire, 35, 45); + SetResistance(ResistanceType.Cold, 35, 45); + SetResistance(ResistanceType.Poison, 35, 45); + SetResistance(ResistanceType.Energy, 35, 45); - public override string CorpseName => "a tentacles corpse"; + SetSkill(SkillName.Meditation, 100.0); + SetSkill(SkillName.MagicResist, 120.1, 140.0); + SetSkill(SkillName.Swords, 90.1, 100.0); + SetSkill(SkillName.Tactics, 90.1, 100.0); + SetSkill(SkillName.Wrestling, 90.1, 100.0); - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Harrower { get; set; } + Fame = 15000; + Karma = -15000; - public override string DefaultName => "tentacles of the harrower"; + VirtualArmor = 60; - public override bool AutoDispel => true; - public override bool Unprovokable => true; - public override Poison PoisonImmune => Poison.Lethal; - public override bool DisallowAllMoves => true; + _timer = new DrainTimer(this); + _timer.Start(); - public override void CheckReflect(Mobile caster, ref bool reflect) - { - reflect = true; - } + PackReg(50); + PackNecroReg(15, 75); + } - public override int GetIdleSound() => 0x101; + public override string CorpseName => "a tentacles corpse"; - public override int GetAngerSound() => 0x5E; + public override string DefaultName => "tentacles of the harrower"; - public override int GetDeathSound() => 0x1C2; + public override bool AutoDispel => true; + public override bool Unprovokable => true; + public override Poison PoisonImmune => Poison.Lethal; + public override bool DisallowAllMoves => true; - public override int GetAttackSound() => -1; + public override void CheckReflect(Mobile caster, ref bool reflect) + { + reflect = true; + } - public override int GetHurtSound() => 0x289; + public override int GetIdleSound() => 0x101; - public override void GenerateLoot() - { - AddLoot(LootPack.FilthyRich, 2); - AddLoot(LootPack.MedScrolls, 3); - AddLoot(LootPack.HighScrolls, 2); - } + public override int GetAngerSound() => 0x5E; - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + public override int GetDeathSound() => 0x1C2; - writer.Write(0); // version + public override int GetAttackSound() => -1; - writer.Write(Harrower); - } + public override int GetHurtSound() => 0x289; - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override void GenerateLoot() + { + AddLoot(LootPack.FilthyRich, 2); + AddLoot(LootPack.MedScrolls, 3); + AddLoot(LootPack.HighScrolls, 2); + } - var version = reader.ReadInt(); + [AfterDeserialization] + private void AfterDeserialization() + { + _timer = new DrainTimer(this); + _timer.Start(); + } - switch (version) - { - case 0: - { - Harrower = reader.ReadEntity(); + public override void OnAfterDelete() + { + _timer?.Stop(); + _timer = null; - m_Timer = new DrainTimer(this); - m_Timer.Start(); + base.OnAfterDelete(); + } - break; - } - } - } + private class DrainTimer : Timer + { + private readonly HarrowerTentacles m_Owner; - public override void OnAfterDelete() + public DrainTimer(HarrowerTentacles owner) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) { - m_Timer?.Stop(); - m_Timer = null; - - base.OnAfterDelete(); + m_Owner = owner; } - private class DrainTimer : Timer + protected override void OnTick() { - private readonly HarrowerTentacles m_Owner; - - public DrainTimer(HarrowerTentacles owner) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) + if (m_Owner.Deleted) { - m_Owner = owner; + Stop(); + return; } - protected override void OnTick() + var eable = m_Owner.GetMobilesInRange(9); + + foreach (var m in eable) { - if (m_Owner.Deleted) + if (m == m_Owner || !m_Owner.CanBeHarmful(m)) { - Stop(); - return; + continue; } - var eable = m_Owner.GetMobilesInRange(9); - - foreach (var m in eable) + if (m.Player && !m.Alive) { - if (m == m_Owner || !m_Owner.CanBeHarmful(m)) - { - continue; - } - - if (m.Player && !m.Alive) - { - continue; - } - - if (m is BaseCreature bc && !(bc.Controlled || bc.IsAnimatedDead || bc.Summoned || bc.Team != m_Owner.Team)) - { - continue; - } + continue; + } - m_Owner.DoHarmful(m); + if (m is BaseCreature bc && !(bc.Controlled || bc.IsAnimatedDead || bc.Summoned || bc.Team != m_Owner.Team)) + { + continue; + } - m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist); - m.PlaySound(0x1F1); + m_Owner.DoHarmful(m); - var drain = Utility.RandomMinMax(14, 30); + m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist); + m.PlaySound(0x1F1); - m_Owner.Hits += drain; + var drain = Utility.RandomMinMax(14, 30); - if (m_Owner.Harrower != null) - { - m_Owner.Harrower.Hits += drain; - } + m_Owner.Hits += drain; - m.Damage(drain, m_Owner); + if (m_Owner.Harrower != null) + { + m_Owner.Harrower.Hits += drain; } - eable.Free(); + m.Damage(drain, m_Owner); } + + eable.Free(); } } } diff --git a/Projects/UOContent/Mobiles/Special/LordOaks.cs b/Projects/UOContent/Mobiles/Special/LordOaks.cs index 3a42db6a0c..0387d6353d 100644 --- a/Projects/UOContent/Mobiles/Special/LordOaks.cs +++ b/Projects/UOContent/Mobiles/Special/LordOaks.cs @@ -1,13 +1,18 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Items; namespace Server.Mobiles; -public class LordOaks : BaseChampion +[SerializationGenerator(0, false)] +public partial class LordOaks : BaseChampion { - private BaseCreature m_Queen; - private bool m_SpawnedQueen; + [SerializableField(0)] + private BaseCreature _queen; + + [SerializableField(1)] + private bool _spawnedQueen; [Constructible] public LordOaks() : base(AIType.AI_Mage, FightMode.Evil) @@ -45,10 +50,6 @@ public LordOaks() : base(AIType.AI_Mage, FightMode.Evil) VirtualArmor = 100; } - public LordOaks(Serial serial) : base(serial) - { - } - public override ChampionSkullType SkullType => ChampionSkullType.Enlightenment; public override Type[] UniqueList => new[] { typeof(OrcChieftainHelm) }; @@ -109,20 +110,20 @@ public bool CheckQueen() return false; } - if (!m_SpawnedQueen) + if (!_spawnedQueen) { Say(1042153); // Come forth my queen! - m_Queen = new Silvani { Team = Team }; - m_Queen.MoveToWorld(Location, Map); + _queen = new Silvani { Team = Team }; + _queen.MoveToWorld(Location, Map); - m_SpawnedQueen = true; + _spawnedQueen = true; return true; } - if (m_Queen?.Deleted != false) + if (_queen?.Deleted != false) { - m_Queen = null; + _queen = null; return false; } @@ -154,32 +155,4 @@ public override void OnGotMeleeAttack(Mobile attacker, int damage) attacker.Stam -= Utility.Random(20, 10); attacker.Mana -= Utility.Random(20, 10); } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Queen); - writer.Write(m_SpawnedQueen); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - m_Queen = reader.ReadEntity(); - m_SpawnedQueen = reader.ReadBool(); - - break; - } - } - } } diff --git a/Projects/UOContent/Mobiles/Special/Mephitis.cs b/Projects/UOContent/Mobiles/Special/Mephitis.cs index 84575b1ab1..3efff28b78 100644 --- a/Projects/UOContent/Mobiles/Special/Mephitis.cs +++ b/Projects/UOContent/Mobiles/Special/Mephitis.cs @@ -1,92 +1,75 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class Mephitis : BaseChampion { - public class Mephitis : BaseChampion + [Constructible] + public Mephitis() : base(AIType.AI_Melee) { - [Constructible] - public Mephitis() : base(AIType.AI_Melee) - { - Body = 173; - BaseSoundID = 0x183; - - SetStr(505, 1000); - SetDex(102, 300); - SetInt(402, 600); - - SetHits(3000); - SetStam(105, 600); - - SetDamage(21, 33); - - SetDamageType(ResistanceType.Physical, 50); - SetDamageType(ResistanceType.Poison, 50); - - SetResistance(ResistanceType.Physical, 75, 80); - SetResistance(ResistanceType.Fire, 60, 70); - SetResistance(ResistanceType.Cold, 60, 70); - SetResistance(ResistanceType.Poison, 100); - SetResistance(ResistanceType.Energy, 60, 70); + Body = 173; + BaseSoundID = 0x183; - SetSkill(SkillName.MagicResist, 70.7, 140.0); - SetSkill(SkillName.Tactics, 97.6, 100.0); - SetSkill(SkillName.Wrestling, 97.6, 100.0); + SetStr(505, 1000); + SetDex(102, 300); + SetInt(402, 600); - Fame = 22500; - Karma = -22500; + SetHits(3000); + SetStam(105, 600); - VirtualArmor = 80; - } + SetDamage(21, 33); - public Mephitis(Serial serial) : base(serial) - { - } + SetDamageType(ResistanceType.Physical, 50); + SetDamageType(ResistanceType.Poison, 50); - public override ChampionSkullType SkullType => ChampionSkullType.Venom; + SetResistance(ResistanceType.Physical, 75, 80); + SetResistance(ResistanceType.Fire, 60, 70); + SetResistance(ResistanceType.Cold, 60, 70); + SetResistance(ResistanceType.Poison, 100); + SetResistance(ResistanceType.Energy, 60, 70); - public override Type[] UniqueList => new[] { typeof(Calm) }; + SetSkill(SkillName.MagicResist, 70.7, 140.0); + SetSkill(SkillName.Tactics, 97.6, 100.0); + SetSkill(SkillName.Wrestling, 97.6, 100.0); - public override Type[] SharedList => new[] - { - typeof(OblivionsNeedle), typeof(ANecromancerShroud), typeof(EmbroideredOakLeafCloak), - typeof(TheMostKnowledgePerson) - }; + Fame = 22500; + Karma = -22500; - public override Type[] DecorativeList => new[] { typeof(Web), typeof(MonsterStatuette) }; + VirtualArmor = 80; + } - public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Spider }; + public override ChampionSkullType SkullType => ChampionSkullType.Venom; - public override string DefaultName => "Mephitis"; + public override Type[] UniqueList => new[] { typeof(Calm) }; - public override Poison PoisonImmune => Poison.Lethal; - public override Poison HitPoison => Poison.Lethal; + public override Type[] SharedList => new[] + { + typeof(OblivionsNeedle), typeof(ANecromancerShroud), typeof(EmbroideredOakLeafCloak), + typeof(TheMostKnowledgePerson) + }; - public override void GenerateLoot() - { - AddLoot(LootPack.UltraRich, 4); - } + public override Type[] DecorativeList => new[] { typeof(Web), typeof(MonsterStatuette) }; - public override void OnGotMeleeAttack(Mobile attacker, int damage) - { - base.OnGotMeleeAttack(attacker, damage); + public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Spider }; - // TODO: Web ability - } + public override string DefaultName => "Mephitis"; - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + public override Poison PoisonImmune => Poison.Lethal; + public override Poison HitPoison => Poison.Lethal; - writer.Write(0); // version - } + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 4); + } - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override void OnGotMeleeAttack(Mobile attacker, int damage) + { + base.OnGotMeleeAttack(attacker, damage); - var version = reader.ReadInt(); - } + // TODO: Web ability } } diff --git a/Projects/UOContent/Mobiles/Special/Neira.cs b/Projects/UOContent/Mobiles/Special/Neira.cs index e3404ea9bd..cc8fa055c4 100644 --- a/Projects/UOContent/Mobiles/Special/Neira.cs +++ b/Projects/UOContent/Mobiles/Special/Neira.cs @@ -1,303 +1,283 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Items; -namespace Server.Mobiles -{ - public class Neira : BaseChampion - { - private const double SpeedBoostScalar = 1.2; +namespace Server.Mobiles; - private bool m_SpeedBoost; +[SerializationGenerator(1, false)] +public partial class Neira : BaseChampion +{ + private const double SpeedBoostScalar = 1.2; - [Constructible] - public Neira() : base(AIType.AI_Mage) - { - Title = "the necromancer"; - Body = 401; - Hue = 0x83EC; + private bool _speedBoost; - SetStr(305, 425); - SetDex(72, 150); - SetInt(505, 750); + [Constructible] + public Neira() : base(AIType.AI_Mage) + { + Title = "the necromancer"; + Body = 401; + Hue = 0x83EC; - SetHits(4800); - SetStam(102, 300); + SetStr(305, 425); + SetDex(72, 150); + SetInt(505, 750); - SetDamage(25, 35); + SetHits(4800); + SetStam(102, 300); - SetDamageType(ResistanceType.Physical, 100); + SetDamage(25, 35); - SetResistance(ResistanceType.Physical, 25, 30); - SetResistance(ResistanceType.Fire, 35, 45); - SetResistance(ResistanceType.Cold, 50, 60); - SetResistance(ResistanceType.Poison, 30, 40); - SetResistance(ResistanceType.Energy, 20, 30); + SetDamageType(ResistanceType.Physical, 100); - SetSkill(SkillName.EvalInt, 120.0); - SetSkill(SkillName.Magery, 120.0); - SetSkill(SkillName.Meditation, 120.0); - SetSkill(SkillName.MagicResist, 150.0); - SetSkill(SkillName.Tactics, 97.6, 100.0); - SetSkill(SkillName.Wrestling, 97.6, 100.0); + SetResistance(ResistanceType.Physical, 25, 30); + SetResistance(ResistanceType.Fire, 35, 45); + SetResistance(ResistanceType.Cold, 50, 60); + SetResistance(ResistanceType.Poison, 30, 40); + SetResistance(ResistanceType.Energy, 20, 30); - Fame = 22500; - Karma = -22500; + SetSkill(SkillName.EvalInt, 120.0); + SetSkill(SkillName.Magery, 120.0); + SetSkill(SkillName.Meditation, 120.0); + SetSkill(SkillName.MagicResist, 150.0); + SetSkill(SkillName.Tactics, 97.6, 100.0); + SetSkill(SkillName.Wrestling, 97.6, 100.0); - VirtualArmor = 30; - Female = true; + Fame = 22500; + Karma = -22500; - Item shroud = new HoodedShroudOfShadows(); + VirtualArmor = 30; + Female = true; - shroud.Movable = false; + Item shroud = new HoodedShroudOfShadows(); + shroud.Movable = false; - AddItem(shroud); + AddItem(shroud); - var weapon = new Scimitar(); + var weapon = new Scimitar + { + Skill = SkillName.Wrestling, + Hue = 38, + Movable = false + }; - weapon.Skill = SkillName.Wrestling; - weapon.Hue = 38; - weapon.Movable = false; + AddItem(weapon); - AddItem(weapon); + // new SkeletalMount().Rider = this; + AddItem(new VirtualMountItem(this)); + } - // new SkeletalMount().Rider = this; - AddItem(new VirtualMountItem(this)); - } + public override ChampionSkullType SkullType => ChampionSkullType.Death; - public Neira(Serial serial) : base(serial) - { - } + public override Type[] UniqueList => new[] { typeof(ShroudOfDeciet) }; - public override ChampionSkullType SkullType => ChampionSkullType.Death; + public override Type[] SharedList => new[] + { + typeof(ANecromancerShroud), - public override Type[] UniqueList => new[] { typeof(ShroudOfDeciet) }; + typeof(CaptainJohnsHat) + }; - public override Type[] SharedList => new[] - { - typeof(ANecromancerShroud), + public override Type[] DecorativeList => new[] { typeof(WallBlood), typeof(TatteredAncientMummyWrapping) }; - typeof(CaptainJohnsHat) - }; + public override MonsterStatuetteType[] StatueTypes => Array.Empty(); - public override Type[] DecorativeList => new[] { typeof(WallBlood), typeof(TatteredAncientMummyWrapping) }; + public override string DefaultName => "Neira"; - public override MonsterStatuetteType[] StatueTypes => Array.Empty(); + public override bool AlwaysMurderer => true; + public override bool BardImmune => !Core.SE; + public override bool Unprovokable => Core.SE; + public override bool Uncalmable => Core.SE; + public override Poison PoisonImmune => Poison.Deadly; - public override string DefaultName => "Neira"; + public override bool ShowFameTitle => false; + public override bool ClickTitle => false; - public override bool AlwaysMurderer => true; - public override bool BardImmune => !Core.SE; - public override bool Unprovokable => Core.SE; - public override bool Uncalmable => Core.SE; - public override Poison PoisonImmune => Poison.Deadly; + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 3); + AddLoot(LootPack.Meager); + } - public override bool ShowFameTitle => false; - public override bool ClickTitle => false; + public override bool OnBeforeDeath() + { + var mount = Mount; - public override void GenerateLoot() + if (mount != null) { - AddLoot(LootPack.UltraRich, 3); - AddLoot(LootPack.Meager); + mount.Rider = null; } - public override bool OnBeforeDeath() - { - var mount = Mount; - - if (mount != null) - { - mount.Rider = null; - } + (mount as Item)?.Delete(); - (mount as Mobile)?.Delete(); - - return base.OnBeforeDeath(); - } + return base.OnBeforeDeath(); + } - public override void OnDamage(int amount, Mobile from, bool willKill) - { - CheckSpeedBoost(); - base.OnDamage(amount, from, willKill); - } + public override void OnDamage(int amount, Mobile from, bool willKill) + { + CheckSpeedBoost(); + base.OnDamage(amount, from, willKill); + } - private void CheckSpeedBoost() + private void CheckSpeedBoost() + { + if (Hits < HitsMax / 4) { - if (Hits < HitsMax / 4) - { - if (!m_SpeedBoost) - { - ActiveSpeed /= SpeedBoostScalar; - PassiveSpeed /= SpeedBoostScalar; - m_SpeedBoost = true; - } - } - else if (m_SpeedBoost) + if (!_speedBoost) { - ActiveSpeed *= SpeedBoostScalar; - PassiveSpeed *= SpeedBoostScalar; - m_SpeedBoost = false; + ActiveSpeed /= SpeedBoostScalar; + PassiveSpeed /= SpeedBoostScalar; + _speedBoost = true; } } - - public override void OnGaveMeleeAttack(Mobile defender, int damage) + else if (_speedBoost) { - base.OnGaveMeleeAttack(defender, damage); + ActiveSpeed *= SpeedBoostScalar; + PassiveSpeed *= SpeedBoostScalar; + _speedBoost = false; + } + } - if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone - { - AddUnholyBone(defender, 0.25); - } + public override void OnGaveMeleeAttack(Mobile defender, int damage) + { + base.OnGaveMeleeAttack(defender, damage); - CheckSpeedBoost(); + if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone + { + AddUnholyBone(defender, 0.25); } - public override void OnGotMeleeAttack(Mobile attacker, int damage) - { - base.OnGotMeleeAttack(attacker, damage); + CheckSpeedBoost(); + } - if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone - { - AddUnholyBone(attacker, 0.25); - } - } + public override void OnGotMeleeAttack(Mobile attacker, int damage) + { + base.OnGotMeleeAttack(attacker, damage); - public override void AlterDamageScalarFrom(Mobile caster, ref double scalar) + if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone { - base.AlterDamageScalarFrom(caster, ref scalar); - - if (Utility.RandomDouble() < 0.1) // 10% chance to throw an unholy bone - { - AddUnholyBone(caster, 1.0); - } + AddUnholyBone(attacker, 0.25); } + } - public void AddUnholyBone(Mobile target, double chanceToThrow) - { - if (Map == null) - { - return; - } + public override void AlterDamageScalarFrom(Mobile caster, ref double scalar) + { + base.AlterDamageScalarFrom(caster, ref scalar); - if (chanceToThrow >= Utility.RandomDouble()) - { - Direction = GetDirectionTo(target); - MovingEffect(target, 0xF7E, 10, 1, true, false, 0x496, 0); - new DelayTimer(this, target).Start(); - } - else - { - new UnholyBone().MoveToWorld(Location, Map); - } + if (Utility.RandomDouble() < 0.1) // 10% chance to throw an unholy bone + { + AddUnholyBone(caster, 1.0); } + } - public override void Serialize(IGenericWriter writer) + public void AddUnholyBone(Mobile target, double chanceToThrow) + { + if (Map == null) { - base.Serialize(writer); - - writer.Write(1); // version - writer.Write(m_SpeedBoost); + return; } - public override void Deserialize(IGenericReader reader) + if (chanceToThrow >= Utility.RandomDouble()) { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 1: - { - m_SpeedBoost = reader.ReadBool(); - break; - } - } + Direction = GetDirectionTo(target); + MovingEffect(target, 0xF7E, 10, 1, true, false, 0x496, 0); + new DelayTimer(this, target).Start(); } - - private class VirtualMount : IMount + else { - private readonly VirtualMountItem m_Item; + new UnholyBone().MoveToWorld(Location, Map); + } + } - public VirtualMount(VirtualMountItem item) => m_Item = item; + private void Deserialize(IGenericReader reader, int version) + { + // We don't need to serialize this + reader.ReadBool(); // _speedBoost + } - Mobile IMount.Rider - { - get => m_Item.Rider; - set { } - } + [AfterDeserialization] + private void AfterDeserialization() + { + CheckSpeedBoost(); + } - public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill) - { - } + private class VirtualMount : IMount + { + private readonly VirtualMountItem _item; + + public VirtualMount(VirtualMountItem item) => _item = item; + + Mobile IMount.Rider + { + get => _item.Rider; + set { } } - private class VirtualMountItem : Item, IMountItem + public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill) { - private readonly VirtualMount m_Mount; + } + } - public VirtualMountItem(Mobile mob) - : base(0x3EBB) - { - Layer = Layer.Mount; + [SerializationGenerator(0)] + private partial class VirtualMountItem : Item, IMountItem + { + private VirtualMount _mount; - Movable = false; + [SerializableField(0, setter: "private")] + private Mobile _rider; - Rider = mob; - m_Mount = new VirtualMount(this); - } + public VirtualMountItem(Mobile mob) : base(0x3EBB) + { + Layer = Layer.Mount; - public VirtualMountItem(Serial serial) - : base(serial) => - m_Mount = new VirtualMount(this); + Movable = false; - public Mobile Rider { get; private set; } + _rider = mob; + _mount = new VirtualMount(this); + } - public IMount Mount => m_Mount; + public IMount Mount => _mount; - public override void Serialize(IGenericWriter writer) + [AfterDeserialization(false)] + private void AfterDeserialization() + { + if (_rider?.Deleted != false) { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(Rider); + Delete(); } - - public override void Deserialize(IGenericReader reader) + else { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - Rider = reader.ReadEntity(); - - if (Rider == null) - { - Delete(); - } + _mount = new VirtualMount(this); } } - private class DelayTimer : Timer + public override DeathMoveResult OnParentDeath(Mobile parent) { - private readonly Mobile m_Mobile; - private readonly Mobile m_Target; + _mount = null; + Delete(); - public DelayTimer(Mobile m, Mobile target) : base(TimeSpan.FromSeconds(1.0)) - { - m_Mobile = m; - m_Target = target; - } + return DeathMoveResult.RemainEquipped; + } + } - protected override void OnTick() + private class DelayTimer : Timer + { + private readonly Mobile m_Mobile; + private readonly Mobile m_Target; + + public DelayTimer(Mobile m, Mobile target) : base(TimeSpan.FromSeconds(1.0)) + { + m_Mobile = m; + m_Target = target; + } + + protected override void OnTick() + { + if (m_Mobile.CanBeHarmful(m_Target)) { - if (m_Mobile.CanBeHarmful(m_Target)) - { - m_Mobile.DoHarmful(m_Target); - AOS.Damage(m_Target, m_Mobile, Utility.RandomMinMax(10, 20), 100, 0, 0, 0, 0); - new UnholyBone().MoveToWorld(m_Target.Location, m_Target.Map); - } + m_Mobile.DoHarmful(m_Target); + AOS.Damage(m_Target, m_Mobile, Utility.RandomMinMax(10, 20), 100, 0, 0, 0, 0); + new UnholyBone().MoveToWorld(m_Target.Location, m_Target.Map); } } } diff --git a/Projects/UOContent/Mobiles/Special/OrderGuard.cs b/Projects/UOContent/Mobiles/Special/OrderGuard.cs index 41cbcc788c..92f747b953 100644 --- a/Projects/UOContent/Mobiles/Special/OrderGuard.cs +++ b/Projects/UOContent/Mobiles/Special/OrderGuard.cs @@ -1,38 +1,21 @@ +using ModernUO.Serialization; using Server.Guilds; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class OrderGuard : BaseShieldGuard { - public class OrderGuard : BaseShieldGuard + [Constructible] + public OrderGuard() { - [Constructible] - public OrderGuard() - { - } - - public OrderGuard(Serial serial) : base(serial) - { - } - - public override int Keyword => 0x21; // *order shield* - public override BaseShield Shield => new OrderShield(); - public override int SignupNumber => 1007141; // Sign up with a guild of order if thou art interested. - public override GuildType Type => GuildType.Order; - - public override bool BardImmune => true; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } + } - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override int Keyword => 0x21; // *order shield* + public override BaseShield Shield => new OrderShield(); + public override int SignupNumber => 1007141; // Sign up with a guild of order if thou art interested. + public override GuildType Type => GuildType.Order; - var version = reader.ReadInt(); - } - } + public override bool BardImmune => true; } diff --git a/Projects/UOContent/Mobiles/Special/Paragon.cs b/Projects/UOContent/Mobiles/Special/Paragon.cs index be9c805e3f..67fd224366 100644 --- a/Projects/UOContent/Mobiles/Special/Paragon.cs +++ b/Projects/UOContent/Mobiles/Special/Paragon.cs @@ -2,255 +2,249 @@ using Server.Items; using Server.Utilities; -namespace Server.Mobiles +namespace Server.Mobiles; + +public static class Paragon { - public static class Paragon + public const double ChestChance = 0.10; // Chance that a paragon will carry a paragon chest + public const double ChocolateIngredientChance = 0.20; // Chance that a paragon will drop a chocolatiering ingredient + + public static Map[] Maps = { - public const double ChestChance = 0.10; // Chance that a paragon will carry a paragon chest - public const double ChocolateIngredientChance = 0.20; // Chance that a paragon will drop a chocolatiering ingredient + Map.Ilshenar + }; + + private static readonly TimeSpan FastRegenRate = TimeSpan.FromSeconds(0.5); + private static readonly TimeSpan CPUSaverRate = TimeSpan.FromSeconds(2); - public static Map[] Maps = + public static Type[] Artifacts = + { + typeof(GoldBricks), typeof(PhillipsWoodenSteed), + typeof(AlchemistsBauble), typeof(ArcticDeathDealer), + typeof(BlazeOfDeath), typeof(BowOfTheJukaKing), + typeof(BurglarsBandana), typeof(CavortingClub), + typeof(EnchantedTitanLegBone), typeof(GwennosHarp), + typeof(IolosLute), typeof(LunaLance), + typeof(NightsKiss), typeof(NoxRangersHeavyCrossbow), + typeof(OrcishVisage), typeof(PolarBearMask), + typeof(ShieldOfInvulnerability), typeof(StaffOfPower), + typeof(VioletCourage), typeof(HeartOfTheLion), + typeof(WrathOfTheDryad), typeof(PixieSwatter), + typeof(GlovesOfThePugilist) + }; + + public static int Hue = 0x501; // Paragon hue + + // Buffs + public const double HitsBuff = 5.0; + public const double StrBuff = 1.05; + public const double IntBuff = 1.20; + public const double DexBuff = 1.20; + public const double SkillsBuff = 1.20; + public const double SpeedBuff = 1.20; + public const double FameBuff = 1.40; + public const double KarmaBuff = 1.40; + public const int DamageBuff = 5; + + public static void Convert(BaseCreature bc) + { + if (bc.IsParagon) { - Map.Ilshenar - }; + return; + } - private static readonly TimeSpan FastRegenRate = TimeSpan.FromSeconds(0.5); - private static readonly TimeSpan CPUSaverRate = TimeSpan.FromSeconds(2); + bc.Hue = Hue; - public static Type[] Artifacts = + if (bc.HitsMaxSeed >= 0) { - typeof(GoldBricks), typeof(PhillipsWoodenSteed), - typeof(AlchemistsBauble), typeof(ArcticDeathDealer), - typeof(BlazeOfDeath), typeof(BowOfTheJukaKing), - typeof(BurglarsBandana), typeof(CavortingClub), - typeof(EnchantedTitanLegBone), typeof(GwennosHarp), - typeof(IolosLute), typeof(LunaLance), - typeof(NightsKiss), typeof(NoxRangersHeavyCrossbow), - typeof(OrcishVisage), typeof(PolarBearMask), - typeof(ShieldOfInvulnerability), typeof(StaffOfPower), - typeof(VioletCourage), typeof(HeartOfTheLion), - typeof(WrathOfTheDryad), typeof(PixieSwatter), - typeof(GlovesOfThePugilist) - }; - - public static int Hue = 0x501; // Paragon hue - - // Buffs - public const double HitsBuff = 5.0; - public const double StrBuff = 1.05; - public const double IntBuff = 1.20; - public const double DexBuff = 1.20; - public const double SkillsBuff = 1.20; - public const double SpeedBuff = 1.20; - public const double FameBuff = 1.40; - public const double KarmaBuff = 1.40; - public const int DamageBuff = 5; - - public static void Convert(BaseCreature bc) - { - if (bc.IsParagon) - { - return; - } - - bc.Hue = Hue; + bc.HitsMaxSeed = (int)(bc.HitsMaxSeed * HitsBuff); + } - if (bc.HitsMaxSeed >= 0) - { - bc.HitsMaxSeed = (int)(bc.HitsMaxSeed * HitsBuff); - } + bc.RawStr = (int)(bc.RawStr * StrBuff); + bc.RawInt = (int)(bc.RawInt * IntBuff); + bc.RawDex = (int)(bc.RawDex * DexBuff); - bc.RawStr = (int)(bc.RawStr * StrBuff); - bc.RawInt = (int)(bc.RawInt * IntBuff); - bc.RawDex = (int)(bc.RawDex * DexBuff); + bc.Hits = bc.HitsMax; + bc.Mana = bc.ManaMax; + bc.Stam = bc.StamMax; - bc.Hits = bc.HitsMax; - bc.Mana = bc.ManaMax; - bc.Stam = bc.StamMax; + for (var i = 0; i < bc.Skills.Length; i++) + { + var skill = bc.Skills[i]; - for (var i = 0; i < bc.Skills.Length; i++) + if (skill.Base > 0.0) { - var skill = bc.Skills[i]; - - if (skill.Base > 0.0) - { - skill.Base *= SkillsBuff; - } + skill.Base *= SkillsBuff; } + } - bc.PassiveSpeed /= SpeedBuff; - bc.ActiveSpeed /= SpeedBuff; - bc.CurrentSpeed = bc.PassiveSpeed; + bc.PassiveSpeed /= SpeedBuff; + bc.ActiveSpeed /= SpeedBuff; + bc.CurrentSpeed = bc.PassiveSpeed; - bc.DamageMin += DamageBuff; - bc.DamageMax += DamageBuff; + bc.DamageMin += DamageBuff; + bc.DamageMax += DamageBuff; - if (bc.Fame > 0) - { - bc.Fame = (int)(bc.Fame * FameBuff); - } + if (bc.Fame > 0) + { + bc.Fame = (int)(bc.Fame * FameBuff); + } - if (bc.Fame > 32000) - { - bc.Fame = 32000; - } + if (bc.Fame > 32000) + { + bc.Fame = 32000; + } - // TODO: Mana regeneration rate = Sqrt( buffedFame ) / 4 + // TODO: Mana regeneration rate = Sqrt( buffedFame ) / 4 - if (bc.Karma != 0) - { - bc.Karma = (int)(bc.Karma * KarmaBuff); + if (bc.Karma != 0) + { + bc.Karma = (int)(bc.Karma * KarmaBuff); - if (bc.Karma.Abs() > 32000) - { - bc.Karma = 32000 * Math.Sign(bc.Karma); - } + if (bc.Karma.Abs() > 32000) + { + bc.Karma = 32000 * Math.Sign(bc.Karma); } - - new ParagonStamRegen(bc).Start(); } - public static void UnConvert(BaseCreature bc) + new ParagonStamRegen(bc).Start(); + } + + public static void UnConvert(BaseCreature bc) + { + if (!bc.IsParagon) { - if (!bc.IsParagon) - { - return; - } + return; + } - bc.Hue = 0; + bc.Hue = 0; - if (bc.HitsMaxSeed >= 0) - { - bc.HitsMaxSeed = (int)(bc.HitsMaxSeed / HitsBuff); - } + if (bc.HitsMaxSeed >= 0) + { + bc.HitsMaxSeed = (int)(bc.HitsMaxSeed / HitsBuff); + } - bc.RawStr = (int)(bc.RawStr / StrBuff); - bc.RawInt = (int)(bc.RawInt / IntBuff); - bc.RawDex = (int)(bc.RawDex / DexBuff); + bc.RawStr = (int)(bc.RawStr / StrBuff); + bc.RawInt = (int)(bc.RawInt / IntBuff); + bc.RawDex = (int)(bc.RawDex / DexBuff); - bc.Hits = bc.HitsMax; - bc.Mana = bc.ManaMax; - bc.Stam = bc.StamMax; + bc.Hits = bc.HitsMax; + bc.Mana = bc.ManaMax; + bc.Stam = bc.StamMax; - for (var i = 0; i < bc.Skills.Length; i++) - { - var skill = bc.Skills[i]; + for (var i = 0; i < bc.Skills.Length; i++) + { + var skill = bc.Skills[i]; - if (skill.Base > 0.0) - { - skill.Base /= SkillsBuff; - } + if (skill.Base > 0.0) + { + skill.Base /= SkillsBuff; } + } - bc.PassiveSpeed *= SpeedBuff; - bc.ActiveSpeed *= SpeedBuff; - bc.CurrentSpeed = bc.PassiveSpeed; + bc.PassiveSpeed *= SpeedBuff; + bc.ActiveSpeed *= SpeedBuff; + bc.CurrentSpeed = bc.PassiveSpeed; - bc.DamageMin -= DamageBuff; - bc.DamageMax -= DamageBuff; + bc.DamageMin -= DamageBuff; + bc.DamageMax -= DamageBuff; - if (bc.Fame > 0) - { - bc.Fame = (int)(bc.Fame / FameBuff); - } + if (bc.Fame > 0) + { + bc.Fame = (int)(bc.Fame / FameBuff); + } - if (bc.Karma != 0) - { - bc.Karma = (int)(bc.Karma / KarmaBuff); - } + if (bc.Karma != 0) + { + bc.Karma = (int)(bc.Karma / KarmaBuff); } + } - public static bool CheckConvert(BaseCreature bc) => CheckConvert(bc, bc.Location, bc.Map); + public static bool CheckConvert(BaseCreature bc) => CheckConvert(bc, bc.Location, bc.Map); - public static bool CheckConvert(BaseCreature bc, Point3D location, Map m) + public static bool CheckConvert(BaseCreature bc, Point3D location, Map m) + { + if (!Core.AOS) { - if (!Core.AOS) - { - return false; - } + return false; + } - if (Array.IndexOf(Maps, m) == -1) - { - return false; - } + if (Array.IndexOf(Maps, m) == -1) + { + return false; + } - if (bc is BaseChampion or Harrower or BaseVendor or BaseEscortable or Clone || bc.IsParagon) - { - return false; - } + if (bc is BaseChampion or Harrower or BaseVendor or BaseEscortable or Clone || bc.IsParagon) + { + return false; + } - var fame = bc.Fame; + var fame = bc.Fame; - if (fame > 32000) - { - fame = 32000; - } + if (fame > 32000) + { + fame = 32000; + } + + var chance = 1 / Math.Round(20.0 - fame / 3200.0); - var chance = 1 / Math.Round(20.0 - fame / 3200.0); + return chance > Utility.RandomDouble(); + } - return chance > Utility.RandomDouble(); + public static bool CheckArtifactChance(Mobile m, BaseCreature bc) + { + if (!Core.AOS) + { + return false; } - public static bool CheckArtifactChance(Mobile m, BaseCreature bc) + double fame = bc.Fame; + + if (fame > 32000) { - if (!Core.AOS) - { - return false; - } + fame = 32000; + } - double fame = bc.Fame; + var chance = + 1 / (Math.Max(10, 100 * (0.83 - Math.Round(Math.Log(Math.Round(fame / 6000, 3) + 0.001, 10), 3))) * + (100 - Math.Sqrt(m.Luck)) / 100.0); - if (fame > 32000) - { - fame = 32000; - } + return chance > Utility.RandomDouble(); + } - var chance = - 1 / (Math.Max(10, 100 * (0.83 - Math.Round(Math.Log(Math.Round(fame / 6000, 3) + 0.001, 10), 3))) * - (100 - Math.Sqrt(m.Luck)) / 100.0); + public static void GiveArtifactTo(Mobile m) + { + var item = Artifacts.RandomElement().CreateInstance(); - return chance > Utility.RandomDouble(); + if (m.AddToBackpack(item)) + { + m.SendMessage("As a reward for slaying the mighty paragon, an artifact has been placed in your backpack."); } - - public static void GiveArtifactTo(Mobile m) + else { - var item = Artifacts.RandomElement().CreateInstance(); - - if (m.AddToBackpack(item)) - { - m.SendMessage("As a reward for slaying the mighty paragon, an artifact has been placed in your backpack."); - } - else - { - m.SendMessage( - "As your backpack is full, your reward for destroying the legendary paragon has been placed at your feet." - ); - } + m.SendMessage( + "As your backpack is full, your reward for destroying the legendary paragon has been placed at your feet." + ); } + } - private class ParagonStamRegen : Timer - { - private readonly BaseCreature m_Owner; + private class ParagonStamRegen : Timer + { + private readonly BaseCreature m_Owner; + + public ParagonStamRegen(Mobile m) : base(FastRegenRate, FastRegenRate) => m_Owner = m as BaseCreature; - public ParagonStamRegen(Mobile m) - : base(FastRegenRate, FastRegenRate) + protected override void OnTick() + { + if (!m_Owner.Deleted && m_Owner.IsParagon && m_Owner.Map != Map.Internal) { + m_Owner.Stam++; - m_Owner = m as BaseCreature; + Delay = Interval = m_Owner.Stam < m_Owner.StamMax * .75 ? FastRegenRate : CPUSaverRate; } - - protected override void OnTick() + else { - if (!m_Owner.Deleted && m_Owner.IsParagon && m_Owner.Map != Map.Internal) - { - m_Owner.Stam++; - - Delay = Interval = m_Owner.Stam < m_Owner.StamMax * .75 ? FastRegenRate : CPUSaverRate; - } - else - { - Stop(); - } + Stop(); } } } diff --git a/Projects/UOContent/Mobiles/Special/Rikktor.cs b/Projects/UOContent/Mobiles/Special/Rikktor.cs index 540cd2536f..e2616e789f 100644 --- a/Projects/UOContent/Mobiles/Special/Rikktor.cs +++ b/Projects/UOContent/Mobiles/Special/Rikktor.cs @@ -1,165 +1,148 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class Rikktor : BaseChampion { - public class Rikktor : BaseChampion + [Constructible] + public Rikktor() : base(AIType.AI_Melee) { - [Constructible] - public Rikktor() : base(AIType.AI_Melee) - { - Body = 172; + Body = 172; - SetStr(701, 900); - SetDex(201, 350); - SetInt(51, 100); + SetStr(701, 900); + SetDex(201, 350); + SetInt(51, 100); - SetHits(3000); - SetStam(203, 650); + SetHits(3000); + SetStam(203, 650); - SetDamage(28, 55); + SetDamage(28, 55); - SetDamageType(ResistanceType.Physical, 25); - SetDamageType(ResistanceType.Fire, 50); - SetDamageType(ResistanceType.Energy, 25); + SetDamageType(ResistanceType.Physical, 25); + SetDamageType(ResistanceType.Fire, 50); + SetDamageType(ResistanceType.Energy, 25); - SetResistance(ResistanceType.Physical, 80, 90); - SetResistance(ResistanceType.Fire, 80, 90); - SetResistance(ResistanceType.Cold, 30, 40); - SetResistance(ResistanceType.Poison, 80, 90); - SetResistance(ResistanceType.Energy, 80, 90); + SetResistance(ResistanceType.Physical, 80, 90); + SetResistance(ResistanceType.Fire, 80, 90); + SetResistance(ResistanceType.Cold, 30, 40); + SetResistance(ResistanceType.Poison, 80, 90); + SetResistance(ResistanceType.Energy, 80, 90); - SetSkill(SkillName.Anatomy, 100.0); - SetSkill(SkillName.MagicResist, 140.2, 160.0); - SetSkill(SkillName.Tactics, 100.0); + SetSkill(SkillName.Anatomy, 100.0); + SetSkill(SkillName.MagicResist, 140.2, 160.0); + SetSkill(SkillName.Tactics, 100.0); - Fame = 22500; - Karma = -22500; + Fame = 22500; + Karma = -22500; - VirtualArmor = 130; - } + VirtualArmor = 130; + } - public Rikktor(Serial serial) : base(serial) - { - } + public override ChampionSkullType SkullType => ChampionSkullType.Power; - public override ChampionSkullType SkullType => ChampionSkullType.Power; + public override Type[] UniqueList => new[] { typeof(CrownOfTalKeesh) }; - public override Type[] UniqueList => new[] { typeof(CrownOfTalKeesh) }; + public override Type[] SharedList => new[] + { + typeof(TheMostKnowledgePerson), + typeof(BraveKnightOfTheBritannia), + typeof(LieutenantOfTheBritannianRoyalGuard) + }; - public override Type[] SharedList => new[] - { - typeof(TheMostKnowledgePerson), - typeof(BraveKnightOfTheBritannia), - typeof(LieutenantOfTheBritannianRoyalGuard) - }; + public override Type[] DecorativeList => new[] + { + typeof(LavaTile), + typeof(MonsterStatuette), + typeof(MonsterStatuette) + }; - public override Type[] DecorativeList => new[] - { - typeof(LavaTile), - typeof(MonsterStatuette), - typeof(MonsterStatuette) - }; + public override MonsterStatuetteType[] StatueTypes => new[] + { + MonsterStatuetteType.OphidianArchMage, + MonsterStatuetteType.OphidianWarrior + }; - public override MonsterStatuetteType[] StatueTypes => new[] - { - MonsterStatuetteType.OphidianArchMage, - MonsterStatuetteType.OphidianWarrior - }; + public override string DefaultName => "Rikktor"; + + public override Poison PoisonImmune => Poison.Lethal; + public override ScaleType ScaleType => ScaleType.All; + public override int Scales => 20; - public override string DefaultName => "Rikktor"; + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 4); + } - public override Poison PoisonImmune => Poison.Lethal; - public override ScaleType ScaleType => ScaleType.All; - public override int Scales => 20; + public override void OnGaveMeleeAttack(Mobile defender, int damage) + { + base.OnGaveMeleeAttack(defender, damage); - public override void GenerateLoot() + if (Utility.RandomDouble() < 0.2) { - AddLoot(LootPack.UltraRich, 4); + Earthquake(); } + } - public override void OnGaveMeleeAttack(Mobile defender, int damage) - { - base.OnGaveMeleeAttack(defender, damage); + public void Earthquake() + { + var map = Map; - if (Utility.RandomDouble() < 0.2) - { - Earthquake(); - } + if (map == null) + { + return; } - public void Earthquake() - { - var map = Map; + PlaySound(0x2F3); - if (map == null) + var eable = GetMobilesInRange(8); + + foreach (var m in eable) + { + if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive)) { - return; + continue; } - PlaySound(0x2F3); + if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team)) + { + continue; + } - var eable = GetMobilesInRange(8); + var damage = m.Hits * 0.6; - foreach (var m in eable) + if (damage < 10.0) { - if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive)) - { - continue; - } - - if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team)) - { - continue; - } - - var damage = m.Hits * 0.6; - - if (damage < 10.0) - { - damage = 10.0; - } - else if (damage > 75.0) - { - damage = 75.0; - } - - DoHarmful(m); - - AOS.Damage(m, this, (int)damage, 100, 0, 0, 0, 0); - - if (m.Alive && m.Body.IsHuman && !m.Mounted) - { - m.Animate(20, 7, 1, true, false, 0); // take hit - } + damage = 10.0; + } + else if (damage > 75.0) + { + damage = 75.0; } - eable.Free(); - } - - public override int GetAngerSound() => Utility.Random(0x2CE, 2); + DoHarmful(m); - public override int GetIdleSound() => 0x2D2; + AOS.Damage(m, this, (int)damage, 100, 0, 0, 0, 0); - public override int GetAttackSound() => Utility.Random(0x2C7, 5); + if (m.Alive && m.Body.IsHuman && !m.Mounted) + { + m.Animate(20, 7, 1, true, false, 0); // take hit + } + } - public override int GetHurtSound() => 0x2D1; + eable.Free(); + } - public override int GetDeathSound() => 0x2CC; + public override int GetAngerSound() => Utility.Random(0x2CE, 2); - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + public override int GetIdleSound() => 0x2D2; - writer.Write(0); // version - } + public override int GetAttackSound() => Utility.Random(0x2C7, 5); - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override int GetHurtSound() => 0x2D1; - var version = reader.ReadInt(); - } - } + public override int GetDeathSound() => 0x2CC; } diff --git a/Projects/UOContent/Mobiles/Special/Semidar.cs b/Projects/UOContent/Mobiles/Special/Semidar.cs index dae2cab39b..0aa232ebcc 100644 --- a/Projects/UOContent/Mobiles/Special/Semidar.cs +++ b/Projects/UOContent/Mobiles/Special/Semidar.cs @@ -1,10 +1,12 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Items; namespace Server.Mobiles; -public class Semidar : BaseChampion +[SerializationGenerator(0, false)] +public partial class Semidar : BaseChampion { [Constructible] public Semidar() : base(AIType.AI_Mage) @@ -43,10 +45,6 @@ public Semidar() : base(AIType.AI_Mage) VirtualArmor = 20; } - public Semidar(Serial serial) : base(serial) - { - } - public override ChampionSkullType SkullType => ChampionSkullType.Pain; public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) }; @@ -88,18 +86,6 @@ public override void AlterDamageScalarFrom(Mobile caster, ref double scalar) 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; diff --git a/Projects/UOContent/Mobiles/Special/Serado.cs b/Projects/UOContent/Mobiles/Special/Serado.cs index b399a3d5da..4153ad55e0 100644 --- a/Projects/UOContent/Mobiles/Special/Serado.cs +++ b/Projects/UOContent/Mobiles/Special/Serado.cs @@ -1,194 +1,177 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Engines.Plants; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class Serado : BaseChampion { - public class Serado : BaseChampion + [Constructible] + public Serado() : base(AIType.AI_Melee) { - [Constructible] - public Serado() : base(AIType.AI_Melee) - { - Title = "the awakened"; + Title = "the awakened"; - Body = 249; - Hue = 0x96C; + Body = 249; + Hue = 0x96C; - SetStr(1000); - SetDex(150); - SetInt(300); + SetStr(1000); + SetDex(150); + SetInt(300); - SetHits(9000); - SetMana(300); + SetHits(9000); + SetMana(300); - SetDamage(29, 35); + SetDamage(29, 35); - SetDamageType(ResistanceType.Physical, 70); - SetDamageType(ResistanceType.Poison, 20); - SetDamageType(ResistanceType.Energy, 10); + SetDamageType(ResistanceType.Physical, 70); + SetDamageType(ResistanceType.Poison, 20); + SetDamageType(ResistanceType.Energy, 10); - SetResistance(ResistanceType.Physical, 30); - SetResistance(ResistanceType.Fire, 60); - SetResistance(ResistanceType.Cold, 60); - SetResistance(ResistanceType.Poison, 90); - SetResistance(ResistanceType.Energy, 50); + SetResistance(ResistanceType.Physical, 30); + SetResistance(ResistanceType.Fire, 60); + SetResistance(ResistanceType.Cold, 60); + SetResistance(ResistanceType.Poison, 90); + SetResistance(ResistanceType.Energy, 50); - SetSkill(SkillName.MagicResist, 120.0); - SetSkill(SkillName.Tactics, 120.0); - SetSkill(SkillName.Wrestling, 70.0); - SetSkill(SkillName.Poisoning, 150.0); + SetSkill(SkillName.MagicResist, 120.0); + SetSkill(SkillName.Tactics, 120.0); + SetSkill(SkillName.Wrestling, 70.0); + SetSkill(SkillName.Poisoning, 150.0); - Fame = 22500; - Karma = -22500; + Fame = 22500; + Karma = -22500; - PackItem(Seed.RandomBonsaiSeed()); - } + PackItem(Seed.RandomBonsaiSeed()); + } - public Serado(Serial serial) : base(serial) - { - } + public override ChampionSkullType SkullType => ChampionSkullType.Power; - public override ChampionSkullType SkullType => ChampionSkullType.Power; + public override Type[] UniqueList => new[] { typeof(Pacify) }; - public override Type[] UniqueList => new[] { typeof(Pacify) }; + public override Type[] SharedList => new[] + { + typeof(BraveKnightOfTheBritannia), + typeof(DetectiveBoots), + typeof(EmbroideredOakLeafCloak), + typeof(LieutenantOfTheBritannianRoyalGuard) + }; - public override Type[] SharedList => new[] - { - typeof(BraveKnightOfTheBritannia), - typeof(DetectiveBoots), - typeof(EmbroideredOakLeafCloak), - typeof(LieutenantOfTheBritannianRoyalGuard) - }; + public override Type[] DecorativeList => new[] { typeof(Futon), typeof(SwampTile) }; - public override Type[] DecorativeList => new[] { typeof(Futon), typeof(SwampTile) }; + public override MonsterStatuetteType[] StatueTypes => Array.Empty(); - public override MonsterStatuetteType[] StatueTypes => Array.Empty(); + public override string DefaultName => "Serado"; - public override string DefaultName => "Serado"; + public override int TreasureMapLevel => 5; - public override int TreasureMapLevel => 5; + public override Poison HitPoison => Poison.Lethal; + public override Poison PoisonImmune => Poison.Lethal; + public override double HitPoisonChance => 0.8; - public override Poison HitPoison => Poison.Lethal; - public override Poison PoisonImmune => Poison.Lethal; - public override double HitPoisonChance => 0.8; + public override int Feathers => 30; - public override int Feathers => 30; + public override bool ShowFameTitle => false; + public override bool ClickTitle => false; - public override bool ShowFameTitle => false; - public override bool ClickTitle => false; + public override WeaponAbility GetWeaponAbility() => WeaponAbility.DoubleStrike; - public override WeaponAbility GetWeaponAbility() => WeaponAbility.DoubleStrike; + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 4); + AddLoot(LootPack.FilthyRich); + AddLoot(LootPack.Gems, 6); + } - public override void GenerateLoot() - { - AddLoot(LootPack.UltraRich, 4); - AddLoot(LootPack.FilthyRich); - AddLoot(LootPack.Gems, 6); - } + // TODO: Hit Lightning Area - // TODO: Hit Lightning Area + public override void OnDamagedBySpell(Mobile attacker, int damage) + { + base.OnDamagedBySpell(attacker, damage); - public override void OnDamagedBySpell(Mobile attacker, int damage) - { - base.OnDamagedBySpell(attacker, damage); + ScaleResistances(); + DoCounter(attacker); + } - ScaleResistances(); - DoCounter(attacker); - } + public override void OnGotMeleeAttack(Mobile attacker, int damage) + { + base.OnGotMeleeAttack(attacker, damage); - public override void OnGotMeleeAttack(Mobile attacker, int damage) - { - base.OnGotMeleeAttack(attacker, damage); + ScaleResistances(); + DoCounter(attacker); + } - ScaleResistances(); - DoCounter(attacker); - } + private void ScaleResistances() + { + var hitsLost = (HitsMax - Hits) / (double)HitsMax; - private void ScaleResistances() + SetResistance(ResistanceType.Physical, 30 + (int)(hitsLost * (95 - 30))); + SetResistance(ResistanceType.Fire, 60 + (int)(hitsLost * (95 - 60))); + SetResistance(ResistanceType.Cold, 60 + (int)(hitsLost * (95 - 60))); + SetResistance(ResistanceType.Poison, 90 + (int)(hitsLost * (95 - 90))); + SetResistance(ResistanceType.Energy, 50 + (int)(hitsLost * (95 - 50))); + } + + private void DoCounter(Mobile attacker) + { + if (Map == null) { - var hitsLost = (HitsMax - Hits) / (double)HitsMax; + return; + } - SetResistance(ResistanceType.Physical, 30 + (int)(hitsLost * (95 - 30))); - SetResistance(ResistanceType.Fire, 60 + (int)(hitsLost * (95 - 60))); - SetResistance(ResistanceType.Cold, 60 + (int)(hitsLost * (95 - 60))); - SetResistance(ResistanceType.Poison, 90 + (int)(hitsLost * (95 - 90))); - SetResistance(ResistanceType.Energy, 50 + (int)(hitsLost * (95 - 50))); + if (!(Utility.RandomDouble() < 0.2)) + { + return; } - private void DoCounter(Mobile attacker) + Mobile target = null; + + if (attacker is BaseCreature bcAttacker) { - if (Map == null) + if (bcAttacker.BardProvoked) { return; } - if (!(Utility.RandomDouble() < 0.2)) - { - return; - } + target = bcAttacker.GetMaster(); + } - Mobile target = null; + /* Counterattack with Hit Poison Area + * 20-25 damage, unresistable + * Lethal poison, 100% of the time + * Particle effect: Type: "2" From: "0x4061A107" To: "0x0" ItemId: "0x36BD" ItemIdName: "explosion" FromLocation: "(296 615, 17)" ToLocation: "(296 615, 17)" Speed: "1" Duration: "10" FixedDirection: "True" Explode: "False" Hue: "0xA6" RenderMode: "0x0" Effect: "0x1F78" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x4061A107" Layer: "255" Unknown: "0x0" + * Doesn't work on provoked monsters + */ - if (attacker is BaseCreature bcAttacker) - { - if (bcAttacker.BardProvoked) - { - return; - } + if (target?.InRange(this, 25) != true) + { + target = attacker; + } - target = bcAttacker.GetMaster(); - } + Animate(10, 4, 1, true, false, 0); - /* Counterattack with Hit Poison Area - * 20-25 damage, unresistable - * Lethal poison, 100% of the time - * Particle effect: Type: "2" From: "0x4061A107" To: "0x0" ItemId: "0x36BD" ItemIdName: "explosion" FromLocation: "(296 615, 17)" ToLocation: "(296 615, 17)" Speed: "1" Duration: "10" FixedDirection: "True" Explode: "False" Hue: "0xA6" RenderMode: "0x0" Effect: "0x1F78" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x4061A107" Layer: "255" Unknown: "0x0" - * Doesn't work on provoked monsters - */ + var eable = target.GetMobilesInRange(8); - if (target?.InRange(this, 25) != true) + foreach (var m in eable) + { + if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive)) { - target = attacker; + continue; } - Animate(10, 4, 1, true, false, 0); - - var eable = target.GetMobilesInRange(8); - - foreach (var m in eable) + if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team)) { - 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); - - AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0); - - m.FixedParticles(0x36BD, 1, 10, 0x1F78, 0xA6, 0, (EffectLayer)255); - m.ApplyPoison(this, Poison.Lethal); + continue; } - } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + DoHarmful(m); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0); - var version = reader.ReadInt(); + m.FixedParticles(0x36BD, 1, 10, 0x1F78, 0xA6, 0, (EffectLayer)255); + m.ApplyPoison(this, Poison.Lethal); } } } diff --git a/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs b/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs index cf9836831e..98ea5eb564 100644 --- a/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs +++ b/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs @@ -1,41 +1,25 @@ -namespace Server.Mobiles -{ - public class ServantOfSemidar : BaseCreature - { - [Constructible] - public ServantOfSemidar() : base(AIType.AI_Melee, FightMode.None) => Body = 0x26; - - public ServantOfSemidar(Serial serial) : base(serial) - { - } - - public override string DefaultName => "a Servant of Semidar"; +using ModernUO.Serialization; - public override bool DisallowAllMoves => true; +namespace Server.Mobiles; - public override bool InitialInnocent => true; - - public override bool CanBeDamaged() => false; +[SerializationGenerator(0)] +public partial class ServantOfSemidar : BaseCreature +{ + [Constructible] + public ServantOfSemidar() : base(AIType.AI_Melee, FightMode.None) => Body = 0x26; - public override void AddNameProperties(IPropertyList list) - { - base.AddNameProperties(list); + public override string DefaultName => "a Servant of Semidar"; - list.Add(1005494); // enslaved - } + public override bool DisallowAllMoves => true; - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + public override bool InitialInnocent => true; - writer.WriteEncodedInt(0); // version - } + public override bool CanBeDamaged() => false; - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + public override void AddNameProperties(IPropertyList list) + { + base.AddNameProperties(list); - var version = reader.ReadEncodedInt(); - } + list.Add(1005494); // enslaved } } diff --git a/Projects/UOContent/Mobiles/Special/Silvani.cs b/Projects/UOContent/Mobiles/Special/Silvani.cs index ecc61d5238..9d8f04cefe 100644 --- a/Projects/UOContent/Mobiles/Special/Silvani.cs +++ b/Projects/UOContent/Mobiles/Special/Silvani.cs @@ -1,6 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Mobiles; -public class Silvani : BaseCreature +[SerializationGenerator(0, false)] +public partial class Silvani : BaseCreature { [Constructible] public Silvani() : base(AIType.AI_Mage, FightMode.Evil, 18) @@ -38,10 +41,6 @@ public Silvani() : base(AIType.AI_Mage, FightMode.Evil, 18) VirtualArmor = 50; } - public Silvani(Serial serial) : base(serial) - { - } - public override string DefaultName => "Silvani"; public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead; @@ -67,18 +66,4 @@ public override void OnGaveMeleeAttack(Mobile defender, int damage) defender.Stam -= Utility.Random(20, 10); defender.Mana -= Utility.Random(20, 10); } - - 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(); - } }