diff --git a/Info/Ideas.txt b/Info/Ideas.txt new file mode 100644 index 000000000..17be0964a --- /dev/null +++ b/Info/Ideas.txt @@ -0,0 +1,3 @@ +Snack Pack + - Holds food and water + - Double click eats and drinks. diff --git a/Info/NeedsFinished.txt b/Info/NeedsFinished.txt index 48d36850a..06f1fa8f0 100644 --- a/Info/NeedsFinished.txt +++ b/Info/NeedsFinished.txt @@ -19,5 +19,4 @@ Citizens - Treausre hoard pile. CentrED Stuff - - Add Trees west of stonehollow - Add unspawnable tiles around dungeons diff --git a/Projects/UOContent/Gumps/Dashboard.cs b/Projects/UOContent/Gumps/Dashboard.cs new file mode 100644 index 000000000..e2806836b --- /dev/null +++ b/Projects/UOContent/Gumps/Dashboard.cs @@ -0,0 +1,238 @@ +// UO Wildlands Custom Script +// Adapted for ModernUO +// Licensed under the GNU General Public License v3.0 (GPL-3.0) +// Derived from ServUO Core and Community scripts (Original Authors Unknown) +// Compiled & Modified by: [Feng / UO Wildlands Team] + +using System; +using System.Collections.Generic; +using System.Text; +using Server.Accounting; +using Server.Commands; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Gumps; + +public class DashboardCommand +{ + public static void Initialize() + { + CommandSystem.Register("dashboard", AccessLevel.Player, Dashboard_OnCommand); + } + + [Usage("dashboard")] + [Description("Opens the Player Statistics dashboard.")] + public static void Dashboard_OnCommand(CommandEventArgs e) => PlayerStatsGump.DisplayTo(e.Mobile); +} + +public class PlayerStatsGump(Mobile from) : DynamicGump(50, 50) +{ + // Color Constants + private const int C_YELLOW = 2213; + private const int C_WHITE = 1152; + private const int C_RED = 1258; + private const int C_BLUE = 1264; + private const int C_GREEN = 1270; + private const int C_PURPLE = 1276; + private const int C_GREY = 2401; + + // Column X-Coordinates + private const int LeftCol = 30; + private const int RightCol = 340; + private const int ThirdCol = 650; + + public override bool Singleton => true; + + public static void DisplayTo(Mobile from) + { + if (from is not { Deleted: false, NetState: not null }) + { + return; + } + + from.SendGump(new PlayerStatsGump(from)); + } + + private readonly struct StatRow(int x, int y, string name, string value, int valueHue = C_WHITE, int valueOffset = 130) + { + public readonly int X = x; + public readonly int Y = y; + public readonly string Name = name; + public readonly string Value = value; + public readonly int ValueHue = valueHue; + public readonly int ValueOffset = valueOffset; + } + + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + const int width = 960; + + // Trimmed the height down from 780 to 700 so it ends perfectly past "Lower Ammo" + const int height = 700; + + const int attrY = 55; + const int resY = 145; + const int colY = 205; + + int Aos(AosAttribute attribute) => AosAttributes.GetValue(from, attribute); + PlayerMobile pm = from as PlayerMobile; + + builder.AddPage(); + builder.AddBackground(0, 0, width, height, 9270); + builder.AddAlphaRegion(10, 10, width - 20 , height - 20); + + // --- HEADER --- + builder.AddHtml(0, 25, width, 25, ColorAndCenter("#FFCC00", from.Name)); + + builder.AddLabel(width - 110, 20, C_YELLOW, "Refresh"); + builder.AddButton(width - 55, 21, 0x15E1, 0x15E5, 1, GumpButtonType.Reply, 0); + + // --- ATTRIBUTES --- + builder.AddLabel(LeftCol, attrY, C_YELLOW, "Attributes"); + + StatRow[] stats = + [ + new(LeftCol, attrY + 25, "Strength", from.Str.ToString()), + new(LeftCol, attrY + 50, "Hit Points", $"{from.Hits} / {from.HitsMax}"), + new(RightCol, attrY + 25, "Dexterity", from.Dex.ToString()), + new(RightCol, attrY + 50, "Stamina", $"{from.Stam} / {from.StamMax}"), + new(ThirdCol, attrY + 25, "Intelligence", from.Int.ToString()), + new(ThirdCol, attrY + 50, "Mana", $"{from.Mana} / {from.ManaMax}"), + ]; + + AddRows(ref builder, stats); + + // --- RESISTANCES --- + DrawDivider(ref builder, 20, resY - 5, width - 40); + + // Pushed X from LeftCol (30) to 435 to visually center the text over the 960px width + builder.AddLabel(435, resY + 5, C_YELLOW, "Resistances"); + + StatRow[] resists = + [ + new(100, resY + 30, "Phys", $"{from.PhysicalResistance}%", C_GREY, 55), + new(260, resY + 30, "Fire", $"{from.FireResistance}%", C_RED, 55), + new(420, resY + 30, "Cold", $"{from.ColdResistance}%", C_BLUE, 55), + new(580, resY + 30, "Poison", $"{from.PoisonResistance}%", C_GREEN, 55), + new(740, resY + 30, "Energy", $"{from.EnergyResistance}%", C_PURPLE, 55), + ]; + + AddRows(ref builder, resists); + + // --- MAIN COLUMNS --- + DrawDivider(ref builder, 20, colY - 5, width - 40); + + builder.AddLabel(LeftCol, colY + 5, C_YELLOW, "Combat Ratings"); + builder.AddLabel(RightCol, colY + 5, C_YELLOW, "Magic Ratings"); + builder.AddLabel(RightCol, colY + 140, C_YELLOW, "Miscellaneous"); + builder.AddLabel(ThirdCol, colY + 5, C_YELLOW, "Skills"); + + double swingSpeed = from.Weapon is BaseWeapon weapon ? weapon.GetDelay(from).TotalSeconds : 0.0; + double bandageSeconds = Math.Clamp(Math.Ceiling(11.0 - from.Dex / 10.0), 2.0, 8.0); + int lAmmo = from.FindItemOnLayer(Layer.Cloak) is BaseQuiver quiver ? quiver.LowerAmmoCost : 0; + + StatRow[] ratings = + [ + // 1st Column: Combat + new(LeftCol, colY + 30, "HCI", $"{Aos(AosAttribute.AttackChance)} / 45"), + new(LeftCol, colY + 50, "DCI", $"{Aos(AosAttribute.DefendChance)} / 45"), + new(LeftCol, colY + 70, "DI", $"{Aos(AosAttribute.WeaponDamage)} / 100"), + new(LeftCol, colY + 90, "SSI", $"{Aos(AosAttribute.WeaponSpeed)} / 60"), + new(LeftCol, colY + 130, "Swing Speed", swingSpeed > 0 ? $"{swingSpeed:F2}s" : "---"), + new(LeftCol, colY + 150, "Bandage Speed", $"{bandageSeconds:F1}s"), + new(LeftCol, colY + 185, "HP Regen", $"{Aos(AosAttribute.RegenHits)}"), + new(LeftCol, colY + 205, "Stam Regen", $"{Aos(AosAttribute.RegenStam)}"), + new(LeftCol, colY + 225, "Mana Regen", $"{Aos(AosAttribute.RegenMana)}"), + new(LeftCol, colY + 265, "HP Increase", $"{Aos(AosAttribute.BonusHits)} / 25"), + new(LeftCol, colY + 285, "Stam Increase", $"{Aos(AosAttribute.BonusStam)} / 25"), + new(LeftCol, colY + 305, "Mana Increase", $"{Aos(AosAttribute.BonusMana)} / 25"), + new(LeftCol, colY + 345, "Str Bonus", $"{Aos(AosAttribute.BonusStr)}"), + new(LeftCol, colY + 365, "Dex Bonus", $"{Aos(AosAttribute.BonusDex)}"), + new(LeftCol, colY + 385, "Int Bonus", $"{Aos(AosAttribute.BonusInt)}"), + new(LeftCol, colY + 425, "Enhance Pots", $"{Aos(AosAttribute.EnhancePotions)} / 50"), + new(LeftCol, colY + 445, "Lower Ammo", $"{lAmmo}"), + + // 2nd Column: Magic + new(RightCol, colY + 30, "SDI", $"{Aos(AosAttribute.SpellDamage)}%"), + new(RightCol, colY + 50, "LRC", $"{Aos(AosAttribute.LowerRegCost)}%"), + new(RightCol, colY + 70, "LMC", $"{Aos(AosAttribute.LowerManaCost)}%"), + new(RightCol, colY + 90, "FC", $"{Aos(AosAttribute.CastSpeed)} / 4"), + new(RightCol, colY + 110, "FCR", $"{Aos(AosAttribute.CastRecovery)} / 6"), + + // 2nd Column: Miscellaneous + new(RightCol, colY + 165, "Followers", $"{from.Followers} / {from.FollowersMax}"), + new(RightCol, colY + 185, "Weight", $"{from.TotalWeight} / {from.MaxWeight}"), + new(RightCol, colY + 205, "Luck", $"{Aos(AosAttribute.Luck)}"), + new(RightCol, colY + 225, "Fame", $"{from.Fame}"), + new(RightCol, colY + 245, "Karma", $"{from.Karma}"), + new(RightCol, colY + 265, "Tithing", $"{pm?.TithingPoints ?? 0}"), + new(RightCol, colY + 305, "Player Deaths", pm != null ? pm.TotalPlayerDeaths.ToString("N0") : "0"), + new(RightCol, colY + 325, "Monsters Killed", pm != null ? pm.TotalMonstersKilled.ToString("N0") : "0"), + new(RightCol, colY + 345, "Gold Looted", pm != null ? pm.TotalGoldLooted.ToString("N0") : "0"), + new(RightCol, colY + 385, "Spendable KP", pm != null ? pm.MonsterKillPoints.ToString("N0") : "0"), + new(RightCol, colY + 405, "Lifetime KP", pm != null ? pm.LifetimeKillPoints.ToString("N0") : "0"), + ]; + + AddRows(ref builder, ratings); + + // --- 3rd Column: SKILL LIST --- + var skills = from.Skills; + double humanMinimum = from.Race == Race.Human ? 20.0 : 0.0; + var visibleSkills = new List(skills.Length); + + for (int i = 0; i < skills.Length; i++) + { + Skill sk = skills[i]; + if (sk.Base > 0 || (sk.Value > sk.Base && sk.Value > humanMinimum)) + { + visibleSkills.Add(sk); + } + } + + visibleSkills.Sort(static (a, b) => string.CompareOrdinal(a.Name, b.Name)); + var skillHtml = new StringBuilder(2048); + + for (int i = 0; i < visibleSkills.Count; i++) + { + Skill sk = visibleSkills[i]; + if (i > 0) + { + skillHtml.Append("
"); + } + skillHtml.Append($"{sk.Name} {sk.Value:F1} / {sk.Cap:F1}"); + } + + // Reduced height from 500 to 430 so it seamlessly matches the new bottom edge + builder.AddHtml( + ThirdCol, colY + 30, 280, 430, + $"{skillHtml}", scrollbar: true + ); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (info.ButtonID == 1) + { + DisplayTo(from); + } + } + + private static void AddRows(ref DynamicGumpBuilder builder, ReadOnlySpan rows) + { + for (int i = 0; i < rows.Length; i++) + { + var r = rows[i]; + builder.AddLabel(r.X, r.Y, C_WHITE, r.Name); + builder.AddLabel(r.X + r.ValueOffset, r.Y, r.ValueHue, r.Value); + } + } + + private static string ColorAndCenter(string color, string text) => + $"
{text}
"; + + private static void DrawDivider(ref DynamicGumpBuilder builder, int x, int y, int width) => + builder.AddImageTiled(x, y, width, 2, 9264); +} diff --git a/Projects/UOContent/Gumps/MyStats.cs b/Projects/UOContent/Gumps/MyPoints.cs similarity index 94% rename from Projects/UOContent/Gumps/MyStats.cs rename to Projects/UOContent/Gumps/MyPoints.cs index 307b604f4..0e6ed83b1 100644 --- a/Projects/UOContent/Gumps/MyStats.cs +++ b/Projects/UOContent/Gumps/MyPoints.cs @@ -11,10 +11,10 @@ namespace Server.Custom.UI { public static void Initialize() { - CommandSystem.Register("MyStats", AccessLevel.Player, new CommandEventHandler(OnCommand)); + CommandSystem.Register("MyPoints", AccessLevel.Player, new CommandEventHandler(OnCommand)); } - [Usage("MyStats")] + [Usage("MyPoints")] [Description("Opens the player statistics gump.")] public static void OnCommand(CommandEventArgs e) { diff --git a/Projects/UOContent/Gumps/StatusBar.cs b/Projects/UOContent/Gumps/StatusBar.cs index e962fc0a1..570b42600 100644 --- a/Projects/UOContent/Gumps/StatusBar.cs +++ b/Projects/UOContent/Gumps/StatusBar.cs @@ -68,23 +68,31 @@ namespace Server.Custom.UI if (m_IsVertical) { - AddBackground(0, 0, 150, 142, 9270); + // Expanded height by 25px to accommodate the new line + AddBackground(0, 0, 150, 167, 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); - AddButton(115, 115, 9720, 9722, 2, GumpButtonType.Reply, 0); + AddHtml(15, 115, 120, 20, $"Tithing: {pm.TithingPoints}", false, false); + + // Shifted the toggle button down 25px + AddButton(115, 140, 9720, 9722, 2, GumpButtonType.Reply, 0); } else { - AddBackground(0, 0, 440, 45, 9270); + // Expanded width by 100px to accommodate the new column + AddBackground(0, 0, 540, 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); - AddButton(410, 15, 9720, 9722, 2, GumpButtonType.Reply, 0); + AddHtml(415, 12, 110, 20, $"Tithing: {pm.TithingPoints}", false, false); + + // Shifted the toggle button right 100px + AddButton(510, 15, 9720, 9722, 2, GumpButtonType.Reply, 0); } } diff --git a/ToAdd/MyStats.cs b/ToAdd/MyStats.cs deleted file mode 100644 index 99011a388..000000000 --- a/ToAdd/MyStats.cs +++ /dev/null @@ -1,256 +0,0 @@ -// UO Wildlands Custom Script -// Adapted for ModernUO -// Licensed under the GNU General Public License v3.0 (GPL-3.0) -// Derived from ServUO Core and Community scripts (Original Authors Unknown) -// Compiled & Modified by: [Feng / UO Wildlands Team] - -using System; -using System.Collections.Generic; -using System.Text; -using Server.Accounting; -using Server.Commands; -using Server.Gumps; -using Server.Items; -using Server.Mobiles; -using Server.Network; - -namespace Server.Gumps; - -public class MyStatsCommand -{ - public static void Initialize() - { - CommandSystem.Register("mystats", AccessLevel.Player, MyStats_OnCommand); - } - - [Usage("mystats")] - [Description("Opens the Player Statistics dashboard.")] - public static void MyStats_OnCommand(CommandEventArgs e) => PlayerStatsGump.DisplayTo(e.Mobile); -} - -public class PlayerStatsGump(Mobile from) : DynamicGump(50, 50) -{ - // Color Constants - private const int C_YELLOW = 2213; - private const int C_WHITE = 2036; - private const int C_RED = 1258; - private const int C_BLUE = 1264; - private const int C_GREEN = 1270; - private const int C_PURPLE = 1276; - private const int C_GREY = 2401; - - private const int RightCol = 340; // right-hand column X: Magic Ratings / Miscellaneous / Skills - - public override bool Singleton => true; - - public static void DisplayTo(Mobile from) - { - if (from is not { Deleted: false, NetState: not null }) - { - return; - } - - from.SendGump(new PlayerStatsGump(from)); - } - - // One "label: value" line anywhere in the gump. ValueOffset/ValueHue cover the - // resistance boxes, whose colored value sits 55px from the name instead of 130px. - private readonly struct StatRow(int x, int y, string name, string value, int valueHue = C_WHITE, int valueOffset = 130) - { - public readonly int X = x; - public readonly int Y = y; - public readonly string Name = name; - public readonly string Value = value; - public readonly int ValueHue = valueHue; - public readonly int ValueOffset = valueOffset; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - const int width = 640; - const int height = 850; - const int attrY = 55; - const int resY = 175; - const int colY = 220; - - // Short alias: every AOS property fetch below reads from the same mobile. - int Aos(AosAttribute attribute) => AosAttributes.GetValue(from, attribute); - - builder.AddPage(); - builder.AddBackground(0, 0, width, height, 9200); - builder.AddImageTiled(10, 10, width - 20, height - 20, 2624); - builder.AddAlphaRegion(10, 10, width - 20, height - 20); - - // --- HEADER --- - builder.AddHtml(0, 25, width, 25, ColorAndCenter("#FFCC00", from.Name)); - - builder.AddButton(width - 55, 21, 0xFBE, 0xFBF, 1, GumpButtonType.Reply, 0); // Refresh - - // --- ATTRIBUTES --- - builder.AddLabel(30, attrY, C_YELLOW, "Attributes"); - - StatRow[] stats = - [ - new(30, attrY + 25, "Strength", from.Str.ToString()), - new(RightCol, attrY + 25, "Hit Points", $"{from.Hits} / {from.HitsMax}"), - new(30, attrY + 50, "Dexterity", from.Dex.ToString()), - new(RightCol, attrY + 50, "Stamina", $"{from.Stam} / {from.StamMax}"), - new(30, attrY + 75, "Intelligence", from.Int.ToString()), - new(RightCol, attrY + 75, "Mana", $"{from.Mana} / {from.ManaMax}"), - ]; - - AddRows(ref builder, stats); - - // --- RESISTANCES --- - DrawDivider(ref builder, 20, resY - 5, width - 40); - builder.AddLabel(30, resY + 5, C_YELLOW, "Resistances"); - - StatRow[] resists = - [ - new(130, resY + 5, "Phys", $"{from.PhysicalResistance}%", C_GREY, 55), - new(230, resY + 5, "Fire", $"{from.FireResistance}%", C_RED, 55), - new(330, resY + 5, "Cold", $"{from.ColdResistance}%", C_BLUE, 55), - new(430, resY + 5, "Poison", $"{from.PoisonResistance}%", C_GREEN, 55), - new(530, resY + 5, "Energy", $"{from.EnergyResistance}%", C_PURPLE, 55), - ]; - - AddRows(ref builder, resists); - - // --- RATINGS (Combat & Magic) --- - DrawDivider(ref builder, 20, colY - 5, width - 40); - builder.AddLabel(30, colY + 5, C_YELLOW, "Combat Ratings"); - builder.AddLabel(RightCol, colY + 5, C_YELLOW, "Magic Ratings"); - - double swingSpeed = from.Weapon is BaseWeapon weapon ? weapon.GetDelay(from).TotalSeconds : 0.0; - - // Standard AOS bandage math, clamped to [2.0, 8.0] - double bandageSeconds = Math.Clamp(Math.Ceiling(11.0 - from.Dex / 10.0), 2.0, 8.0); - - // ModernUO: ammo-cost reduction lives on the equipped quiver (Layer.Cloak), - // not as a global AOS attribute. - int lAmmo = from.FindItemOnLayer(Layer.Cloak) is BaseQuiver quiver ? quiver.LowerAmmoCost : 0; - - // TODO: AbsorptionAttributes / SAAbsorptionAttribute is a custom shard system - // not present in ModernUO. Stubbed at 0 until it's wired back in. - int dmgEater = 0; - - // TODO: PointsSystem / CleanUpBritannia is a custom shard system - // not present in ModernUO. Stubbed at 0 until it's wired back in. - const double cubPoints = 0; - - StatRow[] ratings = - [ - // Left side: Combat - new(30, colY + 30, "HCI", $"{Aos(AosAttribute.AttackChance)} / 45"), - new(30, colY + 50, "DCI", $"{Aos(AosAttribute.DefendChance)} / 45"), - new(30, colY + 70, "DI", $"{Aos(AosAttribute.WeaponDamage)} / 100"), - new(30, colY + 90, "SSI", $"{Aos(AosAttribute.WeaponSpeed)} / 60"), - new(30, colY + 130, "Swing Speed", swingSpeed > 0 ? $"{swingSpeed:F2}s" : "---"), - new(30, colY + 150, "Bandage Speed", $"{bandageSeconds:F1}s"), - new(30, colY + 185, "HP Regen", $"{Aos(AosAttribute.RegenHits)}"), - new(30, colY + 205, "Stam Regen", $"{Aos(AosAttribute.RegenStam)}"), - new(30, colY + 225, "Mana Regen", $"{Aos(AosAttribute.RegenMana)}"), - new(30, colY + 265, "HP Increase", $"{Aos(AosAttribute.BonusHits)} / 25"), - new(30, colY + 285, "Stam Increase", $"{Aos(AosAttribute.BonusStam)} / 25"), - new(30, colY + 305, "Mana Increase", $"{Aos(AosAttribute.BonusMana)} / 25"), - new(30, colY + 345, "Str Bonus", $"{Aos(AosAttribute.BonusStr)}"), - new(30, colY + 365, "Dex Bonus", $"{Aos(AosAttribute.BonusDex)}"), - new(30, colY + 385, "Int Bonus", $"{Aos(AosAttribute.BonusInt)}"), - new(30, colY + 425, "Damage Eater", $"{dmgEater} / 30"), - new(30, colY + 445, "Enhance Pots", $"{Aos(AosAttribute.EnhancePotions)} / 50"), - new(30, colY + 465, "Lower Ammo", $"{lAmmo}"), - new(30, colY + 505, "Followers", $"{from.Followers} / {from.FollowersMax}"), - new(30, colY + 525, "Weight", $"{from.TotalWeight} / {from.MaxWeight}"), - - // Right side: Magic - new(RightCol, colY + 30, "SDI", $"{Aos(AosAttribute.SpellDamage)}%"), - new(RightCol, colY + 50, "LRC", $"{Aos(AosAttribute.LowerRegCost)}%"), - new(RightCol, colY + 70, "LMC", $"{Aos(AosAttribute.LowerManaCost)}%"), - new(RightCol, colY + 90, "FC", $"{Aos(AosAttribute.CastSpeed)} / 4"), - new(RightCol, colY + 110, "FCR", $"{Aos(AosAttribute.CastRecovery)} / 6"), - - // Miscellaneous - new(RightCol, colY + 165, "Luck", $"{Aos(AosAttribute.Luck)}"), - new(RightCol, colY + 185, "Fame", $"{from.Fame}"), - new(RightCol, colY + 205, "Karma", $"{from.Karma}"), - new(RightCol, colY + 225, "Tithing", $"{(from as PlayerMobile)?.TithingPoints ?? 0}"), - new(RightCol, colY + 265, "Acct Gold", $"{(from.Account is Account acct ? acct.GetTotalGold() : 0):N0}"), - new(RightCol, colY + 285, "CUB Points", $"{cubPoints:N0}"), - ]; - - AddRows(ref builder, ratings); - - builder.AddLabel(RightCol, colY + 140, C_YELLOW, "Miscellaneous"); - - // --- SKILL LIST (native client scrollbar, area Y 595..840) --- - builder.AddLabel(RightCol, colY + 345, C_YELLOW, "Skills"); - - var skills = from.Skills; - - // Filters out Human Jack of All Trades (20.0) unless base is invested or gear pushes it higher - double humanMinimum = from.Race == Race.Human ? 20.0 : 0.0; - - // Collect visible skills and sort alphabetically (gump rebuild only, not a hot path). - var visibleSkills = new List(skills.Length); - - for (int i = 0; i < skills.Length; i++) - { - Skill sk = skills[i]; - - if (sk.Base > 0 || (sk.Value > sk.Base && sk.Value > humanMinimum)) - { - visibleSkills.Add(sk); - } - } - - visibleSkills.Sort(static (a, b) => string.CompareOrdinal(a.Name, b.Name)); - - // One left-aligned block with
separators: names form a straight left edge, - // values follow the names. (A
per line would add paragraph spacing from the renderer.) - var skillHtml = new StringBuilder(2048); - - for (int i = 0; i < visibleSkills.Count; i++) - { - Skill sk = visibleSkills[i]; - - if (i > 0) - { - skillHtml.Append("
"); - } - - skillHtml.Append($"{sk.Name} {sk.Value:F1} / {sk.Cap:F1}"); - } - - // scrollbar is named arg #11 in current ModernUO: AddHtml(x,y,w,h,text,color,size,fontStyle,align,background,scrollbar) - // Height 225 ends the area at Y=820 so the scrollbar's down-arrow clears the window frame. - builder.AddHtml( - RightCol, colY + 375, 280, 225, - $"{skillHtml}", scrollbar: true - ); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) // Refresh - { - DisplayTo(from); - } - } - - // Renders any batch of StatRows — attributes, resistances and ratings all share this loop. - private static void AddRows(ref DynamicGumpBuilder builder, ReadOnlySpan rows) - { - for (int i = 0; i < rows.Length; i++) - { - var r = rows[i]; - builder.AddLabel(r.X, r.Y, C_WHITE, r.Name); - builder.AddLabel(r.X + r.ValueOffset, r.Y, r.ValueHue, r.Value); - } - } - - private static string ColorAndCenter(string color, string text) => - $"
{text}
"; - - private static void DrawDivider(ref DynamicGumpBuilder builder, int x, int y, int width) => - builder.AddImageTiled(x, y, width, 2, 9264); -}