From b4e25a27137d4bcca749e61c6ecb325770ad90d0 Mon Sep 17 00:00:00 2001 From: WarrentyExpired Date: Fri, 9 Oct 2026 18:16:18 -0400 Subject: [PATCH] #W# MLQuest: Bounty system. --- .../Engines/ML Quests/Items/BountyToken.cs | 23 +++ .../Engines/ML Quests/Mobiles/BountyVendor.cs | 160 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 Projects/UOContent/Engines/ML Quests/Items/BountyToken.cs create mode 100644 Projects/UOContent/Engines/ML Quests/Mobiles/BountyVendor.cs diff --git a/Projects/UOContent/Engines/ML Quests/Items/BountyToken.cs b/Projects/UOContent/Engines/ML Quests/Items/BountyToken.cs new file mode 100644 index 000000000..69494d2cb --- /dev/null +++ b/Projects/UOContent/Engines/ML Quests/Items/BountyToken.cs @@ -0,0 +1,23 @@ +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class BountyToken : Item +{ + [Constructible] + public BountyToken() : this(1) + { + } + + [Constructible] + public BountyToken(int amount) : base(0x0EED) // Gold coin artwork + { + Name = "Bounty Token"; + Hue = 0x482; // Unique vibrant cyan/blue hue + Stackable = true; + Amount = amount; + LootType = LootType.Blessed; // Keeps tokens safe in dungeons upon death + } +} diff --git a/Projects/UOContent/Engines/ML Quests/Mobiles/BountyVendor.cs b/Projects/UOContent/Engines/ML Quests/Mobiles/BountyVendor.cs new file mode 100644 index 000000000..0426f0f73 --- /dev/null +++ b/Projects/UOContent/Engines/ML Quests/Mobiles/BountyVendor.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using ModernUO.Serialization; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Mobiles; + +[SerializationGenerator(0, false)] +public partial class BountyVendor : BaseCreature +{ + [Constructible] + public BountyVendor() : base(AIType.AI_Vendor, FightMode.None, 2) + { + Title = "the Bounty Quartermaster"; + Race = Race.Human; + + // Randomize gender, body ID, and name + Female = Utility.RandomBool(); + Body = Female ? 0x191 : 0x190; + Name = NameList.RandomName(Female ? "female" : "male"); + + Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); + InitStats(100, 100, 25); + + // Assign randomized hair and facial hair with matching hue + int hairHue = Race.RandomHairHue(); + Utility.AssignRandomHair(this, hairHue); + + if (!Female) + { + Utility.AssignRandomFacialHair(this, hairHue); + } + + AddItem(new Backpack()); + AddItem(new FancyShirt(0x482)); + + if (Female) + { + AddItem(Utility.RandomBool() ? new Skirt(0x1F0D) : new LongPants(0x1F0D)); + } + else + { + AddItem(new LongPants(0x1F0D)); + } + + AddItem(new Boots()); + AddItem(new Cloak(0x482)); + } + + public override bool IsInvulnerable => true; + + public override void OnDoubleClick(Mobile from) + { + if (from is PlayerMobile pm) + { + if (from.InRange(Location, 3)) + { + pm.SendGump(new BountyRewardGump(pm, this)); + } + else + { + from.SendLocalizedMessage(500446); // That is too far away. + } + } + } +} + +// ========================================== +// BOUNTY REWARD SHOP GUMP +// ========================================== +public class BountyRewardGump : Gump +{ + private readonly PlayerMobile _player; + private readonly BountyVendor _vendor; + + // Define Reward Store Items using factory functions: (Name, TokenCost, CreateItem) + private static readonly (string Name, int Cost, Func CreateItem)[] Rewards = new (string, int, Func)[] + { + ("Dungeon Hunter Ring", 25, () => new GoldRing()), + ("Decorative Dragon Statue", 50, () => new MonsterStatuette(MonsterStatuetteType.Dragon)), + ("Monster Statue Dyetub", 75, () => new StatuetteDyeTub()), + ("Bag of Sending", 100, () => new BagOfSending()) + }; + + public BountyRewardGump(PlayerMobile player, BountyVendor vendor) : base(50, 50) + { + _player = player; + _vendor = vendor; + + int totalTokens = player.Backpack != null ? player.Backpack.GetAmount(typeof(BountyToken)) : 0; + + AddPage(0); + + // Matching BuildWorld.cs Frame Style + AddBackground(0, 0, 450, 310, 9270); + AddAlphaRegion(10, 10, 430, 290); + + // Header Labels + AddLabel(110, 15, 1152, "BOUNTY QUARTERMASTER STORE"); + AddLabel(20, 40, 0x3F, $"Your Bounty Tokens: {totalTokens}"); + + int y = 75; + for (int i = 0; i < Rewards.Length; i++) + { + var reward = Rewards[i]; + + // Item Purchase Button & Labels + AddButton(20, y, 4005, 4007, i + 1, GumpButtonType.Reply, 0); + AddLabel(55, y, 1152, reward.Name); + AddLabel(300, y, 0x3F, $"{reward.Cost} Tokens"); + + y += 35; + } + + // Close Button + AddButton(20, y + 10, 4017, 4019, 0, GumpButtonType.Reply, 0); + AddLabel(55, y + 10, 1152, "Close Shop"); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (info.ButtonID <= 0 || info.ButtonID > Rewards.Length) + { + return; + } + + int index = info.ButtonID - 1; + var reward = Rewards[index]; + + if (_player.Backpack == null) + { + return; + } + + int tokenCount = _player.Backpack.GetAmount(typeof(BountyToken)); + + if (tokenCount >= reward.Cost) + { + if (_player.Backpack.ConsumeTotal(typeof(BountyToken), reward.Cost)) + { + Item newItem = reward.CreateItem(); + _player.AddToBackpack(newItem); + + _player.SendMessage(0x35, $"You purchased {reward.Name} for {reward.Cost} Bounty Tokens!"); + } + } + else + { + _player.SendMessage(0x22, $"You need {reward.Cost - tokenCount} more Bounty Tokens to buy this item."); + } + + // Reopen gump to refresh balance + _player.SendGump(new BountyRewardGump(_player, _vendor)); + } +}