From daa154d6de3669a31a2a90e739aaa412eca6a567 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 25 Aug 2023 20:59:11 -0700 Subject: [PATCH] fix: Codegens guards (#1474) --- .../Server.Mobiles.ArcherGuard.v0.json | 11 + .../Server.Mobiles.BaseGuard.v0.json | 4 + .../Server.Mobiles.WarriorGuard.v0.json | 11 + .../UOContent/Mobiles/Guards/ArcherGuard.cs | 713 +++++++++--------- .../UOContent/Mobiles/Guards/BaseGuard.cs | 135 ++-- .../UOContent/Mobiles/Guards/WarriorGuard.cs | 670 ++++++++-------- 6 files changed, 752 insertions(+), 792 deletions(-) create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.ArcherGuard.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.BaseGuard.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.WarriorGuard.v0.json diff --git a/Projects/UOContent/Migrations/Server.Mobiles.ArcherGuard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.ArcherGuard.v0.json new file mode 100644 index 000000000..1afc1a5e8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.ArcherGuard.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Mobiles.ArcherGuard", + "properties": [ + { + "name": "Focus", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseGuard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseGuard.v0.json new file mode 100644 index 000000000..8aba12a10 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.BaseGuard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.BaseGuard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.WarriorGuard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.WarriorGuard.v0.json new file mode 100644 index 000000000..484cf1b00 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.WarriorGuard.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Mobiles.WarriorGuard", + "properties": [ + { + "name": "Focus", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs b/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs index 0d22eec97..e682ba9d2 100644 --- a/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs +++ b/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs @@ -1,415 +1,388 @@ using System; +using ModernUO.Serialization; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class ArcherGuard : BaseGuard { - public class ArcherGuard : BaseGuard + private Timer _attackTimer; + private Timer _idleTimer; + + [Constructible] + public ArcherGuard(Mobile target = null) : base(target) { - private Timer m_AttackTimer, m_IdleTimer; + InitStats(100, 125, 25); + Title = "the guard"; - private Mobile m_Focus; + SpeechHue = Utility.RandomDyedHue(); - [Constructible] - public ArcherGuard(Mobile target = null) : base(target) + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) { - InitStats(100, 125, 25); - Title = "the guard"; + Body = 0x191; + Name = NameList.RandomName("female"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + } - SpeechHue = Utility.RandomDyedHue(); + new Horse().Rider = this; - Hue = Race.Human.RandomSkinHue(); + AddItem(new StuddedChest()); + AddItem(new StuddedArms()); + AddItem(new StuddedGloves()); + AddItem(new StuddedGorget()); + AddItem(new StuddedLegs()); + AddItem(new Boots()); + AddItem(new SkullCap()); - if (Female = Utility.RandomBool()) + var bow = new Bow(); + + bow.Movable = false; + bow.Crafter = this; + bow.Quality = WeaponQuality.Exceptional; + + AddItem(bow); + + Container pack = new Backpack(); + + pack.Movable = false; + + var arrows = new Arrow(250); + + arrows.LootType = LootType.Newbied; + + pack.DropItem(arrows); + pack.DropItem(new Gold(10, 25)); + + AddItem(pack); + + Skills.Anatomy.Base = 120.0; + Skills.Tactics.Base = 120.0; + Skills.Archery.Base = 120.0; + Skills.MagicResist.Base = 120.0; + Skills.DetectHidden.Base = 100.0; + + NextCombatTime = Core.TickCount + 500; + Focus = target; + } + + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public override Mobile Focus + { + get => _focus; + set + { + if (Deleted) { - Body = 0x191; - Name = NameList.RandomName("female"); + return; + } + + var oldFocus = _focus; + + if (oldFocus != value) + { + _focus = value; + + if (value != null) + { + AggressiveAction(value); + } + + Combatant = value; + + if (oldFocus?.Alive == false) + { + Say("Thou hast suffered thy punishment, scoundrel."); + } + + if (value != null) + { + Say(500131); // Thou wilt regret thine actions, swine! + } + + if (_attackTimer != null) + { + _attackTimer.Stop(); + _attackTimer = null; + } + + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } + + if (_focus != null) + { + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); + ((AttackTimer)_attackTimer).DoOnTick(); + } + else + { + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); + } + } + else if (_focus == null && _idleTimer == null) + { + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); + } + + this.MarkDirty(); + } + } + + public override bool OnBeforeDeath() + { + if (_focus?.Alive == true) + { + new AvengeTimer(_focus).Start(); // If a guard dies, three more guards will spawn + } + + return base.OnBeforeDeath(); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + if (_focus != null) + { + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); + } + else + { + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); + } + } + + public override void OnAfterDelete() + { + if (_attackTimer != null) + { + _attackTimer.Stop(); + _attackTimer = null; + } + + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } + + base.OnAfterDelete(); + } + + private class AvengeTimer : Timer + { + private readonly Mobile m_Focus; + + // After 2.5 seconds, one guard will spawn every 1.0 second, three times + public AvengeTimer(Mobile focus) : base(TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0), 3) => + m_Focus = focus; + + protected override void OnTick() + { + Spawn(m_Focus, m_Focus, 1, true); + } + } + + private class AttackTimer : Timer + { + private readonly ArcherGuard m_Owner; + // private bool m_Shooting; + + public AttackTimer(ArcherGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => + m_Owner = owner; + + public void DoOnTick() + { + OnTick(); + } + + protected override void OnTick() + { + if (m_Owner.Deleted) + { + Stop(); + return; + } + + m_Owner.Criminal = false; + m_Owner.Kills = 0; + m_Owner.Stam = m_Owner.StamMax; + + var target = m_Owner.Focus; + + if (target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful(target))) + { + m_Owner.Focus = null; + Stop(); + return; + } + + if (m_Owner.Weapon is Fists) + { + m_Owner.Kill(); + Stop(); + return; + } + + if (target != null && m_Owner.Combatant != target) + { + m_Owner.Combatant = target; + } + + if (target == null) + { + Stop(); } else { - Body = 0x190; - Name = NameList.RandomName("male"); - } + // + TeleportTo(target); + target.BoltEffect(0); - new Horse().Rider = this; + if (target is BaseCreature creature) + { + creature.NoKillAwards = true; + } - AddItem(new StuddedChest()); - AddItem(new StuddedArms()); - AddItem(new StuddedGloves()); - AddItem(new StuddedGorget()); - AddItem(new StuddedLegs()); - AddItem(new Boots()); - AddItem(new SkullCap()); + target.Damage(target.HitsMax, m_Owner); + target.Kill(); // just in case, maybe Damage is overridden on some shard - var bow = new Bow(); + if (target.Corpse != null && !target.Player) + { + target.Corpse.Delete(); + } - bow.Movable = false; - bow.Crafter = this; - bow.Quality = WeaponQuality.Exceptional; + m_Owner.Focus = null; + Stop(); + } // - AddItem(bow); - - Container pack = new Backpack(); - - pack.Movable = false; - - var arrows = new Arrow(250); - - arrows.LootType = LootType.Newbied; - - pack.DropItem(arrows); - pack.DropItem(new Gold(10, 25)); - - AddItem(pack); - - Skills.Anatomy.Base = 120.0; - Skills.Tactics.Base = 120.0; - Skills.Archery.Base = 120.0; - Skills.MagicResist.Base = 120.0; - Skills.DetectHidden.Base = 100.0; - - NextCombatTime = Core.TickCount + 500; - Focus = target; - } - - public ArcherGuard(Serial serial) : base(serial) - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public override Mobile Focus - { - get => m_Focus; - set + /*else if (!m_Owner.InRange( target, 20 )) { - if (Deleted) - { - return; - } - - var oldFocus = m_Focus; - - if (oldFocus != value) - { - m_Focus = value; - - if (value != null) - { - AggressiveAction(value); - } - - Combatant = value; - - if (oldFocus?.Alive == false) - { - Say("Thou hast suffered thy punishment, scoundrel."); - } - - if (value != null) - { - Say(500131); // Thou wilt regret thine actions, swine! - } - - if (m_AttackTimer != null) - { - m_AttackTimer.Stop(); - m_AttackTimer = null; - } - - if (m_IdleTimer != null) - { - m_IdleTimer.Stop(); - m_IdleTimer = null; - } - - if (m_Focus != null) - { - m_AttackTimer = new AttackTimer(this); - m_AttackTimer.Start(); - ((AttackTimer)m_AttackTimer).DoOnTick(); - } - else - { - m_IdleTimer = new IdleTimer(this); - m_IdleTimer.Start(); - } - } - else if (m_Focus == null && m_IdleTimer == null) - { - m_IdleTimer = new IdleTimer(this); - m_IdleTimer.Start(); - } + m_Shooting = false; + m_Owner.Focus = null; } - } - - public override bool OnBeforeDeath() - { - if (m_Focus?.Alive == true) + else if (!m_Owner.InLOS( target )) { - new AvengeTimer(m_Focus).Start(); // If a guard dies, three more guards will spawn + m_Shooting = false; + TeleportTo( target ); } - - return base.OnBeforeDeath(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Focus); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) + else if (!m_Owner.CanSee( target )) { - case 0: - { - m_Focus = reader.ReadEntity(); + m_Shooting = false; - if (m_Focus != null) - { - m_AttackTimer = new AttackTimer(this); - m_AttackTimer.Start(); - } - else - { - m_IdleTimer = new IdleTimer(this); - m_IdleTimer.Start(); - } - - break; - } - } - } - - public override void OnAfterDelete() - { - if (m_AttackTimer != null) - { - m_AttackTimer.Stop(); - m_AttackTimer = null; - } - - if (m_IdleTimer != null) - { - m_IdleTimer.Stop(); - m_IdleTimer = null; - } - - base.OnAfterDelete(); - } - - private class AvengeTimer : Timer - { - private readonly Mobile m_Focus; - - public AvengeTimer(Mobile focus) : base( - TimeSpan.FromSeconds(2.5), - TimeSpan.FromSeconds(1.0), - 3 - ) // After 2.5 seconds, one guard will spawn every 1.0 second, three times - => - m_Focus = focus; - - protected override void OnTick() - { - Spawn(m_Focus, m_Focus, 1, true); - } - } - - private class AttackTimer : Timer - { - private readonly ArcherGuard m_Owner; - // private bool m_Shooting; - - public AttackTimer(ArcherGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => - m_Owner = owner; - - public void DoOnTick() - { - OnTick(); - } - - protected override void OnTick() - { - if (m_Owner.Deleted) - { - Stop(); - return; - } - - m_Owner.Criminal = false; - m_Owner.Kills = 0; - m_Owner.Stam = m_Owner.StamMax; - - var target = m_Owner.Focus; - - if (target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful(target))) - { - m_Owner.Focus = null; - Stop(); - return; - } - - if (m_Owner.Weapon is Fists) - { - m_Owner.Kill(); - Stop(); - return; - } - - if (target != null && m_Owner.Combatant != target) - { - m_Owner.Combatant = target; - } - - if (target == null) - { - Stop(); - } - else - { - // - TeleportTo(target); - target.BoltEffect(0); - - if (target is BaseCreature creature) - { - creature.NoKillAwards = true; - } - - target.Damage(target.HitsMax, m_Owner); - target.Kill(); // just in case, maybe Damage is overridden on some shard - - if (target.Corpse != null && !target.Player) - { - target.Corpse.Delete(); - } - - m_Owner.Focus = null; - Stop(); - } // - - /*else if (!m_Owner.InRange( target, 20 )) - { - m_Shooting = false; - m_Owner.Focus = null; - } - else if (!m_Owner.InLOS( target )) - { - m_Shooting = false; + if (!m_Owner.InRange( target, 2 )) + { + if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target )) TeleportTo( target ); - } - else if (!m_Owner.CanSee( target )) - { - m_Shooting = false; + } + else + { + if (!m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0) + m_Owner.Say( "Reveal!" ); + } + } + else + { + if (m_Shooting && (TimeToSpare() || OutOfMaxDistance( target ))) + m_Shooting = false; + else if (!m_Shooting && InMinDistance( target )) + m_Shooting = true; - if (!m_Owner.InRange( target, 2 )) - { - if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target )) - TeleportTo( target ); - } - else - { - if (!m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0) - m_Owner.Say( "Reveal!" ); - } - } - else + if (!m_Shooting) + { + if (m_Owner.InRange( target, 1 )) { - if (m_Shooting && (TimeToSpare() || OutOfMaxDistance( target ))) - m_Shooting = false; - else if (!m_Shooting && InMinDistance( target )) - m_Shooting = true; + if (!m_Owner.Move( (Direction)(m_Owner.GetDirectionTo( target ) - 4) | Direction.Running ) && OutOfMaxDistance( target )) + TeleportTo( target ); // Too close, move away + } + else if (!m_Owner.InRange( target, 2 )) + { + if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target )) + TeleportTo( target ); + } + } + }*/ + } - if (!m_Shooting) - { - if (m_Owner.InRange( target, 1 )) - { - if (!m_Owner.Move( (Direction)(m_Owner.GetDirectionTo( target ) - 4) | Direction.Running ) && OutOfMaxDistance( target )) - TeleportTo( target ); // Too close, move away - } - else if (!m_Owner.InRange( target, 2 )) - { - if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target )) - TeleportTo( target ); - } - } - }*/ + private bool TimeToSpare() => m_Owner.NextCombatTime - Core.TickCount > 1000; + + private bool OutOfMaxDistance(IPoint2D target) => !m_Owner.InRange(target, m_Owner.Weapon.MaxRange); + + private bool InMinDistance(IPoint2D target) => m_Owner.InRange(target, 4); + + private void TeleportTo(IEntity target) + { + var from = m_Owner.Location; + var to = target.Location; + + m_Owner.Location = to; + + Effects.SendLocationParticles( + EffectItem.Create(from, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + Effects.SendLocationParticles( + EffectItem.Create(to, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 5023 + ); + + m_Owner.PlaySound(0x1FE); + } + } + + private class IdleTimer : Timer + { + private readonly ArcherGuard m_Owner; + private int m_Stage; + + public IdleTimer(ArcherGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => + m_Owner = owner; + + protected override void OnTick() + { + if (m_Owner.Deleted) + { + Stop(); + return; } - private bool TimeToSpare() => m_Owner.NextCombatTime - Core.TickCount > 1000; - - private bool OutOfMaxDistance(IPoint2D target) => !m_Owner.InRange(target, m_Owner.Weapon.MaxRange); - - private bool InMinDistance(IPoint2D target) => m_Owner.InRange(target, 4); - - private void TeleportTo(IEntity target) + if (m_Stage++ % 4 == 0 || !m_Owner.Move(m_Owner.Direction)) { - var from = m_Owner.Location; - var to = target.Location; - - m_Owner.Location = to; + m_Owner.Direction = (Direction)Utility.Random(8); + } + if (m_Stage > 16) + { Effects.SendLocationParticles( - EffectItem.Create(from, m_Owner.Map, EffectItem.DefaultDuration), + EffectItem.Create(m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 2023 ); - Effects.SendLocationParticles( - EffectItem.Create(to, m_Owner.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 5023 - ); - m_Owner.PlaySound(0x1FE); - } - } - private class IdleTimer : Timer - { - private readonly ArcherGuard m_Owner; - private int m_Stage; - - public IdleTimer(ArcherGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => - m_Owner = owner; - - protected override void OnTick() - { - if (m_Owner.Deleted) - { - Stop(); - return; - } - - if (m_Stage++ % 4 == 0 || !m_Owner.Move(m_Owner.Direction)) - { - m_Owner.Direction = (Direction)Utility.Random(8); - } - - if (m_Stage > 16) - { - Effects.SendLocationParticles( - EffectItem.Create(m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 2023 - ); - m_Owner.PlaySound(0x1FE); - - m_Owner.Delete(); - } + m_Owner.Delete(); } } } diff --git a/Projects/UOContent/Mobiles/Guards/BaseGuard.cs b/Projects/UOContent/Mobiles/Guards/BaseGuard.cs index bfce8e083..25d302394 100644 --- a/Projects/UOContent/Mobiles/Guards/BaseGuard.cs +++ b/Projects/UOContent/Mobiles/Guards/BaseGuard.cs @@ -1,91 +1,74 @@ +using ModernUO.Serialization; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public abstract partial class BaseGuard : Mobile { - public abstract class BaseGuard : Mobile + public BaseGuard(Mobile target) { - public BaseGuard(Mobile target) + if (target != null) { - if (target != null) - { - Location = target.Location; - Map = target.Map; + Location = target.Location; + Map = target.Map; - Effects.SendLocationParticles( - EffectItem.Create(Location, Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 5023 - ); - } - } - - public BaseGuard(Serial serial) : base(serial) - { - } - - public abstract Mobile Focus { get; set; } - - public static void Spawn(Mobile caller, Mobile target, int amount = 1, bool onlyAdditional = false) - { - if (target?.Deleted != false) - { - return; - } - - foreach (var m in target.GetMobilesInRange(15)) - { - if (m is BaseGuard g) - { - if (g.Focus == null) // idling - { - g.Focus = target; - - --amount; - } - else if (g.Focus == target && !onlyAdditional) - { - --amount; - } - } - } - - while (amount-- > 0) - { - caller.Region.MakeGuard(target); - } - } - - public override bool OnBeforeDeath() - { Effects.SendLocationParticles( EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3728, 10, 10, - 2023 + 5023 ); - - PlaySound(0x1FE); - - Delete(); - - return false; - } - - 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(); } } + + public abstract Mobile Focus { get; set; } + + public static void Spawn(Mobile caller, Mobile target, int amount = 1, bool onlyAdditional = false) + { + if (target?.Deleted != false) + { + return; + } + + foreach (var m in target.GetMobilesInRange(15)) + { + if (m is BaseGuard g) + { + if (g.Focus == null) // idling + { + g.Focus = target; + + --amount; + } + else if (g.Focus == target && !onlyAdditional) + { + --amount; + } + } + } + + while (amount-- > 0) + { + caller.Region.MakeGuard(target); + } + } + + public override bool OnBeforeDeath() + { + Effects.SendLocationParticles( + EffectItem.Create(Location, Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + + PlaySound(0x1FE); + + Delete(); + + return false; + } } diff --git a/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs b/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs index 59c1e23ff..9c8457a11 100644 --- a/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs +++ b/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs @@ -1,393 +1,371 @@ using System; +using ModernUO.Serialization; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class WarriorGuard : BaseGuard { - public class WarriorGuard : BaseGuard + private Timer _attackTimer; + private Timer _idleTimer; + + [Constructible] + public WarriorGuard(Mobile target = null) : base(target) { - private Timer m_AttackTimer, m_IdleTimer; + InitStats(1000, 1000, 1000); + Title = "the guard"; - private Mobile m_Focus; + SpeechHue = Utility.RandomDyedHue(); - [Constructible] - public WarriorGuard(Mobile target = null) : base(target) + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) { - InitStats(1000, 1000, 1000); - Title = "the guard"; + Body = 0x191; + Name = NameList.RandomName("female"); - SpeechHue = Utility.RandomDyedHue(); + AddItem(Utility.RandomBool() ? new LeatherSkirt() : new LeatherShorts()); - Hue = Race.Human.RandomSkinHue(); - - if (Female = Utility.RandomBool()) - { - Body = 0x191; - Name = NameList.RandomName("female"); - - AddItem(Utility.RandomBool() ? new LeatherSkirt() : new LeatherShorts()); - - AddItem( - Utility.Random(5) switch - { - 0 => new FemaleLeatherChest(), - 1 => new FemaleStuddedChest(), - 2 => new LeatherBustierArms(), - 3 => new StuddedBustierArms(), - _ => new FemalePlateChest() // 4 - } - ); - } - else - { - Body = 0x190; - Name = NameList.RandomName("male"); - - AddItem(new PlateChest()); - AddItem(new PlateArms()); - AddItem(new PlateLegs()); - - AddItem( - Utility.Random(3) switch - { - 0 => new Doublet(Utility.RandomNondyedHue()), - 1 => new Tunic(Utility.RandomNondyedHue()), - _ => new BodySash(Utility.RandomNondyedHue()) // 3 - } - ); - } - - Utility.AssignRandomHair(this); - - if (Utility.RandomBool()) - { - Utility.AssignRandomFacialHair(this, HairHue); - } - - var weapon = new Halberd(); - - weapon.Movable = false; - weapon.Crafter = this; - weapon.Quality = WeaponQuality.Exceptional; - - AddItem(weapon); - - Container pack = new Backpack(); - - pack.Movable = false; - - pack.DropItem(new Gold(10, 25)); - - AddItem(pack); - - 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; - - NextCombatTime = Core.TickCount + 500; - Focus = target; - } - - public WarriorGuard(Serial serial) : base(serial) - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public override Mobile Focus - { - get => m_Focus; - set - { - if (Deleted) + AddItem( + Utility.Random(5) switch { - return; + 0 => new FemaleLeatherChest(), + 1 => new FemaleStuddedChest(), + 2 => new LeatherBustierArms(), + 3 => new StuddedBustierArms(), + _ => new FemalePlateChest() // 4 + } + ); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + + AddItem(new PlateChest()); + AddItem(new PlateArms()); + AddItem(new PlateLegs()); + + AddItem( + Utility.Random(3) switch + { + 0 => new Doublet(Utility.RandomNondyedHue()), + 1 => new Tunic(Utility.RandomNondyedHue()), + _ => new BodySash(Utility.RandomNondyedHue()) // 3 + } + ); + } + + Utility.AssignRandomHair(this); + + if (Utility.RandomBool()) + { + Utility.AssignRandomFacialHair(this, HairHue); + } + + var weapon = new Halberd + { + Movable = false, + Crafter = this, + Quality = WeaponQuality.Exceptional + }; + + AddItem(weapon); + + Container pack = new Backpack(); + + pack.Movable = false; + + pack.DropItem(new Gold(10, 25)); + + AddItem(pack); + + 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; + + NextCombatTime = Core.TickCount + 500; + Focus = target; + } + + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public override Mobile Focus + { + get => _focus; + set + { + if (Deleted) + { + return; + } + + var oldFocus = _focus; + + if (oldFocus != value) + { + _focus = value; + + if (value != null) + { + AggressiveAction(value); } - var oldFocus = m_Focus; + Combatant = value; - if (oldFocus != value) + if (oldFocus?.Alive == false) { - m_Focus = value; - - if (value != null) - { - AggressiveAction(value); - } - - Combatant = value; - - if (oldFocus?.Alive == false) - { - Say("Thou hast suffered thy punishment, scoundrel."); - } - - if (value != null) - { - Say(500131); // Thou wilt regret thine actions, swine! - } - - if (m_AttackTimer != null) - { - m_AttackTimer.Stop(); - m_AttackTimer = null; - } - - if (m_IdleTimer != null) - { - m_IdleTimer.Stop(); - m_IdleTimer = null; - } - - if (m_Focus != null) - { - m_AttackTimer = new AttackTimer(this); - m_AttackTimer.Start(); - ((AttackTimer)m_AttackTimer).DoOnTick(); - } - else - { - m_IdleTimer = new IdleTimer(this); - m_IdleTimer.Start(); - } - } - else if (m_Focus == null && m_IdleTimer == null) - { - m_IdleTimer = new IdleTimer(this); - m_IdleTimer.Start(); - } - } - } - - public override bool OnBeforeDeath() - { - if (m_Focus?.Alive == true) - { - new AvengeTimer(m_Focus).Start(); // If a guard dies, three more guards will spawn - } - - return base.OnBeforeDeath(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Focus); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - m_Focus = reader.ReadEntity(); - - if (m_Focus != null) - { - m_AttackTimer = new AttackTimer(this); - m_AttackTimer.Start(); - } - else - { - m_IdleTimer = new IdleTimer(this); - m_IdleTimer.Start(); - } - - break; - } - } - } - - public override void OnAfterDelete() - { - if (m_AttackTimer != null) - { - m_AttackTimer.Stop(); - m_AttackTimer = null; - } - - if (m_IdleTimer != null) - { - m_IdleTimer.Stop(); - m_IdleTimer = null; - } - - base.OnAfterDelete(); - } - - private class AvengeTimer : Timer - { - private readonly Mobile m_Focus; - - public AvengeTimer(Mobile focus) : base(TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0), 3) => - m_Focus = focus; - - protected override void OnTick() - { - Spawn(m_Focus, m_Focus, 1, true); - } - } - - private class AttackTimer : Timer - { - private readonly WarriorGuard m_Owner; - - public AttackTimer(WarriorGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => - m_Owner = owner; - - public void DoOnTick() - { - OnTick(); - } - - protected override void OnTick() - { - if (m_Owner.Deleted) - { - Stop(); - return; + Say("Thou hast suffered thy punishment, scoundrel."); } - m_Owner.Criminal = false; - m_Owner.Kills = 0; - m_Owner.Stam = m_Owner.StamMax; - - var target = m_Owner.Focus; - - if (target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful(target))) + if (value != null) { - m_Owner.Focus = null; - Stop(); - return; + Say(500131); // Thou wilt regret thine actions, swine! } - if (m_Owner.Weapon is Fists) + if (_attackTimer != null) { - m_Owner.Kill(); - Stop(); - return; + _attackTimer.Stop(); + _attackTimer = null; } - if (target != null && m_Owner.Combatant != target) + if (_idleTimer != null) { - m_Owner.Combatant = target; + _idleTimer.Stop(); + _idleTimer = null; } - if (target == null) + if (_focus != null) { - Stop(); + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); + ((AttackTimer)_attackTimer).DoOnTick(); } else { - // - TeleportTo(target); - target.BoltEffect(0); - - if (target is BaseCreature creature) - { - creature.NoKillAwards = true; - } - - target.Damage(target.HitsMax, m_Owner); - target.Kill(); // just in case, maybe Damage is overridden on some shard - - if (target.Corpse != null && !target.Player) - { - target.Corpse.Delete(); - } - - m_Owner.Focus = null; - Stop(); - } // - - /*else if (!m_Owner.InRange( target, 20 )) - { - m_Owner.Focus = null; + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } - else if (!m_Owner.InRange( target, 10 ) || !m_Owner.InLOS( target )) - { - TeleportTo( target ); - } - else if (!m_Owner.InRange( target, 1 )) - { - if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running )) - TeleportTo( target ); - } - else if (!m_Owner.CanSee( target )) - { - if (!m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0) - m_Owner.Say( "Reveal!" ); - }*/ + } + else if (_focus == null && _idleTimer == null) + { + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } - private void TeleportTo(Mobile target) + this.MarkDirty(); + } + } + + public override bool OnBeforeDeath() + { + if (_focus?.Alive == true) + { + new AvengeTimer(_focus).Start(); // If a guard dies, three more guards will spawn + } + + return base.OnBeforeDeath(); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + if (_focus != null) + { + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); + } + else + { + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); + } + } + + public override void OnAfterDelete() + { + if (_attackTimer != null) + { + _attackTimer.Stop(); + _attackTimer = null; + } + + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } + + base.OnAfterDelete(); + } + + private class AvengeTimer : Timer + { + private readonly Mobile m_Focus; + + public AvengeTimer(Mobile focus) : base(TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0), 3) => + m_Focus = focus; + + protected override void OnTick() + { + Spawn(m_Focus, m_Focus, 1, true); + } + } + + private class AttackTimer : Timer + { + private readonly WarriorGuard m_Owner; + + public AttackTimer(WarriorGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => + m_Owner = owner; + + public void DoOnTick() + { + OnTick(); + } + + protected override void OnTick() + { + if (m_Owner.Deleted) { - var from = m_Owner.Location; - var to = target.Location; + Stop(); + return; + } - m_Owner.Location = to; + m_Owner.Criminal = false; + m_Owner.Kills = 0; + m_Owner.Stam = m_Owner.StamMax; + var target = m_Owner.Focus; + + if (target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful(target))) + { + m_Owner.Focus = null; + Stop(); + return; + } + + if (m_Owner.Weapon is Fists) + { + m_Owner.Kill(); + Stop(); + return; + } + + if (target != null && m_Owner.Combatant != target) + { + m_Owner.Combatant = target; + } + + if (target == null) + { + Stop(); + } + else + { + // + TeleportTo(target); + target.BoltEffect(0); + + if (target is BaseCreature creature) + { + creature.NoKillAwards = true; + } + + target.Damage(target.HitsMax, m_Owner); + target.Kill(); // just in case, maybe Damage is overridden on some shard + + if (target.Corpse != null && !target.Player) + { + target.Corpse.Delete(); + } + + m_Owner.Focus = null; + Stop(); + } // + + /*else if (!m_Owner.InRange( target, 20 )) + { + m_Owner.Focus = null; + } + else if (!m_Owner.InRange( target, 10 ) || !m_Owner.InLOS( target )) + { + TeleportTo( target ); + } + else if (!m_Owner.InRange( target, 1 )) + { + if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running )) + TeleportTo( target ); + } + else if (!m_Owner.CanSee( target )) + { + if (!m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0) + m_Owner.Say( "Reveal!" ); + }*/ + } + + private void TeleportTo(Mobile target) + { + var from = m_Owner.Location; + var to = target.Location; + + m_Owner.Location = to; + + Effects.SendLocationParticles( + EffectItem.Create(from, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + Effects.SendLocationParticles( + EffectItem.Create(to, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 5023 + ); + + m_Owner.PlaySound(0x1FE); + } + } + + private class IdleTimer : Timer + { + private readonly WarriorGuard m_Owner; + private int m_Stage; + + public IdleTimer(WarriorGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => + m_Owner = owner; + + protected override void OnTick() + { + if (m_Owner.Deleted) + { + Stop(); + return; + } + + if (m_Stage++ % 4 == 0 || !m_Owner.Move(m_Owner.Direction)) + { + m_Owner.Direction = (Direction)Utility.Random(8); + } + + if (m_Stage > 16) + { Effects.SendLocationParticles( - EffectItem.Create(from, m_Owner.Map, EffectItem.DefaultDuration), + EffectItem.Create(m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 2023 ); - Effects.SendLocationParticles( - EffectItem.Create(to, m_Owner.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 5023 - ); - m_Owner.PlaySound(0x1FE); - } - } - private class IdleTimer : Timer - { - private readonly WarriorGuard m_Owner; - private int m_Stage; - - public IdleTimer(WarriorGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => - m_Owner = owner; - - protected override void OnTick() - { - if (m_Owner.Deleted) - { - Stop(); - return; - } - - if (m_Stage++ % 4 == 0 || !m_Owner.Move(m_Owner.Direction)) - { - m_Owner.Direction = (Direction)Utility.Random(8); - } - - if (m_Stage > 16) - { - Effects.SendLocationParticles( - EffectItem.Create(m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 2023 - ); - m_Owner.PlaySound(0x1FE); - - m_Owner.Delete(); - } + m_Owner.Delete(); } } }