diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index d1db8113b..4504c2115 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -825,13 +825,19 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { if (m_TithingPoints != value) { + int oldValue = m_TithingPoints; m_TithingPoints = value; Delta(MobileDelta.TithingPoints); + OnTithingPointsChange(oldValue); } } } + public virtual void OnTithingPointsChange(int oldValue) + { + } + [CommandProperty(AccessLevel.GameMaster)] public int Followers { diff --git a/Projects/UOContent/Gumps/StatusBar.cs b/Projects/UOContent/Gumps/StatusBar.cs new file mode 100644 index 000000000..9048acc15 --- /dev/null +++ b/Projects/UOContent/Gumps/StatusBar.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Concurrent; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Commands; +using Server.Network; + +namespace Server.Custom.UI +{ + public class StatusBar + { + // Safely tracks if a player prefers the vertical (true) or horizontal (false) layout + public static ConcurrentDictionary Preferences = new ConcurrentDictionary(); + + public static void Initialize() + { + CommandSystem.Register("StatusBar", AccessLevel.Player, new CommandEventHandler(OnCommand)); + } + + [Usage("StatusBar")] + [Description("Opens a quick-view status bar for Fame, Karma, Hunger, Thirst, Tithing, and Kills.")] + public static void OnCommand(CommandEventArgs e) + { + if (e.Mobile is PlayerMobile pm) + { + // Get their saved preference, defaulting to horizontal (false) + bool isVert = Preferences.GetOrAdd(pm, false); + + // ModernUO Generics: Notice the brackets and empty parentheses! + pm.CloseGump(); + pm.SendGump(new StatusBarGump(pm, isVert)); + } + } + + public static void Refresh(Mobile from, bool force = false) + { + if (from is PlayerMobile pm && pm.NetState != null) + { + // ModernUO Generics: HasGump takes 0 arguments now + if (pm.HasGump() || force) + { + bool isVert = Preferences.GetOrAdd(pm, false); + + pm.CloseGump(); + pm.SendGump(new StatusBarGump(pm, isVert)); + } + } + } + } + + public class StatusBarGump : Gump + { + private PlayerMobile m_Player; + private bool m_IsVertical; + + public StatusBarGump(PlayerMobile pm, bool isVertical) : base(50, 50) + { + m_Player = pm; + m_IsVertical = isVertical; + + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; + + AddPage(0); + + if (m_IsVertical) + { + AddBackground(0, 0, 150, 200, 9270); + + AddHtml(15, 15, 120, 20, $"Fame: {pm.Fame}", false, false); + AddHtml(15, 40, 120, 20, $"Karma: {pm.Karma}", false, false); + AddHtml(15, 65, 120, 20, $"Hunger: {pm.Hunger}/20", false, false); + AddHtml(15, 90, 120, 20, $"Thirst: {pm.Thirst}/20", false, false); + AddHtml(15, 115, 120, 20, $"Tithing: {pm.TithingPoints}", false, false); + AddHtml(15, 140, 120, 20, $"Kills: {pm.MonsterKillPoints}", false, false); + + AddButton(115, 167, 9720, 9722, 2, GumpButtonType.Reply, 0); + } + else + { + AddBackground(0, 0, 650, 45, 9270); + + AddHtml(15, 12, 110, 20, $"Fame: {pm.Fame}", false, false); + AddHtml(110, 12, 110, 20, $"Karma: {pm.Karma}", false, false); + AddHtml(215, 12, 110, 20, $"Hunger: {pm.Hunger}/20", false, false); + AddHtml(315, 12, 110, 20, $"Thirst: {pm.Thirst}/20", false, false); + AddHtml(410, 12, 110, 20, $"Tithing: {pm.TithingPoints}", false, false); + AddHtml(510, 12, 110, 20, $"Kills: {pm.MonsterKillPoints}", false, false); + + AddButton(625, 15, 9720, 9722, 2, GumpButtonType.Reply, 0); + } + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (m_Player == null || m_Player.Deleted) + return; + + switch (info.ButtonID) + { + case 1: // Refresh the gump without changing layout + m_Player.SendGump(new StatusBarGump(m_Player, m_IsVertical)); + break; + case 2: // Toggle the layout and save their preference + StatusBar.Preferences[m_Player] = !m_IsVertical; + m_Player.SendGump(new StatusBarGump(m_Player, !m_IsVertical)); + break; + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index ebcdc43b5..7d0174ef8 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -3453,6 +3453,17 @@ namespace Server.Mobiles Paragon.GiveArtifactTo(mob); } } + if (mob is PlayerMobile player) + { + int points = this.Fame / 200; + + if (points > 0) + { + player.MonsterKillPoints += points; + + player.SendMessage(0x43, $"You earned {points} kill points. (Total: {player.MonsterKillPoints})"); + } + } } public override void OnDeath(Container c) diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 39be0e10a..62646b111 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -42,7 +42,7 @@ using BaseMLQuestGump = Server.Engines.MLQuests.Gumps.BaseMLQuestGump; using CalcMoves = Server.Movement.Movement; using QuestOfferGump = Server.Engines.MLQuests.Gumps.QuestOfferGump; using RankDefinition = Server.Guilds.RankDefinition; - +using Server.Custom.UI; namespace Server.Mobiles { [Flags] @@ -729,6 +729,22 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.Counselor, canModify: true)] public VirtueContext Virtues => VirtueSystem.GetOrCreateVirtues(this); + + private int m_MonsterKillPoints; + [CommandProperty(AccessLevel.GameMaster)] + public int MonsterKillPoints { + get => m_MonsterKillPoints; + set + { + int oldValue = m_MonsterKillPoints; + if (oldValue != value) + { + m_MonsterKillPoints = value; + OnMonsterKillPointsChanged(oldValue); + } + } + } + public HonorContext ReceivedHonorContext { get; set; } public QuestArrow QuestArrow @@ -2771,6 +2787,11 @@ namespace Server.Mobiles switch (version) { + case 35: + { + m_MonsterKillPoints = reader.ReadInt(); + goto case 34; + } case 34: // Acquired Recipes is now a Set case 33: // Removes champion title case 32: // Removes virtue properties @@ -3118,7 +3139,9 @@ namespace Server.Mobiles { base.Serialize(writer); - writer.Write(34); // version + writer.Write(35); // version + + writer.Write(MonsterKillPoints); if (Stabled == null) { @@ -3793,10 +3816,32 @@ namespace Server.Mobiles public override void OnKarmaChange(int oldValue) { + StatusBar.Refresh(this); } public override void OnFameChange(int oldValue) { + StatusBar.Refresh(this); + } + + public override void OnHungerChanged(int oldValue) + { + StatusBar.Refresh(this); + } + + public override void OnThirstChanged(int oldValue) + { + StatusBar.Refresh(this); + } + + public override void OnTithingPointsChange(int oldValue) + { + StatusBar.Refresh(this); + } + + public virtual void OnMonsterKillPointsChanged(int oldValue) + { + StatusBar.Refresh(this); } public override void OnSkillChange(SkillName skill, double oldBase)