From cc6fee28d4e93ffa832431564dd3821a9429956a Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 16 Apr 2023 20:24:14 -0700 Subject: [PATCH] feat: Adds hireables (#1393) --- .../Server.Mobiles.BaseHire.v0.json | 30 ++ .../Server.Mobiles.HireBard.v0.json | 4 + .../Server.Mobiles.HireBardArcher.v0.json | 4 + .../Server.Mobiles.HireBeggar.v0.json | 4 + .../Server.Mobiles.HireFighter.v0.json | 4 + .../Server.Mobiles.HireMage.v0.json | 4 + .../Server.Mobiles.HirePaladin.v0.json | 4 + .../Server.Mobiles.HirePeasant.v0.json | 4 + .../Server.Mobiles.HireRanger.v0.json | 4 + .../Server.Mobiles.HireRangerArcher.v0.json | 4 + .../Server.Mobiles.HireSailor.v0.json | 4 + .../Server.Mobiles.HireThief.v0.json | 4 + .../UOContent/Mobiles/Hireables/BaseHire.cs | 318 ++++++++++++++++++ .../UOContent/Mobiles/Hireables/HireBard.cs | 108 ++++++ .../Mobiles/Hireables/HireBardArcher.cs | 108 ++++++ .../UOContent/Mobiles/Hireables/HireBeggar.cs | 80 +++++ .../Mobiles/Hireables/HireFighter.cs | 211 ++++++++++++ .../UOContent/Mobiles/Hireables/HireMage.cs | 63 ++++ .../Mobiles/Hireables/HirePaladin.cs | 90 +++++ .../Mobiles/Hireables/HirePeasant.cs | 79 +++++ .../UOContent/Mobiles/Hireables/HireRanger.cs | 85 +++++ .../Mobiles/Hireables/HireRangerArcher.cs | 79 +++++ .../UOContent/Mobiles/Hireables/HireSailor.cs | 70 ++++ .../UOContent/Mobiles/Hireables/HireThief.cs | 84 +++++ 24 files changed, 1449 insertions(+) create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.BaseHire.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireBard.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireBardArcher.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireBeggar.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireFighter.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireMage.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HirePaladin.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HirePeasant.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireRanger.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireRangerArcher.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireSailor.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Mobiles.HireThief.v0.json create mode 100644 Projects/UOContent/Mobiles/Hireables/BaseHire.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireBard.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireBardArcher.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireBeggar.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireFighter.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireMage.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HirePaladin.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HirePeasant.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireRanger.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireRangerArcher.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireSailor.cs create mode 100644 Projects/UOContent/Mobiles/Hireables/HireThief.cs diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseHire.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseHire.v0.json new file mode 100644 index 000000000..cf8bf5478 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.BaseHire.v0.json @@ -0,0 +1,30 @@ +{ + "version": 0, + "type": "Server.Mobiles.BaseHire", + "properties": [ + { + "name": "NextPay", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "IsHired", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "HoldGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireBard.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireBard.v0.json new file mode 100644 index 000000000..8511ba14a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireBard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireBard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireBardArcher.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireBardArcher.v0.json new file mode 100644 index 000000000..9c5a8a5f7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireBardArcher.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireBardArcher" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireBeggar.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireBeggar.v0.json new file mode 100644 index 000000000..32a46bf10 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireBeggar.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireBeggar" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireFighter.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireFighter.v0.json new file mode 100644 index 000000000..7086317a5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireFighter.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireFighter" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireMage.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireMage.v0.json new file mode 100644 index 000000000..1a0d19d54 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireMage.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireMage" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HirePaladin.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HirePaladin.v0.json new file mode 100644 index 000000000..b4827ca67 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HirePaladin.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HirePaladin" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HirePeasant.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HirePeasant.v0.json new file mode 100644 index 000000000..0b435ab96 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HirePeasant.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HirePeasant" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireRanger.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireRanger.v0.json new file mode 100644 index 000000000..4f7b8506f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireRanger.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireRanger" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireRangerArcher.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireRangerArcher.v0.json new file mode 100644 index 000000000..7d095b74a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireRangerArcher.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireRangerArcher" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireSailor.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireSailor.v0.json new file mode 100644 index 000000000..e0a578738 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireSailor.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireSailor" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HireThief.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HireThief.v0.json new file mode 100644 index 000000000..e9b9d30a1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.HireThief.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Mobiles.HireThief" +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/Hireables/BaseHire.cs b/Projects/UOContent/Mobiles/Hireables/BaseHire.cs new file mode 100644 index 000000000..af3156b79 --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/BaseHire.cs @@ -0,0 +1,318 @@ +using Server.ContextMenus; +using Server.Items; +using System; +using System.Collections.Generic; +using ModernUO.Serialization; +using Server.Collections; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class BaseHire : BaseCreature +{ + [SerializableField(0)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private DateTime _nextPay; + + [SerializableField(2)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _holdGold; + + [CommandProperty(AccessLevel.GameMaster)] + public int Pay => PerDayCost(); + + public int GoldOnDeath { get; set; } + + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public bool IsHired + { + get => _isHired; + set + { + _isHired = value; + + Delta(MobileDelta.Noto); + InvalidateProperties(); + this.MarkDirty(); + } + } + + public BaseHire(AIType AI) : base(AI, FightMode.Aggressor) + { + ControlSlots = 2; + HoldGold = 8; + } + + public BaseHire() : base(AIType.AI_Melee, FightMode.Aggressor) => ControlSlots = 2; + + public override bool IsBondable => false; + public override bool KeepsItemsOnDeath => true; + + [AfterDeserialization] + private void AfterDeserialization() + { + if (IsHired) + { + PayTimer.RegisterTimer(this); + } + } + + public override bool OnBeforeDeath() + { + GoldOnDeath = Backpack?.GetAmount(typeof(Gold)) ?? 0; + return base.OnBeforeDeath(); + } + + public override void Delete() + { + base.Delete(); + + PayTimer.RemoveTimer(this); + } + + public override void OnDeath(Container c) + { + if (GoldOnDeath > 0) + { + c.DropItem(new Gold(GoldOnDeath)); + } + + base.OnDeath(c); + } + + public virtual Mobile GetOwner() + { + if (!Controlled) + { + return null; + } + + var owner = ControlMaster; + IsHired = true; + + if (owner == null) + { + return null; + } + + if (owner.Deleted) + { + Say(1005653); // Hmmm. I seem to have lost my master. + SetControlMaster(null); + return null; + } + + return owner; + } + + public virtual bool AddHire(Mobile m) + { + Mobile owner = GetOwner(); + + if (owner != null) + { + m.SendLocalizedMessage(1043283, owner.Name); // I am following ~1_NAME~. + return false; + } + + if (SetControlMaster(m)) + { + IsHired = true; + + return true; + } + + return false; + } + + public int PerDayCost() => + (int)(Skills[SkillName.Anatomy].Value + + Skills[SkillName.Tactics].Value + + Skills[SkillName.Macing].Value + + Skills[SkillName.Swords].Value + + Skills[SkillName.Fencing].Value + + Skills[SkillName.Archery].Value + + Skills[SkillName.MagicResist].Value + + Skills[SkillName.Healing].Value + + Skills[SkillName.Magery].Value + + Skills[SkillName.Parry].Value) / 35 + 1; + + private bool OnHireDragDrop(Mobile from, Item item) + { + if (Pay == 0) + { + SayTo(from, 500200); // I have no need for that. + return false; + } + + // Is the creature already hired + if (Controlled) + { + SayTo(from, 1042495); // I have already been hired. + return false; + } + + // Is the item the payment in gold + if (item is not Gold) + { + SayTo(from, 1043268); // Tis crass of me, but I want gold + return false; + } + + // Is the payment in gold sufficient + if (item.Amount < Pay) + { + SayHireCost(); + return false; + } + + if (from.Followers + ControlSlots > from.FollowersMax) + { + SayTo(from, 500896); // I see you already have an escort. + return false; + } + + // Try to add the hireling as a follower + if (!AddHire(from)) + { + return false; + } + + // I thank thee for paying me. I will work for thee for ~1_NUMBER~ days. + SayTo(from, 1043258, $"{item.Amount / Pay}"); // Stupid that they don't have "day" cliloc + + HoldGold += item.Amount; + NextPay = Core.Now + PayTimer.GetInterval(); + + PayTimer.RegisterTimer(this); + return true; + } + + public override bool OnDragDrop(Mobile from, Item item) => OnHireDragDrop(from, item) || base.OnDragDrop(from, item); + + internal void SayHireCost() + { + // I am available for hire for ~1_AMOUNT~ gold coins a day. If thou dost give me gold, I will work for thee. + Say(1043256, $"{Pay}"); + } + + public override void OnSpeech(SpeechEventArgs e) + { + // Check for a greeting, a 'hire', or a 'servant' + if (!e.Handled && e.Mobile.InRange(this, 6) && (e.HasKeyword(0x003B) || e.HasKeyword(0x0162) || e.HasKeyword(0x000C))) + { + if (Controlled) + { + Say(1042495); // I have already been hired. + } + else + { + e.Handled = true; + SayHireCost(); + } + } + + base.OnSpeech(e); + } + + public override void GetContextMenuEntries(Mobile from, List list) + { + if (Deleted) + { + return; + } + + if (!Controlled) + { + if (CanPaperdollBeOpenedBy(from)) + { + list.Add(new PaperdollEntry(this)); + } + + list.Add(new HireEntry(this)); + } + else + { + base.GetContextMenuEntries(from, list); + } + } + + public class PayTimer : Timer + { + private readonly HashSet _hires = new(); + public static PayTimer Instance { get; set; } + + public PayTimer() : base(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)) + { + } + + public static TimeSpan GetInterval() => TimeSpan.FromMinutes(30.0); + + protected override void OnTick() + { + using var queue = PooledRefQueue.Create(); + foreach (var hire in _hires) + { + if (hire.NextPay > Core.Now) + { + continue; + } + + hire.NextPay = Core.Now + GetInterval(); + + int pay = hire.Pay; + + if (hire.HoldGold <= pay) + { + queue.Enqueue(hire); + } + else + { + hire.HoldGold -= pay; + } + } + + while (queue.Count > 0) + { + var hire = (BaseHire)queue.Dequeue(); + + hire.GetOwner(); // Sets owner to null + hire.Say(503235); // I regret nothing! + hire.Delete(); + } + } + + public static void RegisterTimer(BaseHire hire) + { + Instance ??= new PayTimer(); + + if (!Instance.Running) + { + Instance.Start(); + } + + Instance._hires.Add(hire); + } + + public static void RemoveTimer(BaseHire hire) + { + if (Instance?._hires.Remove(hire) == true && Instance._hires.Count == 0) + { + Instance.Stop(); + } + } + } + + public class HireEntry : ContextMenuEntry + { + private readonly BaseHire _hire; + + public HireEntry(BaseHire hire) : base(6120, 3) => _hire = hire; + + public override void OnClick() + { + _hire.SayHireCost(); + } + } +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireBard.cs b/Projects/UOContent/Mobiles/Hireables/HireBard.cs new file mode 100644 index 000000000..791cbae17 --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireBard.cs @@ -0,0 +1,108 @@ +using Server.Items; +using ModernUO.Serialization; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireBard : BaseHire +{ + [Constructible] + public HireBard() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Skirt(Utility.RandomDyedHue())); + break; + } + case 1: + { + EquipItem(new Kilt(Utility.RandomNeutralHue())); + break; + } + } + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + + Title = "the bard"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(16, 16); + SetDex(26, 26); + SetInt(26, 26); + + SetDamage(5, 10); + + SetSkill(SkillName.Tactics, 35, 57); + SetSkill(SkillName.Magery, 22, 22); + SetSkill(SkillName.Swords, 45, 67); + SetSkill(SkillName.Archery, 36, 67); + SetSkill(SkillName.Parry, 45, 60); + SetSkill(SkillName.Musicianship, 66.0, 97.5); + SetSkill(SkillName.Peacemaking, 65.0, 87.5); + + Fame = 100; + Karma = 100; + + EquipItem(new Shoes(Utility.RandomNeutralHue())); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Doublet(Utility.RandomDyedHue())); + break; + } + case 1: + { + EquipItem(new Shirt(Utility.RandomDyedHue())); + break; + } + } + } + + public override void GenerateLoot() + { + AddLoot(LootPack.Average); + + if (Utility.RandomBool()) + { + PackItem(Loot.RandomInstrument()); + } + + switch (Utility.Random(2)) + { + case 0: + { + PackItem(new Longsword()); + break; + } + case 1: + { + PackItem(new Bow()); + PackItem(new Arrow(100)); + break; + } + } + + PackGold(10, 50); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireBardArcher.cs b/Projects/UOContent/Mobiles/Hireables/HireBardArcher.cs new file mode 100644 index 000000000..21347d856 --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireBardArcher.cs @@ -0,0 +1,108 @@ +using Server.Items; +using ModernUO.Serialization; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireBardArcher : BaseHire +{ + [Constructible] + public HireBardArcher() : base(AIType.AI_Archer) + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Skirt(Utility.RandomDyedHue())); + break; + } + case 1: + { + EquipItem(new Kilt(Utility.RandomNeutralHue())); + break; + } + } + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + + Title = "the bard"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(16, 16); + SetDex(26, 26); + SetInt(26, 26); + + SetDamage(5, 10); + + SetSkill(SkillName.Tactics, 35, 57); + SetSkill(SkillName.Magery, 22, 22); + SetSkill(SkillName.Swords, 45, 67); + SetSkill(SkillName.Archery, 36, 67); + SetSkill(SkillName.Parry, 45, 60); + SetSkill(SkillName.Musicianship, 66.0, 97.5); + SetSkill(SkillName.Peacemaking, 65.0, 87.5); + + Fame = 100; + Karma = 100; + + EquipItem(new Shoes(Utility.RandomNeutralHue())); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Doublet(Utility.RandomDyedHue())); + break; + } + case 1: + { + EquipItem(new Shirt(Utility.RandomDyedHue())); + break; + } + } + } + + public override void GenerateLoot() + { + AddLoot(LootPack.Average); + + if (Utility.RandomBool()) + { + PackItem(Loot.RandomInstrument()); + } + + switch (Utility.Random(2)) + { + case 0: + { + PackItem(new Longsword()); + break; + } + case 1: + { + PackItem(new Bow()); + PackItem(new Arrow(100)); + break; + } + } + + PackGold(10, 50); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireBeggar.cs b/Projects/UOContent/Mobiles/Hireables/HireBeggar.cs new file mode 100644 index 000000000..3ae243aff --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireBeggar.cs @@ -0,0 +1,80 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireBeggar : BaseHire +{ + [Constructible] + public HireBeggar() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Skirt(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Kilt(Utility.RandomNeutralHue())); + break; + } + } + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + + Title = "the beggar"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(26, 26); + SetDex(21, 21); + SetInt(36, 36); + + SetDamage(1, 1); + + SetSkill(SkillName.Begging, 66, 97); + SetSkill(SkillName.Tactics, 5, 27); + SetSkill(SkillName.Wrestling, 5, 27); + SetSkill(SkillName.Magery, 2, 2); + + Fame = 0; + Karma = 0; + + EquipItem(new Sandals(Utility.RandomNeutralHue())); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Doublet(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Shirt(Utility.RandomNeutralHue())); + break; + } + } + + PackGold(0, 25); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireFighter.cs b/Projects/UOContent/Mobiles/Hireables/HireFighter.cs new file mode 100644 index 000000000..ecfd8422a --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireFighter.cs @@ -0,0 +1,211 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireFighter : BaseHire +{ + [Constructible] + public HireFighter() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + } + + Title = "the fighter"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(91, 91); + SetDex(91, 91); + SetInt(50, 50); + + SetDamage(7, 14); + + SetSkill(SkillName.Tactics, 36, 67); + SetSkill(SkillName.Magery, 22, 22); + SetSkill(SkillName.Swords, 64, 100); + SetSkill(SkillName.Parry, 60, 82); + SetSkill(SkillName.Macing, 36, 67); + SetSkill(SkillName.Focus, 36, 67); + SetSkill(SkillName.Wrestling, 25, 47); + + Fame = 100; + Karma = 100; + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Shoes(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Boots(Utility.RandomNeutralHue())); + break; + } + } + + EquipItem(new Shirt()); + + // Pick a random sword + switch (Utility.Random(5)) + { + case 0: + { + EquipItem(new Longsword()); + break; + } + case 1: + { + EquipItem(new Broadsword()); + break; + } + case 2: + { + EquipItem(new VikingSword()); + break; + } + case 3: + { + EquipItem(new BattleAxe()); + break; + } + case 4: + { + EquipItem(new TwoHandedAxe()); + break; + } + } + + // Pick a random shield + if (FindItemOnLayer(Layer.TwoHanded) == null) + { + switch (Utility.Random(8)) + { + case 0: + { + EquipItem(new BronzeShield()); + break; + } + case 1: + { + EquipItem(new HeaterShield()); + break; + } + case 2: + { + EquipItem(new MetalKiteShield()); + break; + } + case 3: + { + EquipItem(new MetalShield()); + break; + } + case 4: + { + EquipItem(new WoodenKiteShield()); + break; + } + case 5: + { + EquipItem(new WoodenShield()); + break; + } + case 6: + { + EquipItem(new OrderShield()); + break; + } + case 7: + { + EquipItem(new ChaosShield()); + break; + } + } + } + + switch (Utility.Random(5)) + { + case 0: + { + break; + } + case 1: + { + EquipItem(new Bascinet()); + break; + } + case 2: + { + EquipItem(new CloseHelm()); + break; + } + case 3: + { + EquipItem(new NorseHelm()); + break; + } + case 4: + { + EquipItem(new Helmet()); + break; + } + } + // Pick some armour + switch (Utility.Random(4)) + { + case 0: // Leather + { + EquipItem(new LeatherChest()); + EquipItem(new LeatherArms()); + EquipItem(new LeatherGloves()); + EquipItem(new LeatherGorget()); + EquipItem(new LeatherLegs()); + break; + } + case 1: // Studded Leather + { + EquipItem(new StuddedChest()); + EquipItem(new StuddedArms()); + EquipItem(new StuddedGloves()); + EquipItem(new StuddedGorget()); + EquipItem(new StuddedLegs()); + break; + } + case 2: // Ringmail + { + EquipItem(new RingmailChest()); + EquipItem(new RingmailArms()); + EquipItem(new RingmailGloves()); + EquipItem(new RingmailLegs()); + break; + } + case 3: // Chain + { + EquipItem(new ChainChest()); + //EquipItem(new ChainCoif()); + EquipItem(new ChainLegs()); + break; + } + } + + PackGold(25, 100); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireMage.cs b/Projects/UOContent/Mobiles/Hireables/HireMage.cs new file mode 100644 index 000000000..328bb80cf --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireMage.cs @@ -0,0 +1,63 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireMage : BaseHire +{ + [Constructible] + public HireMage() : base(AIType.AI_Mage) + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + Title = "the mage"; + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(61, 75); + SetDex(81, 95); + SetInt(86, 100); + + SetDamage(10, 23); + + SetSkill(SkillName.EvalInt, 100.0, 125); + SetSkill(SkillName.Magery, 100, 125); + SetSkill(SkillName.Meditation, 100, 125); + SetSkill(SkillName.MagicResist, 100, 125); + SetSkill(SkillName.Tactics, 100, 125); + SetSkill(SkillName.Macing, 100, 125); + + Fame = 100; + Karma = 100; + + EquipItem(new Shirt()); + EquipItem(new Robe(Utility.RandomNeutralHue())); + + if (Utility.RandomBool()) + { + EquipItem(new Shoes(Utility.RandomNeutralHue())); + } + else + { + EquipItem(new ThighBoots()); + } + + PackGold(20, 100); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HirePaladin.cs b/Projects/UOContent/Mobiles/Hireables/HirePaladin.cs new file mode 100644 index 000000000..0226ebe63 --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HirePaladin.cs @@ -0,0 +1,90 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HirePaladin : BaseHire +{ + [Constructible] + public HirePaladin() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + } + + Title = "the paladin"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + switch (Utility.Random(5)) + { + case 0: + { + break; + } + case 1: + { + EquipItem(new Bascinet()); + break; + } + case 2: + { + EquipItem(new CloseHelm()); + break; + } + case 3: + { + EquipItem(new NorseHelm()); + break; + } + case 4: + { + EquipItem(new Helmet()); + break; + } + } + + SetStr(86, 100); + SetDex(81, 95); + SetInt(61, 75); + + SetDamage(10, 23); + + SetSkill(SkillName.Swords, 66.0, 97.5); + SetSkill(SkillName.Anatomy, 65.0, 87.5); + SetSkill(SkillName.MagicResist, 25.0, 47.5); + SetSkill(SkillName.Healing, 65.0, 87.5); + SetSkill(SkillName.Tactics, 65.0, 87.5); + SetSkill(SkillName.Wrestling, 15.0, 37.5); + SetSkill(SkillName.Parry, 45.0, 60.5); + SetSkill(SkillName.Chivalry, 85, 100); + + Fame = 100; + Karma = 250; + + EquipItem(new Shoes(Utility.RandomNeutralHue())); + EquipItem(new Shirt()); + EquipItem(new VikingSword()); + EquipItem(new MetalKiteShield()); + + EquipItem(new PlateChest()); + EquipItem(new PlateLegs()); + EquipItem(new PlateArms()); + EquipItem(new LeatherGorget()); + PackGold(20, 100); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HirePeasant.cs b/Projects/UOContent/Mobiles/Hireables/HirePeasant.cs new file mode 100644 index 000000000..150a11eda --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HirePeasant.cs @@ -0,0 +1,79 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HirePeasant : BaseHire +{ + [Constructible] + public HirePeasant() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Skirt(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Kilt(Utility.RandomNeutralHue())); + break; + } + } + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + Title = "the peasant"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + EquipItem(new Katana()); + + SetStr(26, 26); + SetDex(21, 21); + SetInt(16, 16); + + SetDamage(10, 23); + + SetSkill(SkillName.Tactics, 5, 27); + SetSkill(SkillName.Wrestling, 5, 5); + SetSkill(SkillName.Swords, 5, 27); + + Fame = 0; + Karma = 0; + + EquipItem(new Sandals(Utility.RandomNeutralHue())); + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Doublet(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Shirt(Utility.RandomNeutralHue())); + break; + } + } + + PackGold(0, 25); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireRanger.cs b/Projects/UOContent/Mobiles/Hireables/HireRanger.cs new file mode 100644 index 000000000..30e98b98f --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireRanger.cs @@ -0,0 +1,85 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireRanger : BaseHire +{ + [Constructible] + public HireRanger() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + + Title = "the ranger"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(91, 91); + SetDex(76, 76); + SetInt(61, 61); + + SetDamage(13, 24); + + SetSkill(SkillName.Wrestling, 15, 37); + SetSkill(SkillName.Parry, 45, 60); + SetSkill(SkillName.Archery, 66, 97); + SetSkill(SkillName.Magery, 62, 62); + SetSkill(SkillName.Swords, 35, 57); + SetSkill(SkillName.Fencing, 15, 37); + SetSkill(SkillName.Tactics, 65, 87); + + Fame = 100; + Karma = 125; + + EquipItem(new Shoes(Utility.RandomNeutralHue())); + EquipItem(new Shirt()); + + // Pick a random sword + switch (Utility.Random(3)) + { + case 0: + { + EquipItem(new Longsword()); + break; + } + case 1: + { + EquipItem(new VikingSword()); + break; + } + case 2: + { + EquipItem(new Broadsword()); + break; + } + } + + EquipItem(new StuddedChest()); + EquipItem(new StuddedArms()); + EquipItem(new StuddedGloves()); + EquipItem(new StuddedLegs()); + EquipItem(new StuddedGorget()); + } + + public override void GenerateLoot() + { + AddLoot(LootPack.Average); + PackItem(new Arrow(20)); + PackGold(10, 75); + } +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireRangerArcher.cs b/Projects/UOContent/Mobiles/Hireables/HireRangerArcher.cs new file mode 100644 index 000000000..bb2ddb8bf --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireRangerArcher.cs @@ -0,0 +1,79 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireRangerArcher : BaseHire +{ + [Constructible] + public HireRangerArcher() : base(AIType.AI_Archer) + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + } + + Title = "the ranger"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(91, 91); + SetDex(76, 76); + SetInt(61, 61); + + SetDamage(13, 24); + + SetSkill(SkillName.Wrestling, 15, 37); + SetSkill(SkillName.Parry, 45, 60); + SetSkill(SkillName.Archery, 66, 97); + SetSkill(SkillName.Magery, 62, 62); + SetSkill(SkillName.Swords, 35, 57); + SetSkill(SkillName.Fencing, 15, 37); + SetSkill(SkillName.Tactics, 65, 87); + + Fame = 100; + Karma = 125; + + EquipItem(new Shoes(Utility.RandomNeutralHue())); + EquipItem(new Shirt()); + + // Pick a random sword + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Bow()); + break; + } + case 1: + { + EquipItem(new CompositeBow()); + break; + } + } + + EquipItem(new RangerChest()); + EquipItem(new RangerArms()); + EquipItem(new RangerGloves()); + EquipItem(new RangerGorget()); + EquipItem(new RangerLegs()); + } + + public override void GenerateLoot() + { + PackItem(new Arrow(100)); + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireSailor.cs b/Projects/UOContent/Mobiles/Hireables/HireSailor.cs new file mode 100644 index 000000000..bdd6d1a31 --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireSailor.cs @@ -0,0 +1,70 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireSailor : BaseHire +{ + [Constructible] + public HireSailor() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + Title = "the sailor"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(86); + SetDex(66); + SetInt(41); + + SetDamage(10, 23); + + SetSkill(SkillName.Stealing, 66.0, 97.5); + SetSkill(SkillName.Peacemaking, 65.0, 87.5); + SetSkill(SkillName.MagicResist, 25.0, 47.5); + SetSkill(SkillName.Healing, 65.0, 87.5); + SetSkill(SkillName.Tactics, 65.0, 87.5); + SetSkill(SkillName.Fencing, 65.0, 87.5); + SetSkill(SkillName.Parry, 45.0, 60.5); + SetSkill(SkillName.Lockpicking, 65, 87); + SetSkill(SkillName.Hiding, 65, 87); + SetSkill(SkillName.Snooping, 65, 87); + Fame = 100; + Karma = 0; + + EquipItem(new Shoes(Utility.RandomNeutralHue())); + EquipItem(new Cutlass()); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Doublet(Utility.RandomDyedHue())); + break; + } + case 1: + { + EquipItem(new Shirt(Utility.RandomDyedHue())); + break; + } + } + } + + public override bool ClickTitle => false; +} diff --git a/Projects/UOContent/Mobiles/Hireables/HireThief.cs b/Projects/UOContent/Mobiles/Hireables/HireThief.cs new file mode 100644 index 000000000..03e20d2f0 --- /dev/null +++ b/Projects/UOContent/Mobiles/Hireables/HireThief.cs @@ -0,0 +1,84 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles; + +[SerializationGenerator(0)] +public partial class HireThief : BaseHire +{ + [Constructible] + public HireThief() + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); + + if (Female = Utility.RandomBool()) + { + Body = 0x191; + Name = NameList.RandomName("female"); + + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Skirt(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Kilt(Utility.RandomNeutralHue())); + break; + } + } + } + else + { + Body = 0x190; + Name = NameList.RandomName("male"); + EquipItem(new ShortPants(Utility.RandomNeutralHue())); + } + Title = "the thief"; + HairItemID = Race.RandomHair(Female); + HairHue = Race.RandomHairHue(); + Race.RandomFacialHair(this); + + SetStr(81, 95); + SetDex(86, 100); + SetInt(61, 75); + + SetDamage(10, 23); + + SetSkill(SkillName.Stealing, 66.0, 97.5); + SetSkill(SkillName.Peacemaking, 65.0, 87.5); + SetSkill(SkillName.MagicResist, 25.0, 47.5); + SetSkill(SkillName.Healing, 65.0, 87.5); + SetSkill(SkillName.Tactics, 65.0, 87.5); + SetSkill(SkillName.Fencing, 65.0, 87.5); + SetSkill(SkillName.Parry, 45.0, 60.5); + SetSkill(SkillName.Lockpicking, 65, 87); + SetSkill(SkillName.Hiding, 65, 87); + SetSkill(SkillName.Snooping, 65, 87); + Fame = 100; + Karma = 0; + + EquipItem(new Sandals(Utility.RandomNeutralHue())); + EquipItem(new Dagger()); + switch (Utility.Random(2)) + { + case 0: + { + EquipItem(new Doublet(Utility.RandomNeutralHue())); + break; + } + case 1: + { + EquipItem(new Shirt(Utility.RandomNeutralHue())); + break; + } + } + + PackGold(0, 25); + } + + public override bool ClickTitle => false; +}