diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index 54c564b4d..30a2c41b0 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs @@ -65,7 +65,7 @@ public partial class HouseRaffleStone : Item public static readonly TimeSpan ExpirationTime = TimeSpan.FromDays(30.0); // Time a person has to use the stone to place the house private static Timer _allStonesTimer; - private static HashSet _allStones = null; + private static HashSet _allStones; private HouseRaffleRegion _region; @@ -227,9 +227,7 @@ public partial class HouseRaffleStone : Item private static void RemoveRaffleStone(HouseRaffleStone stone) { - _allStones.Remove(stone); - - if (_allStones.Count == 0) + if (_allStones?.Remove(stone) == true && _allStones.Count == 0) { _allStonesTimer.Stop(); _allStonesTimer = null; diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Meraktus.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.Meraktus.v1.json new file mode 100644 index 000000000..36fa86a12 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Meraktus.v1.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "type": "Server.Mobiles.Meraktus", + "properties": [ + { + "name": "Followers", + "type": "Server.Mobile[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs index 96d596570..fcaa513be 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs @@ -3,225 +3,254 @@ using Server.Engines.CannedEvil; using Server.Items; using ModernUO.Serialization; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(1, false)] +public partial class Meraktus : BaseChampion { - [SerializationGenerator(0, false)] - public partial class Meraktus : BaseChampion + [SerializableField(0)] + private Mobile[] _followers; + + [Constructible] + public Meraktus() : base(AIType.AI_Melee) { - [Constructible] - public Meraktus() - : base(AIType.AI_Melee) + Title = "the Tormented"; + Body = 263; + BaseSoundID = 680; + Hue = 0x835; + + SetStr(1419, 1438); + SetDex(309, 413); + SetInt(129, 131); + + SetHits(4100, 4200); + + SetDamage(16, 30); + + SetDamageType(ResistanceType.Physical, 100); + + SetResistance(ResistanceType.Physical, 65, 90); + SetResistance(ResistanceType.Fire, 65, 70); + SetResistance(ResistanceType.Cold, 50, 60); + SetResistance(ResistanceType.Poison, 40, 60); + SetResistance(ResistanceType.Energy, 50, 55); + + // SetSkill( SkillName.Meditation, Unknown ); + // SetSkill( SkillName.EvalInt, Unknown ); + // SetSkill( SkillName.Magery, Unknown ); + // SetSkill( SkillName.Poisoning, Unknown ); + SetSkill(SkillName.Anatomy, 0); + SetSkill(SkillName.MagicResist, 107.0, 111.3); + SetSkill(SkillName.Tactics, 107.0, 117.0); + SetSkill(SkillName.Wrestling, 100.0, 105.0); + + Fame = 70000; + Karma = -70000; + + VirtualArmor = 28; // Don't know what it should be + + if (Core.ML) { - Title = "the Tormented"; - Body = 263; - BaseSoundID = 680; - Hue = 0x835; - - SetStr(1419, 1438); - SetDex(309, 413); - SetInt(129, 131); - - SetHits(4100, 4200); - - SetDamage(16, 30); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 65, 90); - SetResistance(ResistanceType.Fire, 65, 70); - SetResistance(ResistanceType.Cold, 50, 60); - SetResistance(ResistanceType.Poison, 40, 60); - SetResistance(ResistanceType.Energy, 50, 55); - - // SetSkill( SkillName.Meditation, Unknown ); - // SetSkill( SkillName.EvalInt, Unknown ); - // SetSkill( SkillName.Magery, Unknown ); - // SetSkill( SkillName.Poisoning, Unknown ); - SetSkill(SkillName.Anatomy, 0); - SetSkill(SkillName.MagicResist, 107.0, 111.3); - SetSkill(SkillName.Tactics, 107.0, 117.0); - SetSkill(SkillName.Wrestling, 100.0, 105.0); - - Fame = 70000; - Karma = -70000; - - VirtualArmor = 28; // Don't know what it should be - - if (Core.ML) - { - PackResources(8); - PackTalismans(5); - } - - Timer.StartTimer(TimeSpan.FromSeconds(1), SpawnTormented); - } - - public override string CorpseName => "the remains of Meraktus"; - public override ChampionSkullType SkullType => ChampionSkullType.Pain; - - public override Type[] UniqueList => new[] { typeof(Subdue) }; - public override Type[] SharedList => Array.Empty(); - - public override Type[] DecorativeList => new[] - { - typeof(ArtifactLargeVase), - typeof(ArtifactVase), - typeof(MinotaurStatueDeed) - }; - - public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Minotaur }; - - public override string DefaultName => "Meraktus"; - - public override int Meat => 2; - public override int Hides => 10; - public override HideType HideType => HideType.Regular; - public override Poison PoisonImmune => Poison.Regular; - public override int TreasureMapLevel => 3; - public override bool BardImmune => true; - public override bool Unprovokable => true; - public override bool Uncalmable => true; - - public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount; - - public virtual void PackResources(int amount) - { - for (var i = 0; i < amount; i++) - { - PackItem( - Utility.Random(6) switch - { - 0 => new Blight(), - 1 => new Scourge(), - 2 => new Taint(), - 3 => new Putrefication(), - 4 => new Corruption(), - _ => new Muculent() // 5 - } - ); - } - } - - public virtual void PackTalismans(int amount) - { - var count = Utility.Random(amount); - - for (var i = 0; i < count; i++) - { - PackItem(new RandomTalisman()); - } - } - - public override void OnDeath(Container c) - { - base.OnDeath(c); - - if (!Core.ML) - { - return; - } - - c.DropItem(new MalletAndChisel()); - - c.DropItem( - Utility.Random(3) switch - { - 0 => new MinotaurHedge(), - 1 => new LightYarn(), - _ => new BonePile() // 2 - } - ); - - if (Utility.RandomBool()) - { - c.DropItem(new TormentedChains()); - } - - if (Utility.RandomDouble() < 0.025) - { - c.DropItem(new CrimsonCincture()); - } - } - - public override void GenerateLoot() - { - if (Core.ML) - { - AddLoot(LootPack.AosSuperBoss, 5); // Need to verify - } - } - - public override int GetAngerSound() => 0x597; - - public override int GetIdleSound() => 0x596; - - public override int GetAttackSound() => 0x599; - - public override int GetHurtSound() => 0x59a; - - public override int GetDeathSound() => 0x59c; - - public override void OnGaveMeleeAttack(Mobile defender, int damage) - { - base.OnGaveMeleeAttack(defender, damage); - - if (Utility.RandomDouble() < 0.2) - { - Earthquake(); - } - } - - public void Earthquake() - { - var eable = GetMobilesInRange(8); - - foreach (var m in eable) - { - if (m == this || !CanBeHarmful(m) || m.Deleted || !m.Player && - !(m is BaseCreature creature && (creature.Controlled || creature.Summoned || creature.Team != Team))) - { - continue; - } - - if (m is PlayerMobile pm && pm.Mounted) - { - pm.Mount.Rider = null; - } - - var damage = (int)(m.Hits * 0.6); - if (damage < 10) - { - damage = 10; - } - else if (damage > 75) - { - damage = 75; - } - - DoHarmful(m); - AOS.Damage(m, this, damage, 100, 0, 0, 0, 0); - if (m.Alive && m.Body.IsHuman && !m.Mounted) - { - m.Animate(20, 7, 1, true, false, 0); // take hit - } - } - - eable.Free(); - } - - public void SpawnTormented() - { - BaseCreature spawna = new TormentedMinotaur(); - spawna.MoveToWorld(Location, Map); - - BaseCreature spawnb = new TormentedMinotaur(); - spawnb.MoveToWorld(Location, Map); - - BaseCreature spawnc = new TormentedMinotaur(); - spawnc.MoveToWorld(Location, Map); - - BaseCreature spawnd = new TormentedMinotaur(); - spawnd.MoveToWorld(Location, Map); + PackResources(8); + PackTalismans(5); } } + + protected override void OnMapChange(Map oldMap) + { + if (_followers == null) + { + if (Map != null && Map != Map.Internal && (oldMap == null || oldMap == Map.Internal)) + { + _followers = new Mobile[4]; + + for (var i = 0; i < _followers.Length; i++) + { + var follower = _followers[i] = new TormentedMinotaur(); + follower.MoveToWorld(Location, Map); + } + } + + return; + } + + for (var i = 0; i < _followers.Length; i++) + { + var follower = _followers[i]; + if (follower?.Deleted == false) + { + follower.MoveToWorld(Location, Map); + } + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if (_followers != null) + { + for (var i = 0; i < _followers.Length; i++) + { + _followers[i]?.Delete(); + } + } + } + + public override string CorpseName => "the remains of Meraktus"; + public override ChampionSkullType SkullType => ChampionSkullType.Pain; + + public override Type[] UniqueList => new[] { typeof(Subdue) }; + public override Type[] SharedList => Array.Empty(); + + public override Type[] DecorativeList => new[] + { + typeof(ArtifactLargeVase), + typeof(ArtifactVase), + typeof(MinotaurStatueDeed) + }; + + public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Minotaur }; + + public override string DefaultName => "Meraktus"; + + public override int Meat => 2; + public override int Hides => 10; + public override HideType HideType => HideType.Regular; + public override Poison PoisonImmune => Poison.Regular; + public override int TreasureMapLevel => 3; + public override bool BardImmune => true; + public override bool Unprovokable => true; + public override bool Uncalmable => true; + + public override WeaponAbility GetWeaponAbility() => WeaponAbility.Dismount; + + public virtual void PackResources(int amount) + { + for (var i = 0; i < amount; i++) + { + PackItem( + Utility.Random(6) switch + { + 0 => new Blight(), + 1 => new Scourge(), + 2 => new Taint(), + 3 => new Putrefication(), + 4 => new Corruption(), + _ => new Muculent() // 5 + } + ); + } + } + + public virtual void PackTalismans(int amount) + { + var count = Utility.Random(amount); + + for (var i = 0; i < count; i++) + { + PackItem(new RandomTalisman()); + } + } + + public override void OnDeath(Container c) + { + base.OnDeath(c); + + if (!Core.ML) + { + return; + } + + c.DropItem(new MalletAndChisel()); + + c.DropItem( + Utility.Random(3) switch + { + 0 => new MinotaurHedge(), + 1 => new LightYarn(), + _ => new BonePile() // 2 + } + ); + + if (Utility.RandomBool()) + { + c.DropItem(new TormentedChains()); + } + + if (Utility.RandomDouble() < 0.025) + { + c.DropItem(new CrimsonCincture()); + } + } + + public override void GenerateLoot() + { + if (Core.ML) + { + AddLoot(LootPack.AosSuperBoss, 5); // Need to verify + } + } + + public override int GetAngerSound() => 0x597; + + public override int GetIdleSound() => 0x596; + + public override int GetAttackSound() => 0x599; + + public override int GetHurtSound() => 0x59a; + + public override int GetDeathSound() => 0x59c; + + public override void OnGaveMeleeAttack(Mobile defender, int damage) + { + base.OnGaveMeleeAttack(defender, damage); + + if (Utility.RandomDouble() < 0.2) + { + Earthquake(); + } + } + + public void Earthquake() + { + var eable = GetMobilesInRange(8); + + foreach (var m in eable) + { + if (m == this || !CanBeHarmful(m) || m.Deleted || !m.Player && + !(m is BaseCreature creature && (creature.Controlled || creature.Summoned || creature.Team != Team))) + { + continue; + } + + if (m is PlayerMobile pm && pm.Mounted) + { + pm.Mount.Rider = null; + } + + var damage = (int)(m.Hits * 0.6); + if (damage < 10) + { + damage = 10; + } + else if (damage > 75) + { + damage = 75; + } + + DoHarmful(m); + AOS.Damage(m, this, damage, 100, 0, 0, 0, 0); + if (m.Alive && m.Body.IsHuman && !m.Mounted) + { + m.Animate(20, 7, 1, true, false, 0); // take hit + } + } + + eable.Free(); + } + + private void MigrateFrom(V0Content content) + { + } }