diff --git a/Distribution/Data/MLQuests.cfg b/Distribution/Data/MLQuests.cfg index 4a322ffc3..8db2212ca 100644 --- a/Distribution/Data/MLQuests.cfg +++ b/Distribution/Data/MLQuests.cfg @@ -1,4 +1,4 @@ -IronForTheForge -DestardDrakeBounty -DungeonBountyBoard -DeceitSkeletalKnightBounty +#IronForTheForge +#DestardDrakeBounty +#DungeonBountyBoard +#DeceitSkeletalKnightBounty diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/DungeonBountyBoard.cs b/Projects/UOContent/Engines/ML Quests/Definitions/DungeonBountyBoard.cs index 858f23447..f669c1266 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/DungeonBountyBoard.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/DungeonBountyBoard.cs @@ -18,6 +18,13 @@ public partial class DungeonBountyBoard : Item, IQuestGiver // Implementation for IQuestGiver public List MLQuests => MLQuestSystem.FindQuestList(GetType()); + // Automatically registers all bounties when the server boots up + public static void Initialize() + { + MLQuestSystem.Register(new DestardDrakeBounty(), typeof(DungeonBountyBoard)); + MLQuestSystem.Register(new DeceitSkeletalKnightBounty(), typeof(DungeonBountyBoard)); + } + [Constructible] public DungeonBountyBoard() : base(0x1E5E) // Bulletin board Graphic ID { @@ -61,7 +68,7 @@ public class DestardDrakeBounty : MLQuest Objectives.Add(new KillObjective(10, new[] { typeof(Drake) }, "Drakes")); // Reward: 1,200 Gold - Rewards.Add(new ItemReward("1,200 Gold", typeof(Gold), 1200)); + Rewards.Add(new ItemReward("1,200 Bounty Tokens", typeof(BountyToken), 1200)); } public override void Generate() @@ -91,7 +98,7 @@ public class DeceitSkeletalKnightBounty : MLQuest Objectives.Add(new KillObjective(10, new[] { typeof(SkeletalKnight) }, "Skeletal Knights")); // Reward: 1,500 Gold - Rewards.Add(new ItemReward("1,500 Gold", typeof(Gold), 1500)); + Rewards.Add(new ItemReward("1,500 Bounty Tokens", typeof(BountyToken), 1500)); } public override void Generate() diff --git a/Projects/UOContent/Items/BountyToken.cs b/Projects/UOContent/Items/BountyToken.cs new file mode 100644 index 000000000..69494d2cb --- /dev/null +++ b/Projects/UOContent/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/Mobiles/BountyVendor.cs b/Projects/UOContent/Mobiles/BountyVendor.cs new file mode 100644 index 000000000..b20eb9413 --- /dev/null +++ b/Projects/UOContent/Mobiles/BountyVendor.cs @@ -0,0 +1,141 @@ +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; + Body = 0x190; + Female = false; + Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); + InitStats(100, 100, 25); + + Utility.AssignRandomHair(this); + + AddItem(new Backpack()); + AddItem(new FancyShirt(0x482)); + AddItem(new LongPants(0x1F0D)); + AddItem(new Boots()); + AddItem(new Cloak(0x482)); + } + + public override bool IsInvulnerable => true; + public override string DefaultName => "Capt. Vance"; + + 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)); + } +}