diff --git a/.editorconfig b/.editorconfig index 53c38f2144..b331b53687 100644 --- a/.editorconfig +++ b/.editorconfig @@ -144,6 +144,7 @@ resharper_csharp_wrap_before_invocation_rpar = true resharper_csharp_wrap_parameters_style = chop_if_long resharper_keep_existing_invocation_parens_arrangement = false resharper_keep_existing_property_patterns_arrangement = false +resharper_max_initializer_elements_on_line = 2 resharper_max_invocation_arguments_on_line = 5 resharper_place_expr_property_on_single_line = true resharper_place_simple_initializer_on_single_line = false diff --git a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs index 510b29bf33..435e850333 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs @@ -353,7 +353,7 @@ public Guid FindFirstMap() if (OrderedMaps.Count > 0) { - lowestMap = OrderedMaps.OrderBy(m => m.TimeCreated).FirstOrDefault().MapId; + lowestMap = OrderedMaps.OrderBy(m => m.TimeCreated).FirstOrDefault()?.MapId ?? default; } return lowestMap; diff --git a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListMap.cs b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListMap.cs index 71a03b6db2..a458211cc0 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListMap.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListMap.cs @@ -8,16 +8,14 @@ public partial class MapListMap : MapListItem, IComparable public long TimeCreated; - public MapListMap() : base() + public MapListMap() { Name = "New Map"; Type = 1; } - public int CompareTo(MapListMap obj) - { - return Name.CompareTo(obj.Name); - } + public int CompareTo(MapListMap other) => + string.Compare(Name, other.Name, StringComparison.CurrentCultureIgnoreCase); public void PostLoad(DatabaseObjectLookup gameMaps, bool isServer = true) { diff --git a/Framework/Intersect.Framework.Core/Models/DbList.cs b/Framework/Intersect.Framework.Core/Models/DbList.cs new file mode 100644 index 0000000000..95e5cca8b5 --- /dev/null +++ b/Framework/Intersect.Framework.Core/Models/DbList.cs @@ -0,0 +1,34 @@ +using System.Diagnostics.CodeAnalysis; +using Intersect.Collections; + +namespace Intersect.Models; + +public partial class DbList : List where T : IDatabaseObject +{ + public List GetAll() + { + var list = new List(); + foreach (var l in ToArray()) + { + list.Add((T)DatabaseObjectLookup.LookupMap[typeof(T)].Get(l)); + } + + return list; + } + + public T Get(Guid id) + { + return (T)DatabaseObjectLookup.LookupMap[typeof(T)].Get(id); + } + + public bool TryGet(Guid id, [NotNullWhen(true)] out T? value) + { + if (DatabaseObjectLookup.LookupMap.TryGetValue(typeof(T), out var lookupMap)) + { + return lookupMap.TryGetValue(id, out value); + } + + value = default; + return false; + } +} \ No newline at end of file diff --git a/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs b/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs index 4db21ab758..9ce80c4392 100644 --- a/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs +++ b/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs @@ -1,5 +1,4 @@ -using Intersect.Collections; -using Intersect.Enums; +using Intersect.Enums; using Newtonsoft.Json; namespace Intersect.Models; @@ -27,25 +26,4 @@ public interface IDatabaseObject : INamedObject void Delete(); -} - -public partial class DbList : List -{ - - public List GetAll() - { - var list = new List(); - foreach (var l in ToArray()) - { - list.Add((T) DatabaseObjectLookup.LookupMap[typeof(T)].Get(l)); - } - - return list; - } - - public T Get(Guid id) - { - return (T) DatabaseObjectLookup.LookupMap[typeof(T)].Get(id); - } - -} +} \ No newline at end of file diff --git a/Intersect (Core)/Network/Packets/Client/EventInputVariablePacket.cs b/Intersect (Core)/Network/Packets/Client/EventInputVariablePacket.cs index 634678b85d..96a17ce9d5 100644 --- a/Intersect (Core)/Network/Packets/Client/EventInputVariablePacket.cs +++ b/Intersect (Core)/Network/Packets/Client/EventInputVariablePacket.cs @@ -26,7 +26,7 @@ public EventInputVariablePacket(Guid eventId, bool booleanValue, int value, stri public bool BooleanValue { get; set; } [Key(2)] - public string StringValue { get; set; } + public string? StringValue { get; set; } [Key(3)] public int Value { get; set; } diff --git a/Intersect.Client.Core/Core/Graphics.cs b/Intersect.Client.Core/Core/Graphics.cs index b8d87894c3..bc2b668668 100644 --- a/Intersect.Client.Core/Core/Graphics.cs +++ b/Intersect.Client.Core/Core/Graphics.cs @@ -529,7 +529,7 @@ public static void DrawInGame(TimeSpan deltaTime) } //Game Rendering - public static void Render(TimeSpan deltaTime, TimeSpan _) + public static void Render(TimeSpan deltaTime, TimeSpan totalTime) { var takingScreenshot = false; if (Renderer?.ScreenshotRequests.Count > 0) @@ -592,7 +592,7 @@ public static void Render(TimeSpan deltaTime, TimeSpan _) Renderer.Scale = Globals.Database.UIScale; - Interface.Interface.DrawGui(); + Interface.Interface.DrawGui(deltaTime, totalTime); DrawGameTexture( Renderer.GetWhiteTexture(), new FloatRect(0, 0, 1, 1), CurrentView, diff --git a/Intersect.Client.Core/Core/Input.cs b/Intersect.Client.Core/Core/Input.cs index 1e7a6783fb..b1687064c0 100644 --- a/Intersect.Client.Core/Core/Input.cs +++ b/Intersect.Client.Core/Core/Input.cs @@ -17,6 +17,8 @@ namespace Intersect.Client.Core; +public delegate bool MouseButtonEventInterceptor(Keys modifier, MouseButton mouseButton); + public static partial class Input { public delegate void HandleKeyEvent(Keys modifier, Keys key); @@ -82,9 +84,10 @@ public static void OnKeyPressed(Keys modifier, Keys key) return; case Keys.Enter: - for (int i = Interface.Interface.InputBlockingComponents.Count - 1; i >= 0; i--) + var components = Interface.Interface.InputBlockingComponents.ToArray(); + for (int i = components.Length - 1; i >= 0; i--) { - var inputBlockingComponent = Interface.Interface.InputBlockingComponents[i]; + var inputBlockingComponent = components[i]; try { if (inputBlockingComponent is InputBox { IsHidden: false } inputBox) @@ -229,10 +232,14 @@ public static void OnKeyPressed(Keys modifier, Keys key) switch (control) { case Control.Enter: - if (!selectCharacterWindow.IsHidden && selectCharacterWindow.Characters[selectCharacterWindow.mSelectedChar] != null) + if (selectCharacterWindow is { IsHidden: false, CharacterSelectionPreviews: { } previews }) { - selectCharacterWindow.ButtonPlay_Clicked(null, null); - consumeKey = true; + var selectedPreviewIndex = selectCharacterWindow._selectedCharacterIndex; + if (previews.Length > selectedPreviewIndex && previews[selectedPreviewIndex] != default) + { + selectCharacterWindow.ButtonPlay_Clicked(null, null); + consumeKey = true; + } } break; } @@ -356,10 +363,31 @@ public static void OnKeyReleased(Keys modifier, Keys key) } } - public static void OnMouseDown(Keys modifier, MouseButton btn) + public static event MouseButtonEventInterceptor? MouseDownIntercept; + + public static event MouseButtonEventInterceptor? MouseUpIntercept; + + private static bool InvokeMouseButtonInterceptors( + MulticastDelegate? multicastDelegate, + Keys modifier, + MouseButton mouseButton + ) + { + var rawInvocationList = multicastDelegate?.GetInvocationList() ?? []; + var invocationList = rawInvocationList.OfType().ToArray(); + return invocationList.Any(interceptor => interceptor(modifier, mouseButton)); + } + + public static bool TestInterceptMouse(Keys modifier, MouseButton mouseButton, bool down) + { + return modifier != Keys.Alt && + InvokeMouseButtonInterceptors(down ? MouseDownIntercept : MouseUpIntercept, modifier, mouseButton); + } + + public static void OnMouseDown(Keys modifier, MouseButton mouseButton) { var key = Keys.None; - switch (btn) + switch (mouseButton) { case MouseButton.Left: key = Keys.LButton; @@ -406,7 +434,7 @@ public static void OnMouseDown(Keys modifier, MouseButton btn) return; } - if (modifier == Keys.None && btn == MouseButton.Left && Globals.Me.TryTarget()) + if (modifier == Keys.None && mouseButton == MouseButton.Left && Globals.Me.TryTarget()) { return; } @@ -440,10 +468,10 @@ public static void OnMouseDown(Keys modifier, MouseButton btn) } } - public static void OnMouseUp(Keys modifier, MouseButton btn) + public static void OnMouseUp(Keys modifier, MouseButton mouseButton) { var key = Keys.LButton; - switch (btn) + switch (mouseButton) { case MouseButton.Right: key = Keys.RButton; @@ -485,7 +513,7 @@ public static void OnMouseUp(Keys modifier, MouseButton btn) return; } - if (btn != MouseButton.Right) + if (mouseButton != MouseButton.Right) { return; } diff --git a/Intersect.Client.Core/Core/Main.cs b/Intersect.Client.Core/Core/Main.cs index a727e85057..6ee7b02392 100644 --- a/Intersect.Client.Core/Core/Main.cs +++ b/Intersect.Client.Core/Core/Main.cs @@ -98,6 +98,8 @@ public static void Update(TimeSpan deltaTime) Globals.InputManager.Update(deltaTime); Audio.Update(); + Time.Update(); + Globals.OnGameUpdate(deltaTime); } } @@ -267,7 +269,6 @@ private static void ProcessGame() } Graphics.UpdatePlayerLight(); - Time.Update(); } public static void JoinGame() diff --git a/Intersect.Client.Core/Entities/Entity.cs b/Intersect.Client.Core/Entities/Entity.cs index 4c9e329f0c..1b78ba16aa 100644 --- a/Intersect.Client.Core/Entities/Entity.cs +++ b/Intersect.Client.Core/Entities/Entity.cs @@ -106,7 +106,7 @@ public Guid[] Equipment public Guid Id { get; set; } //Inventory/Spells/Equipment - public IItem[] Inventory { get; set; } = new IItem[Options.Instance.Player.MaxInventory]; + public IItem[] Inventory { get; } = new IItem[Options.Instance.Player.MaxInventory]; IReadOnlyList IEntity.Items => [.. Inventory]; diff --git a/Intersect.Client.Core/Entities/Events/Dialog.cs b/Intersect.Client.Core/Entities/Events/Dialog.cs index 5fdffee620..9d78b2d59c 100644 --- a/Intersect.Client.Core/Entities/Events/Dialog.cs +++ b/Intersect.Client.Core/Entities/Events/Dialog.cs @@ -4,19 +4,11 @@ public partial class Dialog { public Guid EventId; - public string Face = string.Empty; + public string? Face; - public string Opt1 = string.Empty; + public string[] Options = []; - public string Opt2 = string.Empty; + public string? Prompt; - public string Opt3 = string.Empty; - - public string Opt4 = string.Empty; - - public string Prompt = string.Empty; - - public int ResponseSent; - - public int Type; + public bool ResponseSent; } diff --git a/Intersect.Client.Core/Entities/Player.cs b/Intersect.Client.Core/Entities/Player.cs index cb600f340d..04277a2146 100644 --- a/Intersect.Client.Core/Entities/Player.cs +++ b/Intersect.Client.Core/Entities/Player.cs @@ -4,6 +4,9 @@ using Intersect.Client.Entities.Projectiles; using Intersect.Client.Framework.Entities; using Intersect.Client.Framework.GenericClasses; +using Intersect.Client.Framework.Gwen.Control; +using Intersect.Client.Framework.Gwen.Control.EventArguments; +using Intersect.Client.Framework.Gwen.Control.EventArguments.InputSubmissionEvent; using Intersect.Client.Framework.Input; using Intersect.Client.Framework.Items; using Intersect.Client.General; @@ -30,7 +33,7 @@ namespace Intersect.Client.Entities; public partial class Player : Entity, IPlayer { - public delegate void InventoryUpdated(); + public delegate void InventoryUpdatedEventHandler(Player player, int slotIndex); private Guid _class; @@ -63,7 +66,19 @@ public Guid Class public HotbarInstance[] Hotbar { get; set; } = new HotbarInstance[Options.Instance.Player.HotbarSlotCount]; - public InventoryUpdated? InventoryUpdatedDelegate { get; set; } + public event InventoryUpdatedEventHandler? InventoryUpdated; + + public void UpdateInventory( + int slotIndex, + Guid descriptorId, + int quantity, + Guid? bagId, + ItemProperties itemProperties + ) + { + Inventory[slotIndex].Load(id: descriptorId, quantity: quantity, bagId: bagId, itemProperties: itemProperties); + InventoryUpdated?.Invoke(this, slotIndex); + } IReadOnlyDictionary IPlayer.ItemCooldowns => ItemCooldowns; @@ -396,23 +411,28 @@ public void TryDropItem(int inventorySlotIndex) var quantity = inventorySlot.Quantity; var canDropMultiple = quantity > 1; - var inputType = canDropMultiple ? InputBox.InputType.NumericSliderInput : InputBox.InputType.YesNo; + var inputType = canDropMultiple ? InputType.NumericSliderInput : InputType.YesNo; var prompt = canDropMultiple ? Strings.Inventory.DropItemPrompt : Strings.Inventory.DropPrompt; _ = new InputBox( title: Strings.Inventory.DropItemTitle, prompt: prompt.ToString(itemDescriptor.Name), inputType: inputType, quantity: quantity, - maxQuantity: GetQuantityOfItemInInventory(itemDescriptor.Id), + maximumQuantity: GetQuantityOfItemInInventory(itemDescriptor.Id), userData: inventorySlotIndex, - onSuccess: (s, e) => + onSubmit: (sender, args) => { - if (s is not InputBox inputBox || inputBox.UserData is not int invSlot) + if (sender is not InputBox { UserData: int slotIndex }) { return; } - var value = (int)Math.Round(inputBox.Value); + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); if (value <= 0) { @@ -422,7 +442,7 @@ public void TryDropItem(int inventorySlotIndex) // Check if the item can be dropped in multiple quantities or if value is less than or equal to the quantity in the initial slot if (!canDropMultiple || value <= quantity) { - PacketSender.SendDropItem(invSlot, !canDropMultiple ? 1 : value); + PacketSender.SendDropItem(slotIndex, !canDropMultiple ? 1 : value); return; } @@ -430,7 +450,7 @@ public void TryDropItem(int inventorySlotIndex) var itemSlots = Inventory.Where(s => s.ItemId == itemDescriptor.Id).ToList(); // Send the drop item packet for the initial slot. - PacketSender.SendDropItem(invSlot, quantity); + PacketSender.SendDropItem(slotIndex, quantity); value -= quantity; _ = itemSlots.Remove(inventorySlot); // Remove the initial slot from the list of item slots @@ -682,13 +702,15 @@ public void TrySellItem(int inventorySlotIndex) || shop?.BuyingWhitelist == false && !shop.BuyingItems.Any(buyingItem => buyingItem.ItemId == itemDescriptor.Id); var prompt = Strings.Shop.SellPrompt; - var inputType = InputBox.InputType.YesNo; - EventHandler? onSuccess = (s, e) => + var inputType = InputType.YesNo; + Base.GwenEventHandler? onSuccess = (sender, args) => { - if (s is InputBox inputBox && inputBox.UserData is int invSlot) + if (sender is not InputBox { UserData: int slotIndex }) { - PacketSender.SendSellItem(invSlot, 1); + return; } + + PacketSender.SendSellItem(slotIndex, 1); }; var userData = inventorySlotIndex; var slotQuantity = inventorySlot.Quantity; @@ -697,7 +719,7 @@ public void TrySellItem(int inventorySlotIndex) if (!shopCanBuyItem) { prompt = Strings.Shop.CannotSell; - inputType = InputBox.InputType.OkayOnly; + inputType = InputType.Okay; onSuccess = null; userData = -1; } @@ -708,18 +730,8 @@ public void TrySellItem(int inventorySlotIndex) { maxQuantity = inventoryQuantity; prompt = Strings.Shop.SellItemPrompt; - inputType = InputBox.InputType.NumericSliderInput; - onSuccess = (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is int invSlot) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - PacketSender.SendSellItem(invSlot, value); - } - } - }; + inputType = InputType.NumericSliderInput; + onSuccess = TrySellItemOnSubmit; userData = inventorySlotIndex; } } @@ -729,12 +741,31 @@ public void TrySellItem(int inventorySlotIndex) prompt: prompt.ToString(itemDescriptor.Name), inputType: inputType, quantity: slotQuantity, - maxQuantity: maxQuantity, + maximumQuantity: maxQuantity, userData: userData, - onSuccess: onSuccess + onSubmit: onSuccess ); } + private static void TrySellItemOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: int slotIndex }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendSellItem(slotIndex, value); + } + } + public void TryBuyItem(int shopSlotIndex) { //Confirm the purchase @@ -771,24 +802,33 @@ public void TryBuyItem(int shopSlotIndex) _ = new InputBox( title: Strings.Shop.BuyItem, prompt: Strings.Shop.BuyItemPrompt.ToString(itemDescriptor.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: maxBuyAmount, - maxQuantity: maxBuyAmount, + maximumQuantity: maxBuyAmount, userData: shopSlotIndex, - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is int shopSlot) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - PacketSender.SendBuyItem(shopSlot, value); - } - } - } + onSubmit: TryBuyItemOnSubmit ); } + private static void TryBuyItemOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: int slotIndex }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendBuyItem(slotIndex, value); + } + } + /// /// Attempts to deposit the item from the inventory into the bank. /// @@ -798,7 +838,7 @@ public void TryBuyItem(int shopSlotIndex) /// /// /// - public bool TryDepositItem( + public bool TryStoreItemInBank( int inventorySlotIndex, IItem? slot = null, int bankSlotIndex = -1, @@ -881,27 +921,35 @@ public bool TryDepositItem( _ = new InputBox( title: Strings.Bank.DepositItem, prompt: Strings.Bank.DepositItemPrompt.ToString(itemDescriptor.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: movableQuantity, - maxQuantity: maximumQuantity, + maximumQuantity: maximumQuantity, userData: new Tuple(inventorySlotIndex, bankSlotIndex), - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is Tuple bankData) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - var (invSlot, bankSlot) = bankData; - PacketSender.SendDepositItem(invSlot, value, bankSlot); - } - } - } + onSubmit: TryDepositItemOnSubmitted ); return true; } + private static void TryDepositItemOnSubmitted(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: (int inventorySlotIndex, int bankSlotIndex) }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendDepositItem(inventorySlotIndex, value, bankSlotIndex); + } + } + private static bool IsGuildBankDepositAllowed() { return !string.IsNullOrWhiteSpace(Globals.Me?.Guild) && @@ -917,7 +965,7 @@ private static bool IsGuildBankDepositAllowed() /// /// /// - public bool TryWithdrawItem( + public bool TryRetrieveItemFromBank( int bankSlotIndex, IItem? slot = null, int inventorySlotIndex = -1, @@ -999,27 +1047,35 @@ public bool TryWithdrawItem( _ = new InputBox( title: Strings.Bank.WithdrawItem, prompt: Strings.Bank.WithdrawItemPrompt.ToString(itemDescriptor.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: movableQuantity, - maxQuantity: maximumQuantity, + maximumQuantity: maximumQuantity, userData: new Tuple(bankSlotIndex, inventorySlotIndex), - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is Tuple bankData) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - var (bankSlot, invSlot) = bankData; - PacketSender.SendWithdrawItem(bankSlot, value, invSlot); - } - } - } + onSubmit: TryWithdrawItemOnSubmitted ); return true; } + private static void TryWithdrawItemOnSubmitted(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: (int bankSlotIndex, int inventorySlotIndex) }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendWithdrawItem(bankSlotIndex, value, inventorySlotIndex); + } + } + private static bool IsGuildBankWithdrawAllowed() { return !string.IsNullOrWhiteSpace(Globals.Me?.Guild) && @@ -1027,7 +1083,7 @@ private static bool IsGuildBankWithdrawAllowed() } //Bag - public void TryStoreBagItem(int inventorySlotIndex, int bagSlotIndex) + public void TryStoreItemInBag(int inventorySlotIndex, int bagSlotIndex) { var inventorySlot = Inventory[inventorySlotIndex]; if (!ItemBase.TryGet(inventorySlot.ItemId, out var itemDescriptor)) @@ -1047,26 +1103,34 @@ public void TryStoreBagItem(int inventorySlotIndex, int bagSlotIndex) _ = new InputBox( title: Strings.Bags.StoreItem, prompt: Strings.Bags.StoreItemPrompt.ToString(itemDescriptor.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: quantity, - maxQuantity: maxQuantity, + maximumQuantity: maxQuantity, userData: new Tuple(inventorySlotIndex, bagSlotIndex), - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is Tuple bagData) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - var (invSlot, bagSlot) = bagData; - PacketSender.SendStoreBagItem(invSlot, value, bagSlot); - } - } - } + onSubmit: TryStoreItemInBagOnSubmit ); } - public void TryRetreiveBagItem(int bagSlotIndex, int inventorySlotIndex) + private static void TryStoreItemInBagOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: (int inventorySlotIndex, int bagSlotIndex) }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendStoreBagItem(inventorySlotIndex, value, bagSlotIndex); + } + } + + public void TryRetrieveItemFromBag(int bagSlotIndex, int inventorySlotIndex) { var bagSlot = Globals.Bag[bagSlotIndex]; if (bagSlot == default) @@ -1091,27 +1155,35 @@ public void TryRetreiveBagItem(int bagSlotIndex, int inventorySlotIndex) _ = new InputBox( title: Strings.Bags.RetrieveItem, prompt: Strings.Bags.RetrieveItemPrompt.ToString(itemDescriptor.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: quantity, - maxQuantity: maxQuantity, + maximumQuantity: maxQuantity, userData: new Tuple(bagSlotIndex, inventorySlotIndex), - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is Tuple bagData) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - var (bagSlot, invSlot) = bagData; - PacketSender.SendRetrieveBagItem(bagSlot, value, invSlot); - } - } - } + onSubmit: TryRetrieveItemFromBagOnSubmit ); } + private static void TryRetrieveItemFromBagOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: (int bagSlotIndex, int inventorySlotIndex) }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendRetrieveBagItem(bagSlotIndex, value, inventorySlotIndex); + } + } + //Trade - public void TryTradeItem(int index) + public void TryOfferItemToTrade(int index) { var slot = Inventory[index]; var quantity = slot.Quantity; @@ -1130,25 +1202,34 @@ public void TryTradeItem(int index) _ = new InputBox( title: Strings.Trading.OfferItem, prompt: Strings.Trading.OfferItemPrompt.ToString(tradingItem.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: quantity, - maxQuantity: quantity, + maximumQuantity: quantity, userData: index, - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is int slotIndex) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - PacketSender.SendOfferTradeItem(slotIndex, value); - } - } - } + onSubmit: TryOfferItemToTradeOnSubmit ); } - public void TryRevokeItem(int index) + private static void TryOfferItemToTradeOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: int slotIndex }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendOfferTradeItem(slotIndex, value); + } + } + + public void TryCancelOfferToTradeItem(int index) { var slot = Globals.Trade[0, index]; var quantity = slot.Quantity; @@ -1167,24 +1248,33 @@ public void TryRevokeItem(int index) _ = new InputBox( title: Strings.Trading.RevokeItem, prompt: Strings.Trading.RevokeItemPrompt.ToString(revokedItem.Name), - inputType: InputBox.InputType.NumericSliderInput, + inputType: InputType.NumericSliderInput, quantity: quantity, - maxQuantity: quantity, + maximumQuantity: quantity, userData: index, - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData is int slotIndex) - { - var value = (int)Math.Round(inputBox.Value); - if (value > 0) - { - PacketSender.SendRevokeTradeItem(slotIndex, value); - } - } - } + onSubmit: TryCancelOfferToTradeItemOnSubmit ); } + private static void TryCancelOfferToTradeItemOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: int slotIndex }) + { + return; + } + + if (args.Value is not NumericalSubmissionValue submissionValue) + { + return; + } + + var value = (int)Math.Round(submissionValue.Value); + if (value > 0) + { + PacketSender.SendRevokeTradeItem(slotIndex, value); + } + } + //Spell Processing public void SwapSpells(int fromSpellIndex, int toSpellIndex) { @@ -1200,19 +1290,23 @@ public void TryForgetSpell(int spellIndex) _ = new InputBox( title: Strings.Spells.ForgetSpell, prompt: Strings.Spells.ForgetSpellPrompt.ToString(spellDescriptor.Name), - inputType: InputBox.InputType.YesNo, + inputType: InputType.YesNo, userData: spellIndex, - onSuccess: (s, e) => - { - if (s is InputBox inputBox && inputBox.UserData != default) - { - PacketSender.SendForgetSpell((int)inputBox.UserData); - } - } + onSubmit: TryForgetSpellOnSubmit ); } } + private static void TryForgetSpellOnSubmit(Base sender, InputSubmissionEventArgs args) + { + if (sender is not InputBox { UserData: int slotIndex }) + { + return; + } + + PacketSender.SendForgetSpell(slotIndex); + } + public void TryUseSpell(int index) { if (index < 0 || Spells.Length <= index) diff --git a/Intersect.Client.Core/General/Time.cs b/Intersect.Client.Core/General/Time.cs index c71c72b626..c449d2c3e1 100644 --- a/Intersect.Client.Core/General/Time.cs +++ b/Intersect.Client.Core/General/Time.cs @@ -29,6 +29,12 @@ public static void LoadTime(DateTime timeUpdate, Color clr, float rate) public static void Update() { + if (!Networking.Network.IsConnected) + { + sServerTime = DateTime.Now; + return; + } + if (sUpdateTime < Timing.Global.Milliseconds) { var ts = new TimeSpan(0, 0, 0, 0, (int) (1000 * sRate)); diff --git a/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs b/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs index 92688cebc3..e774c64c5d 100644 --- a/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs +++ b/Intersect.Client.Core/Interface/Debugging/DebugWindow.cs @@ -1,44 +1,55 @@ +using System.Diagnostics; using Intersect.Async; using Intersect.Client.Core; +using Intersect.Client.Entities; using Intersect.Client.Framework.Content; +using Intersect.Client.Framework.File_Management; +using Intersect.Client.Framework.GenericClasses; using Intersect.Client.Framework.Graphics; using Intersect.Client.Framework.Gwen; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Gwen.Control.Data; using Intersect.Client.Framework.Gwen.Control.Layout; using Intersect.Client.Framework.Gwen.Control.Utility; +using Intersect.Client.Framework.Input; using Intersect.Client.General; +using Intersect.Client.Interface.Data; +using Intersect.Client.Interface.Debugging.Providers; using Intersect.Client.Localization; using Intersect.Client.Maps; -using Intersect.Extensions; +using Intersect.Framework.Reflection; using static Intersect.Client.Framework.File_Management.GameContentManager; namespace Intersect.Client.Interface.Debugging; internal sealed partial class DebugWindow : Window { - private readonly List _generators; + private const string NoValue = "-"; + private readonly GameFont? _defaultFont; private bool _wasParentDrawDebugOutlinesEnabled; private bool _drawDebugOutlinesEnabled; + private bool _viewClickedNodeInDebugger; + private readonly NodeUnderCursorProvider _nodeUnderCursorProvider = new(); public DebugWindow(Base parent) : base(parent, Strings.Debug.Title, false, nameof(DebugWindow)) { - _generators = []; - DisableResizing(); - MinimumSize = new Point(320, 320); - Size = new Point(400, 600); - MaximumSize = new Point(800, 600); + _defaultFont = Current?.GetFont("sourcesansproblack", 10); + IsHidden = true; + IsResizable = false; InnerPanelPadding = new Padding(4); - - _defaultFont = Current?.GetFont("sourcesansproblack", 10); + MinimumSize = new Point(360, 320); + Size = new Point(480, 600); + MaximumSize = new Point(800, 600); Tabs = CreateTabs(); TabInfo = Tabs.AddPage(Strings.Debug.TabLabelInfo, nameof(TabInfo)); CheckboxDrawDebugOutlines = CreateInfoCheckboxDrawDebugOutlines(TabInfo.Page); CheckboxEnableLayoutHotReloading = CreateInfoCheckboxEnableLayoutHotReloading(TabInfo.Page); + CheckboxIncludeTextNodesInHover = CreateInfoCheckboxIncludeTextNodesInHover(TabInfo.Page); + CheckboxViewClickedComponentInDebugger = CreateInfoCheckboxViewClickedNodeInDebugger(TabInfo.Page); ButtonShutdownServer = CreateInfoButtonShutdownServer(TabInfo.Page); ButtonShutdownServerAndExit = CreateInfoButtonShutdownServerAndExit(TabInfo.Page); TableDebugStats = CreateInfoTableDebugStats(TabInfo.Page); @@ -49,8 +60,6 @@ public DebugWindow(Base parent) : base(parent, Strings.Debug.Title, false, nameo AssetsButtonReloadAsset = CreateAssetsButtonReloadAsset(AssetsToolsTable, AssetsList); AssetsToolsTable.SizeToChildren(); - - IsHidden = true; } private SearchableTree CreateAssetsList(Base parent) @@ -144,6 +153,10 @@ private Button CreateAssetsButtonReloadAsset(Table table, SearchableTree assetLi private LabeledCheckBox CheckboxEnableLayoutHotReloading { get; } + private LabeledCheckBox CheckboxIncludeTextNodesInHover { get; } + + private LabeledCheckBox CheckboxViewClickedComponentInDebugger { get; } + private Button ButtonShutdownServer { get; } private Button ButtonShutdownServerAndExit { get; } @@ -152,12 +165,9 @@ private Button CreateAssetsButtonReloadAsset(Table table, SearchableTree assetLi protected override void EnsureInitialized() { - // LoadJsonUi(UI.Debug, Graphics.Renderer?.GetResolutionString()); + TableDebugStats.SizeToChildren(); - foreach (var generator in _generators) - { - generator.Start(); - } + LoadJsonUi(UI.Debug, Graphics.Renderer?.GetResolutionString()); } protected override void OnAttached(Base parent) @@ -173,27 +183,12 @@ protected override void OnAttaching(Base newParent) _wasParentDrawDebugOutlinesEnabled = newParent.DrawDebugOutlines; } - protected override void OnDetached() - { - base.OnDetached(); - } - protected override void OnDetaching(Base oldParent) { base.OnDetaching(oldParent); oldParent.DrawDebugOutlines = _wasParentDrawDebugOutlinesEnabled; } - public override void Dispose() - { - foreach (var generator in _generators) - { - generator.Dispose(); - } - - base.Dispose(); - } - private TabControl CreateTabs() { return new TabControl(this, name: "DebugTabs") @@ -237,7 +232,7 @@ private LabeledCheckBox CreateInfoCheckboxEnableLayoutHotReloading(Base parent) TextColorOverride = Color.Yellow, }; - checkbox.CheckChanged += (sender, args) => Globals.ContentManager.ContentWatcher.Enabled = checkbox.IsChecked; + checkbox.CheckChanged += (_, _) => Globals.ContentManager.ContentWatcher.Enabled = checkbox.IsChecked; checkbox.SetToolTipText(Strings.Internals.ExperimentalFeatureTooltip); checkbox.TooltipFont = Skin.DefaultFont; @@ -245,6 +240,94 @@ private LabeledCheckBox CreateInfoCheckboxEnableLayoutHotReloading(Base parent) return checkbox; } + private LabeledCheckBox CreateInfoCheckboxIncludeTextNodesInHover(Base parent) + { + var checkbox = new LabeledCheckBox(parent, nameof(CheckboxIncludeTextNodesInHover)) + { + Dock = Pos.Top, + Font = _defaultFont, + IsChecked = _nodeUnderCursorProvider.Filter.HasFlag(NodeFilter.IncludeText), + Text = Strings.Debug.IncludeTextNodesInHover, + }; + + checkbox.CheckChanged += (_, _) => + { + if (_nodeUnderCursorProvider.Filter.HasFlag(NodeFilter.IncludeText)) + { + _nodeUnderCursorProvider.Filter &= ~NodeFilter.IncludeText; + } + else + { + _nodeUnderCursorProvider.Filter |= NodeFilter.IncludeText; + } + }; + + return checkbox; + } + + private LabeledCheckBox CreateInfoCheckboxViewClickedNodeInDebugger(Base parent) + { + var checkbox = new LabeledCheckBox(parent, nameof(CheckboxViewClickedComponentInDebugger)) + { + Dock = Pos.Top, + Font = _defaultFont, + IsChecked = _viewClickedNodeInDebugger, + Text = Strings.Debug.ViewClickedNodeInDebugger, + TooltipText = Strings.Debug.ViewClickedNodeInDebuggerTooltip, + }; + + checkbox.CheckChanged += (_, _) => + { + _viewClickedNodeInDebugger = !_viewClickedNodeInDebugger; + if (_viewClickedNodeInDebugger) + { + AddIntercepts(); + } + else + { + RemoveIntercepts(); + } + }; + + return checkbox; + } + + private void AddIntercepts() + { + Input.MouseDownIntercept += MouseDownIntercept; + Input.MouseUpIntercept += MouseUpIntercept; + } + + private void RemoveIntercepts() + { + Input.MouseDownIntercept -= MouseDownIntercept; + Input.MouseUpIntercept -= MouseUpIntercept; + } + + private bool MouseDownIntercept(Keys modifier, MouseButton mouseButton) + { + if (IsVisible && _viewClickedNodeInDebugger) + { + return true; + } + + RemoveIntercepts(); + return false; + } + + private bool MouseUpIntercept(Keys modifier, MouseButton mouseButton) + { + if (!IsVisible || !_viewClickedNodeInDebugger) + { + RemoveIntercepts(); + return false; + } + + var node = GetNodeUnderCursor(); + Debugger.Break(); + return true; + } + private Button CreateInfoButtonShutdownServer(Base parent) { var button = new Button(parent, nameof(ButtonShutdownServer)) @@ -255,7 +338,7 @@ private Button CreateInfoButtonShutdownServer(Base parent) Text = Strings.Debug.ShutdownServer, }; - button.Clicked += (sender, args) => + button.Clicked += (_, _) => { // TODO: Implement }; @@ -273,7 +356,7 @@ private Button CreateInfoButtonShutdownServerAndExit(Base parent) Text = Strings.Debug.ShutdownServerAndExit, }; - button.Clicked += (sender, args) => + button.Clicked += (_, _) => { // TODO: Implement }; @@ -296,257 +379,77 @@ private Table CreateInfoTableDebugStats(Base parent) AutoSizeToContentWidthOnChildResize = true, CellSpacing = new Point(8, 2), ColumnCount = 2, - ColumnWidths = [150, null], + ColumnWidths = [180, null], Dock = Pos.Fill, Font = _defaultFont, }; - table.BoundsChanged += (_, _) => table.SizeToChildren(width: false, height: true); - - var fpsProvider = new ValueTableCellDataProvider(() => Graphics.Renderer?.GetFps() ?? 0, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.Fps, name: "FPSRow").Listen(fpsProvider, 1); - _generators.Add(fpsProvider.Generator); - - var pingProvider = new ValueTableCellDataProvider(() => Networking.Network.Ping, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.Ping, name: "PingRow").Listen(pingProvider, 1); - _generators.Add(pingProvider.Generator); - - var drawCallsProvider = new ValueTableCellDataProvider(() => Graphics.DrawCalls, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.Draws, name: "DrawsRow").Listen(drawCallsProvider, 1); - _generators.Add(drawCallsProvider.Generator); - - var mapNameProvider = new ValueTableCellDataProvider(() => + table.SizeChanged += (_, args) => { - var mapId = Globals.Me?.MapId ?? default; - - if (mapId == default) - { - return Strings.Internals.NotApplicable; - } - - return MapInstance.Get(mapId)?.Name ?? Strings.Internals.NotApplicable; - }, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.Map, name: "MapRow").Listen(mapNameProvider, 1); - _generators.Add(mapNameProvider.Generator); - - var coordinateXProvider = new ValueTableCellDataProvider(() => Globals.Me?.X ?? -1, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Internals.CoordinateX, name: "PlayerXRow").Listen(coordinateXProvider, 1); - _generators.Add(coordinateXProvider.Generator); - - var coordinateYProvider = new ValueTableCellDataProvider(() => Globals.Me?.Y ?? -1, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Internals.CoordinateY, name: "PlayerYRow").Listen(coordinateYProvider, 1); - _generators.Add(coordinateYProvider.Generator); - - var coordinateZProvider = new ValueTableCellDataProvider(() => Globals.Me?.Z ?? -1, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Internals.CoordinateZ, name: "PlayerZRow").Listen(coordinateZProvider, 1); - _generators.Add(coordinateZProvider.Generator); - - var knownEntitiesProvider = new ValueTableCellDataProvider(() => Graphics.DrawCalls, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.KnownEntities, name: "KnownEntitiesRow").Listen(knownEntitiesProvider, 1); - _generators.Add(knownEntitiesProvider.Generator); - - var knownMapsProvider = new ValueTableCellDataProvider(() => MapInstance.Lookup.Count, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.KnownMaps, name: "KnownMapsRow").Listen(knownMapsProvider, 1); - _generators.Add(knownMapsProvider.Generator); - - var mapsDrawnProvider = new ValueTableCellDataProvider(() => Graphics.MapsDrawn, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.MapsDrawn, name: "MapsDrawnRow").Listen(mapsDrawnProvider, 1); - _generators.Add(mapsDrawnProvider.Generator); - - var entitiesDrawnProvider = new ValueTableCellDataProvider(() => Graphics.EntitiesDrawn, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.EntitiesDrawn, name: "EntitiesDrawnRow").Listen(entitiesDrawnProvider, 1); - _generators.Add(entitiesDrawnProvider.Generator); - - var lightsDrawnProvider = new ValueTableCellDataProvider(() => Graphics.LightsDrawn, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.LightsDrawn, name: "LightsDrawnRow").Listen(lightsDrawnProvider, 1); - _generators.Add(lightsDrawnProvider.Generator); + table.SizeToChildren(resizeX: args.Value.X > args.OldValue.X, resizeY: true, recursive: true); + }; - var timeProvider = new ValueTableCellDataProvider(Time.GetTime, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.Time, name: "TimeRow").Listen(timeProvider, 1); - _generators.Add(timeProvider.Generator); - - var interfaceObjectsProvider = new ValueTableCellDataProvider(cancellationToken => - { - try - { - return Interface.CurrentInterface?.Children.ToArray().SelectManyRecursive( - x => x != default ? [.. x.Children] : [], - cancellationToken - ).ToArray().Length ?? 0; - } - catch (ObjectDisposedException) - { - throw; - } - catch (InvalidOperationException) - { - return 0; - } - }, waitPredicate: () => Task.FromResult(IsVisible)); - table.AddRow(Strings.Debug.InterfaceObjects, name: "InterfaceObjectsRow").Listen(interfaceObjectsProvider, 1); - _generators.Add(interfaceObjectsProvider.Generator); - - _ = table.AddRow(Strings.Debug.ControlUnderCursor, columnCount: 2, name: "ControlUnderCursorRow"); - - var controlUnderCursorProvider = new ControlUnderCursorProvider(this); - // var controlUnderCursorProvider = new ValueTableCellDataProvider( - // Interface.FindControlAtCursor, - // waitPredicate: () => Task.FromResult(IsVisible) - // ); - table.AddRow(Strings.Internals.Type, name: "TypeRow").Listen(controlUnderCursorProvider, 0); - table.AddRow(Strings.Internals.Name, name: "NameRow").Listen(controlUnderCursorProvider, 1); - table.AddRow(Strings.Internals.LocalItem.ToString(Strings.Internals.Bounds), name: "LocalBoundsRow").Listen(controlUnderCursorProvider, 2); - table.AddRow(Strings.Internals.GlobalItem.ToString(Strings.Internals.Bounds), name: "GlobalBoundsRow").Listen(controlUnderCursorProvider, 3); - table.AddRow(Strings.Internals.Color, name: "ColorRow").Listen(controlUnderCursorProvider, 4); - table.AddRow(Strings.Internals.ColorOverride, name: "ColorOverrideRow").Listen(controlUnderCursorProvider, 5); - table.AddRow(Strings.Internals.InnerBounds, name: "InnerBoundsRow").Listen(controlUnderCursorProvider, 6); - table.AddRow(Strings.Internals.Margin, name: "MarginRow").Listen(controlUnderCursorProvider, 7); - table.AddRow(Strings.Internals.Padding, name: "PaddingRow").Listen(controlUnderCursorProvider, 8); - table.AddRow(Strings.Internals.Dock, name: "Dock").Listen(controlUnderCursorProvider, 9); - table.AddRow(Strings.Internals.Alignment, name: "Alignment").Listen(controlUnderCursorProvider, 10); - table.AddRow(Strings.Internals.TextAlign, name: "TextAlign").Listen(controlUnderCursorProvider, 11); - table.AddRow(Strings.Internals.TextPadding, name: "TextPadding").Listen(controlUnderCursorProvider, 12); - table.AddRow(Strings.Internals.Font, name: "Font").Listen(controlUnderCursorProvider, 13); - table.AddRow(Strings.Internals.AutoSizeToContents, name: "AutoSizeToContents").Listen(controlUnderCursorProvider, 14); - _generators.Add(controlUnderCursorProvider.Generator); + table.AddRow(Strings.Debug.Fps, name: "FPSRow").Listen(1, new DelegateDataProvider(() => Graphics.Renderer?.Fps), NoValue); + // table.AddRow(Strings.Debug.Draws, name: "DrawsRow").Listen(1, new DelegateDataProvider(() => Graphics.DrawCalls), NoValue); + table.AddRow(Strings.Debug.Ping, name: "PingRow").Listen(1, new DelegateDataProvider(() => Networking.Network.Ping, delayMilliseconds: 5000), NoValue); + + table.AddRow(Strings.Debug.Map, name: "MapRow").Listen(1, new DelegateDataProvider(() => MapInstance.TryGet(Globals.Me?.MapId ?? default, out var mapInstance) ? mapInstance.Name : default), NoValue); + table.AddRow(Strings.Internals.CoordinateX, name: "PlayerXRow").Listen(1, new DelegateDataProvider(() => Globals.Me?.X), NoValue); + table.AddRow(Strings.Internals.CoordinateY, name: "PlayerYRow").Listen(1, new DelegateDataProvider(() => Globals.Me?.Y), NoValue); + table.AddRow(Strings.Internals.CoordinateZ, name: "PlayerZRow").Listen(1, new DelegateDataProvider(() => Globals.Me?.Z), NoValue); + + table.AddRow(Strings.Debug.Time, name: "TimeRow").Listen(1, new DelegateDataProvider(Time.GetTime), NoValue); + // table.AddRow(Strings.Debug.KnownEntities, name: "KnownEntitiesRow").Listen(1, new DelegateDataProvider(() => 0), NoValue); + table.AddRow(Strings.Debug.KnownMaps, name: "KnownMapsRow").Listen(1, new DelegateDataProvider(() => MapInstance.Lookup.Count), NoValue); + table.AddRow(Strings.Debug.MapsDrawn, name: "MapsDrawnRow").Listen(1, new DelegateDataProvider(() => Graphics.MapsDrawn), NoValue); + table.AddRow(Strings.Debug.EntitiesDrawn, name: "EntitiesDrawnRow").Listen(1, new DelegateDataProvider(() => Graphics.EntitiesDrawn), NoValue); + table.AddRow(Strings.Debug.LightsDrawn, name: "LightsDrawnRow").Listen(1, new DelegateDataProvider(() => Graphics.LightsDrawn), NoValue); + table.AddRow(Strings.Debug.InterfaceObjects, name: "InterfaceObjectsRow").Listen(1, new DelegateDataProvider(() => Interface.CurrentInterface?.NodeCount, delayMilliseconds: 1000), NoValue); + + var titleRow = table.AddRow(Strings.Debug.ControlUnderCursor, columnCount: 2, name: "ControlUnderCursorRow", columnIndex: 1); + + table.AddRow(Strings.Internals.Type, name: "TypeRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.GetType().GetName(), Strings.Internals.NotApplicable); + table.AddRow(Strings.Internals.Name, name: "NameRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.CanonicalName, NoValue); + table.AddRow(Strings.Internals.IsVisible, name: "IsVisible").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.IsVisible, NoValue); + table.AddRow(Strings.Internals.IsVisibleInParent, name: "IsVisibleInParent").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.IsVisibleInParent, NoValue); + table.AddRow(Strings.Internals.IsDisabled, name: "IsDisabled").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.IsDisabled, NoValue); + table.AddRow(Strings.Internals.IsDisabledByTree, name: "IsDisabledByTree").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.IsDisabledByTree, NoValue); + table.AddRow(Strings.Internals.LocalItem.ToString(Strings.Internals.Bounds), name: "LocalBoundsRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.Bounds, NoValue); + table.AddRow(Strings.Internals.GlobalItem.ToString(Strings.Internals.Bounds), name: "GlobalBoundsRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.GlobalBounds, NoValue); + table.AddRow(Strings.Internals.InnerBounds, name: "InnerBoundsRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.InnerBounds, NoValue); + table.AddRow(Strings.Internals.OuterBounds, name: "OuterBounds").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.OuterBounds, NoValue); + table.AddRow(Strings.Internals.Margin, name: "MarginRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.Margin, NoValue); + table.AddRow(Strings.Internals.Padding, name: "PaddingRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.Padding, NoValue); + table.AddRow(Strings.Internals.Dock, name: "Dock").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.Dock, NoValue); + table.AddRow(Strings.Internals.Alignment, name: "Alignment").Listen(1, _nodeUnderCursorProvider, (node, _) => node is not { Alignment.Length: >0 } ? null : string.Join(", ", node.Alignment), NoValue); + table.AddRow(Strings.Internals.Color, name: "ColorRow").Listen(1, _nodeUnderCursorProvider, (node, _) => (node as IColorableText)?.TextColor, NoValue); + table.AddRow(Strings.Internals.ColorOverride, name: "ColorOverrideRow").Listen(1, _nodeUnderCursorProvider, (node, _) => (node as IColorableText)?.TextColorOverride, NoValue); + table.AddRow(Strings.Internals.TextAlign, name: "TextAlign").Listen(1, _nodeUnderCursorProvider, (node, _) => (node as Label)?.TextAlign, NoValue); + table.AddRow(Strings.Internals.Font, name: "Font").Listen(1, _nodeUnderCursorProvider, (node, _) => (node as Label)?.FontName, NoValue); + table.AddRow(Strings.Internals.FontSize, name: "FontSize").Listen(1, _nodeUnderCursorProvider, (node, _) => (node as Label)?.FontSize, NoValue); + table.AddRow(Strings.Internals.AutoSizeToContents, name: nameof(IAutoSizeToContents.AutoSizeToContents)).Listen(1, _nodeUnderCursorProvider, (node, _) => (node as IAutoSizeToContents)?.AutoSizeToContents, NoValue); + table.AddRow(Strings.Internals.AutoSizeToContentWidth, name: nameof(ISmartAutoSizeToContents.AutoSizeToContentWidth)).Listen(1, _nodeUnderCursorProvider, (node, _) => (node as ISmartAutoSizeToContents)?.AutoSizeToContentWidth, NoValue); + table.AddRow(Strings.Internals.AutoSizeToContentHeight, name: nameof(ISmartAutoSizeToContents.AutoSizeToContentHeight)).Listen(1, _nodeUnderCursorProvider, (node, _) => (node as ISmartAutoSizeToContents)?.AutoSizeToContentHeight, NoValue); + table.AddRow(Strings.Internals.AutoSizeToContentWidthOnChildResize, name: nameof(ISmartAutoSizeToContents.AutoSizeToContentWidthOnChildResize)).Listen(1, _nodeUnderCursorProvider, (node, _) => (node as ISmartAutoSizeToContents)?.AutoSizeToContentWidthOnChildResize, NoValue); + table.AddRow(Strings.Internals.AutoSizeToContentHeightOnChildResize, name: nameof(ISmartAutoSizeToContents.AutoSizeToContentHeightOnChildResize)).Listen(1, _nodeUnderCursorProvider, (node, _) => (node as ISmartAutoSizeToContents)?.AutoSizeToContentHeightOnChildResize, NoValue); var rows = table.Children.OfType().ToArray(); foreach (var row in rows) { - if (row.GetCellContents(0) is Label label) + if (row == titleRow && row.GetCellContents(1) is Label titleLabel) { - label.TextAlign = Pos.Right | Pos.CenterV; + titleLabel.Padding = titleLabel.Padding with { Top = 8 }; } - } - - table.SizeToChildren(); - - return table; - } - - private partial class ControlUnderCursorProvider : ITableDataProvider - { - private readonly Base _owner; - public ControlUnderCursorProvider(Base owner) - { - _owner = owner; - Generator = new CancellableGenerator(CreateControlUnderCursorGenerator); - } - - public event TableDataChangedEventHandler? DataChanged; - - public CancellableGenerator Generator { get; } - - public void Start() - { - _ = Generator; - } - - private AsyncValueGenerator CreateControlUnderCursorGenerator(CancellationToken cancellationToken) => new( - () => WaitForOwnerVisible(cancellationToken).ContinueWith(CreateValue, TaskScheduler.Current), - HandleValue, - cancellationToken - ); - - private async Task WaitForOwnerVisible(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) + if (row.GetCellContents(0) is not Label label) { - await Task.Delay(100, cancellationToken); - - if (_owner.IsVisible) - { - return; - } - - await Task.Yield(); + continue; } - cancellationToken.ThrowIfCancellationRequested(); + label.TextAlign = Pos.Right | Pos.CenterV; } - private static Base? CreateValue(Task _) => - Interface.FindComponentUnderCursor(ComponentStateFilters.IncludeMouseInputDisabled); - - private void HandleValue(Base? component) - { - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs( - 0, - 1, - default, - component?.GetType().Name ?? Strings.Internals.NotApplicable - ) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(1, 1, default, component?.CanonicalName ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(2, 1, default, component?.Bounds.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(3, 1, default, component?.BoundsGlobal.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs( - 4, - 1, - default, - (component as IColorableText)?.TextColor ?? string.Empty - ) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs( - 5, - 1, - default, - (component as IColorableText)?.TextColorOverride ?? string.Empty - ) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(6, 1, default, component?.InnerBounds.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(7, 1, default, component?.Margin.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(8, 1, default, component?.Padding.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(9, 1, default, component?.Dock.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(10, 1, default, string.Join(", ", component?.Alignment ?? [])) - ); - - var label = component as Label; - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(11, 1, default, label?.TextAlign.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(12, 1, default, label?.TextPadding.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(13, 1, default, label?.Font?.ToString() ?? string.Empty) - ); - DataChanged?.Invoke( - this, - new TableDataChangedEventArgs(14, 1, default, (component as IAutoSizeToContents)?.AutoSizeToContents.ToString() ?? string.Empty) - ); - } + return table; } + + private Base? GetNodeUnderCursor() => Interface.FindComponentUnderCursor(_nodeUnderCursorProvider.Filter); } diff --git a/Intersect.Client.Core/Interface/Debugging/Providers/ActivePlayerProvider.cs b/Intersect.Client.Core/Interface/Debugging/Providers/ActivePlayerProvider.cs new file mode 100644 index 0000000000..689ef6fdb0 --- /dev/null +++ b/Intersect.Client.Core/Interface/Debugging/Providers/ActivePlayerProvider.cs @@ -0,0 +1,16 @@ +using Intersect.Client.Entities; +using Intersect.Client.General; +using Intersect.Client.Interface.Data; + +namespace Intersect.Client.Interface.Debugging.Providers; + +public sealed class ActivePlayerProvider(TimeSpan delay) : DelayedDataProvider(delay) +{ + public ActivePlayerProvider() : this(MinimumDelay) { } + + protected override bool TryDelayedUpdate(TimeSpan elapsed, TimeSpan total) + { + var newValue = Globals.Me; + return TrySetValue(newValue); + } +} \ No newline at end of file diff --git a/Intersect.Client.Core/Interface/Debugging/Providers/NodeUnderCursorProvider.cs b/Intersect.Client.Core/Interface/Debugging/Providers/NodeUnderCursorProvider.cs new file mode 100644 index 0000000000..687af9eef3 --- /dev/null +++ b/Intersect.Client.Core/Interface/Debugging/Providers/NodeUnderCursorProvider.cs @@ -0,0 +1,17 @@ +using Intersect.Client.Framework.Gwen.Control; +using Intersect.Client.Interface.Data; + +namespace Intersect.Client.Interface.Debugging.Providers; + +public sealed class NodeUnderCursorProvider(TimeSpan delay) : DelayedDataProvider(delay) +{ + public NodeUnderCursorProvider() : this(MinimumDelay) { } + + public NodeFilter Filter { get; set; } = NodeFilter.IncludeMouseInputDisabled; + + protected override bool TryDelayedUpdate(TimeSpan elapsed, TimeSpan total) + { + var newValue = Interface.FindComponentUnderCursor(Filter); + return TrySetValue(newValue); + } +} \ No newline at end of file diff --git a/Intersect.Client.Core/Interface/Game/Admin/AdminWindow.cs b/Intersect.Client.Core/Interface/Game/Admin/AdminWindow.cs index b28dac2a48..6c8e05a466 100644 --- a/Intersect.Client.Core/Interface/Game/Admin/AdminWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Admin/AdminWindow.cs @@ -1,9 +1,11 @@ using Intersect.Admin.Actions; using Intersect.Client.Core; using Intersect.Client.Framework.Content; +using Intersect.Client.Framework.Graphics; using Intersect.Client.Framework.Gwen; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Gwen.Control.EventArguments; +using Intersect.Client.Framework.Gwen.Control.Layout; using Intersect.Client.General; using Intersect.Client.Interface.Shared; using Intersect.Client.Localization; @@ -16,356 +18,441 @@ namespace Intersect.Client.Interface.Game.Admin; -public partial class AdminWindow : WindowControl +public partial class AdminWindow : Window { - private readonly Checkbox _checkboxChronological; - private readonly ComboBox _dropdownAccess; - private readonly ComboBox _dropdownSprite; - private readonly ComboBox _dropdownFace; - private readonly ImagePanel _facePanel; - private readonly ImagePanel _spritePanel; - private readonly TextBox _textboxName; + private readonly LabeledComboBox _accessDropdown; + + private readonly Panel _accessPanel; + private readonly Button _accessSetButton; + + private readonly Panel _actionPanel; + private readonly Table _actionTable; + private readonly Button _banButton; + private readonly GameFont? _defaultFont; + private readonly TexturePicker _faceTexturePicker; + private readonly Button _kickPlayerButton; + private readonly Button _killPlayerButton; + private readonly Button _leaveInstanceButton; + private readonly Label _mapListLabel; + + private readonly Panel _mapListPanel; + private readonly Panel _mapListPanelHeader; + private readonly LabeledCheckBox _mapSortCheckbox; + private readonly Button _muteButton; + private readonly TextBox _nameInput; + private readonly Label _nameLabel; + + private readonly Panel _namePanel; + + private readonly TexturePicker _spriteTexturePicker; + private readonly Button _unbanButton; + private readonly Button _unmuteButton; + private readonly Button _warpMeToPlayerButton; + private readonly Button _warpPlayerToMeButton; private BanMuteBox? _banOrMuteWindow; - private TreeControl? _mapList; + private TreeControl? _mapTree; public AdminWindow(Base gameCanvas) : base( gameCanvas, - Strings.Admin.Title, + Strings.AdminWindow.Title, false, nameof(AdminWindow) ) { + _defaultFont = Current.GetFont(TitleLabel.FontName, 12); + IsResizable = false; - MinimumSize = new Point(320, 320); - Margin = Margin.Zero; - Padding = Padding.Zero; + TitleLabel.FontSize = 14; + + MinimumSize = new Point(396, 600); + InnerPanelPadding = new Padding(8); + InnerPanel.DockChildSpacing = new Padding(8); + + #region Name Input - // Name label and textbox - _ = new Label(this, "LabelName") + _namePanel = new Panel(this, nameof(_namePanel)) { - Text = Strings.Admin.Name, + Dock = Pos.Top, ShouldDrawBackground = false, }; - _textboxName = new TextBox(this, "TextboxName"); - Interface.FocusComponents.Add(_textboxName); - // Access label, dropdown and set power button - _ = new Label(this, "LabelAccess") + _nameLabel = new Label(_namePanel, nameof(_nameLabel)) { - Text = Strings.Admin.Access, + Dock = Pos.Left, + Font = _defaultFont, + Margin = new Margin(0, 0, 4, 0), + Text = Strings.AdminWindow.Name, + TextAlign = Pos.Right, }; - _dropdownAccess = new ComboBox(this, name: nameof(_dropdownAccess)); - _ = _dropdownAccess.AddItem(Strings.Admin.Access0, userData: "None"); - _ = _dropdownAccess.AddItem(Strings.Admin.Access1, userData: "Moderator"); - _ = _dropdownAccess.AddItem(Strings.Admin.Access2, userData: "Admin"); - - var buttonSetPower = new Button(this, "ButtonSetPower") + _nameInput = new TextBox(_namePanel, nameof(_nameInput)) { - Text = Strings.Admin.SetPower, + Font = _defaultFont, + Dock = Pos.Fill, + PlaceholderText = Strings.AdminWindow.NamePlaceholder, }; - buttonSetPower.Clicked += OnButtonSetPowerOnClicked; + Interface.FocusComponents.Add(_nameInput); - #region Quick Admin Actions + #endregion Name Input - // Warp to me admin action - var buttonWarpToMe = new Button(this, "ButtonWarpToMe") + #region Access + + _accessPanel = new Panel(this, nameof(_accessPanel)) { - Text = Strings.Admin.Warp2Me, + Dock = Pos.Top, ShouldDrawBackground = false, }; - buttonWarpToMe.Clicked += (_, _) => + + _accessSetButton = new Button(_accessPanel, nameof(_accessSetButton)) { - if (_textboxName.Text?.Trim().Length > 0) - { - PacketSender.SendAdminAction(new WarpToMeAction(_textboxName.Text)); - } + Dock = Pos.Right, + Font = _defaultFont, + Margin = new Margin(4, 0, 0, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.SetPower, }; + _accessSetButton.Clicked += AccessSetButtonOnClicked; - // Warp to player admin action - var buttonWarpMeTo = new Button(this, "ButtonWarpMeTo") + _accessDropdown = new LabeledComboBox(_accessPanel, nameof(_accessDropdown)) { - Text = Strings.Admin.WarpMe2, + AutoSizeToContents = false, + Dock = Pos.Fill, + Font = _defaultFont, + TextPadding = new Padding(8, 4, 0, 4), + Label = Strings.AdminWindow.Access, }; - buttonWarpMeTo.Clicked += (_, _) => + _ = _accessDropdown.AddItem(Strings.General.None, userData: "None"); + _ = _accessDropdown.AddItem(Strings.AdminWindow.Access1, userData: "Moderator"); + _ = _accessDropdown.AddItem(Strings.AdminWindow.Access2, userData: "Admin"); + + #endregion Access + + #region Quick Admin Actions + + _actionPanel = new Panel(this, nameof(_actionPanel)) { - if (_textboxName.Text?.Trim().Length > 0) - { - PacketSender.SendAdminAction(new WarpMeToAction(_textboxName.Text)); - } + Dock = Pos.Top, ShouldDrawBackground = false, }; - // Warp to overworld admin action - var buttonOverworldReturn = new Button(this, "ButtonOverworldReturn") + _actionTable = new Table(_actionPanel, nameof(_actionTable)) { - Text = Strings.Admin.OverworldReturn, + CellSpacing = new Point(8, 8), + ColumnCount = 3, + Dock = Pos.Fill, + FitRowHeightToContents = true, + Font = _defaultFont, + SizeToContents = true, }; - buttonOverworldReturn.Clicked += (_, _) => + + _warpMeToPlayerButton = new Button(_actionPanel, nameof(_warpMeToPlayerButton)) { - if (!string.IsNullOrEmpty(_textboxName.Text)) - { - PacketSender.SendAdminAction(new ReturnToOverworldAction(_textboxName.Text)); - } + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.WarpMeToPlayer, }; + _warpMeToPlayerButton.Clicked += WarpMeToPlayerButtonOnClicked; - // Kick player admin action - var buttonKick = new Button(this, "ButtonKick") + _kickPlayerButton = new Button(_actionPanel, nameof(_kickPlayerButton)) { - Text = Strings.Admin.Kick, + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.KickPlayer, }; - buttonKick.Clicked += (_, _) => + _kickPlayerButton.Clicked += KickPlayerButtonOnClicked; + + _killPlayerButton = new Button(_actionPanel, nameof(_killPlayerButton)) { - if (_textboxName.Text?.Trim().Length > 0) - { - PacketSender.SendAdminAction(new KickAction(_textboxName.Text)); - } + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.KillPlayer, }; + _killPlayerButton.Clicked += KillPlayerButtonOnClicked; - // Kill player admin action - var buttonKill = new Button(this, "ButtonKill") + _warpPlayerToMeButton = new Button(_actionPanel, nameof(_warpPlayerToMeButton)) { - Text = Strings.Admin.Kill, + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.WarpPlayerToMe, }; - buttonKill.Clicked += (_, _) => + _warpPlayerToMeButton.Clicked += WarpPlayerToMeButtonOnClicked; + + _muteButton = new Button(_actionPanel, nameof(_muteButton)) { - if (_textboxName.Text?.Trim().Length > 0) - { - PacketSender.SendAdminAction(new KillAction(_textboxName.Text)); - } + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.Mute, }; + _muteButton.Clicked += MuteButtonOnClicked; - // Ban and Unban player admin actions - var buttonBan = new Button(this, "ButtonBan") + _unmuteButton = new Button(_actionPanel, nameof(_unmuteButton)) { - Text = Strings.Admin.Ban, + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.Unmute, }; - buttonBan.Clicked += banButton_Clicked; + _unmuteButton.Clicked += UnmuteButtonOnClicked; - var buttonUnban = new Button(this, "ButtonUnban") + _leaveInstanceButton = new Button(_actionPanel, nameof(_leaveInstanceButton)) { - Text = Strings.Admin.Unban, + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.LeaveInstance, }; - buttonUnban.Clicked += (s, e) => + _leaveInstanceButton.Clicked += LeaveInstanceButtonOnClicked; + + _banButton = new Button(_actionPanel, nameof(_banButton)) { - if (_textboxName.Text?.Trim().Length > 0) - { - _ = new InputBox( - Strings.Admin.UnbanCaption.ToString(_textboxName.Text), - Strings.Admin.UnbanPrompt.ToString(_textboxName.Text), - InputBox.InputType.YesNo, - (_, _) => PacketSender.SendAdminAction(new UnbanAction(_textboxName.Text)) - ); - } + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.Ban, }; + _banButton.Clicked += BanButtonOnClicked; - // Mute and Unmute player admin actions - var buttonMute = new Button(this, "ButtonMute") + _unbanButton = new Button(_actionPanel, nameof(_unbanButton)) { - Text = Strings.Admin.Mute, + Font = _defaultFont, + MinimumSize = new Point(120, 0), + Padding = new Padding(8, 4), + Text = Strings.AdminWindow.Unban, }; - buttonMute.Clicked += muteButton_Clicked; + _unbanButton.Clicked += UnbanButtonOnClicked; + + var rowsAdded = _actionTable.AddCells( + _warpMeToPlayerButton, + _kickPlayerButton, + _killPlayerButton, + _warpPlayerToMeButton, + _muteButton, + _unmuteButton, + _leaveInstanceButton, + _banButton, + _unbanButton + ); + + #endregion Quick Admin Actions - var buttonUnmute = new Button(this, "ButtonUnmute") + #region Sprite/Face Pickers + + _spriteTexturePicker = new TexturePicker(this, nameof(_spriteTexturePicker)) { - Text = Strings.Admin.Unmute, + Dock = Pos.Top, + Font = _defaultFont, + ButtonText = Strings.AdminWindow.SetSprite, + LabelText = Strings.AdminWindow.Sprite, + TextureType = TextureType.Entity, }; - buttonUnmute.Clicked += (s, e) => + _spriteTexturePicker.Submitted += SpriteTexturePickerOnSubmitted; + + _faceTexturePicker = new TexturePicker(this, nameof(_faceTexturePicker)) { - if (_textboxName.Text?.Trim().Length > 0) - { - _ = new InputBox( - Strings.Admin.UnmuteCaption.ToString(_textboxName.Text), - Strings.Admin.UnmutePrompt.ToString(_textboxName.Text), - InputBox.InputType.YesNo, - (_, _) => PacketSender.SendAdminAction(new UnmuteAction(_textboxName.Text)) - ); - } + Dock = Pos.Top, + Font = _defaultFont, + ButtonText = Strings.AdminWindow.SetFace, + LabelText = Strings.AdminWindow.Face, + TextureType = TextureType.Face, }; + _faceTexturePicker.Submitted += FaceTexturePickerOnSubmitted; - #endregion Quick Admin Actions + #endregion Sprite/Face Pickers - #region Change Player Sprite and Face + #region Map List - // Change player sprite admin action - _ = new Label(this, "LabelSprite") + _mapListPanel = new Panel(this, nameof(_mapListPanel)) { - Text = Strings.Admin.Sprite, + Dock = Pos.Fill, ShouldDrawBackground = false, }; - _dropdownSprite = new ComboBox(this, "DropdownSprite"); - _ = _dropdownSprite.AddItem(Strings.Admin.None); - _dropdownSprite.ItemSelected += _dropdownSprite_ItemSelected; - var sprites = Globals.ContentManager.GetTextureNames(TextureType.Entity); - Array.Sort(sprites, new AlphanumComparatorFast()); - foreach (var sprite in sprites) + _mapListPanelHeader = new Panel(_mapListPanel, nameof(_mapListPanelHeader)) { - _ = _dropdownSprite.AddItem(sprite); - } + Dock = Pos.Top, ShouldDrawBackground = false, + }; - var buttonSetSprite = new Button(this, "ButtonSetSprite") + _mapListLabel = new Label(_mapListPanelHeader, nameof(_mapListLabel)) { - Text = Strings.Admin.SetSprite, + Dock = Pos.Left, + Font = _defaultFont, + Text = Strings.AdminWindow.MapList, }; - buttonSetSprite.Clicked += (_, _) => + + _mapSortCheckbox = new LabeledCheckBox(_mapListPanelHeader, nameof(_mapSortCheckbox)) { - if (_textboxName.Text?.Trim().Length > 0) - { - PacketSender.SendAdminAction(new SetSpriteAction(_textboxName.Text, _dropdownSprite.Text)); - } + Dock = Pos.Right, + Font = _defaultFont, + Text = Strings.AdminWindow.SortMapList, + TooltipText = Strings.AdminWindow.SortMapListTooltip, + TooltipFont = _defaultFont, }; - var panelSprite = new ImagePanel(this, "PanelSprite"); - _spritePanel = new ImagePanel(panelSprite); + _mapSortCheckbox.CheckChanged += MapSortCheckboxOnCheckChanged; - // Change player face admin action - _ = new Label(this, "LabelFace") - { - Text = Strings.Admin.Face, - }; - _dropdownFace = new ComboBox(this, "DropdownFace"); - _ = _dropdownFace.AddItem(Strings.Admin.None); - _dropdownFace.ItemSelected += _dropdownFace_ItemSelected; + _mapListPanel.SizeToChildren(recursive: true); + + #endregion Map List + + SkipRender(); + } + + protected override void EnsureInitialized() + { + InnerPanel.SizeToChildren(recursive: true); + + LoadJsonUi(UI.InGame, Graphics.Renderer?.GetResolutionString(), saveOutput: true); + + UpdateMapList(); + } + + #region Action Handlers - var faces = Globals.ContentManager.GetTextureNames(TextureType.Face); - Array.Sort(faces, new AlphanumComparatorFast()); - foreach (var face in faces) + private void UnbanButtonOnClicked(Base s, MouseButtonState e) + { + if (PlayerName is not { Length: > 0 } playerName) { - _ = _dropdownFace.AddItem(face); + return; } - var buttonSetFace = new Button(this, "ButtonSetFace") - { - Text = Strings.Admin.SetFace, - }; - buttonSetFace.Clicked += (_, _) => - { - if (_textboxName.Text?.Trim().Length > 0) - { - PacketSender.SendAdminAction(new SetFaceAction(_textboxName.Text, _dropdownFace.Text)); - } - }; + _ = new InputBox( + Strings.AdminWindow.UnbanCaption.ToString(args: playerName), + Strings.AdminWindow.UnbanPrompt.ToString(args: playerName), + InputType.YesNo, + (_, _) => PacketSender.SendAdminAction(new UnbanAction(playerName)) + ); + } - var panelFace = new ImagePanel(this, "PanelFace"); - _facePanel = new ImagePanel(panelFace); + private void UnmuteButtonOnClicked(Base s, MouseButtonState e) + { + if (PlayerName is not { Length: > 0 } playerName) + { + return; + } - #endregion Change Player Sprite and Face + _ = new InputBox( + Strings.AdminWindow.UnmuteCaption.ToString(args: playerName), + Strings.AdminWindow.UnmutePrompt.ToString(args: playerName), + InputType.YesNo, + (_, _) => PacketSender.SendAdminAction(new UnmuteAction(playerName)) + ); + } - // Map list - _ = new Label(this, "LabelMapList") + private void WarpPlayerToMeButtonOnClicked(Base @base, MouseButtonState mouseButtonState) + { + if (PlayerName is not { Length: > 0 } playerName) { - Text = Strings.Admin.MapList, - }; - _checkboxChronological = new Checkbox(this, "CheckboxChronological"); - _checkboxChronological.SetToolTipText(Strings.Admin.ChronologicalTip); - _checkboxChronological.CheckChanged += (_, _) => UpdateMapList(); - _ = new Label(this, "LabelChronological") + return; + } + + PacketSender.SendAdminAction(new WarpToMeAction(playerName)); + } + + private void KillPlayerButtonOnClicked(Base @base, MouseButtonState mouseButtonState) + { + if (PlayerName is not { Length: > 0 } playerName) { - Text = Strings.Admin.Chronological, - }; + return; + } - LoadJsonUi(UI.InGame, Graphics.Renderer?.GetResolutionString(), saveOutput: true); - UpdateMapList(); + PacketSender.SendAdminAction(new KillAction(playerName)); } - private void OnButtonSetPowerOnClicked(Base @base, MouseButtonState mouseButtonState) + private void KickPlayerButtonOnClicked(Base @base, MouseButtonState mouseButtonState) { - var name = _textboxName.Text?.Trim(); - if (name is null or { Length: < 1 }) + if (PlayerName is not { Length: > 0 } playerName) { return; } - var power = _dropdownAccess.SelectedItem?.UserData.ToString()?.Trim(); - if (power is null or { Length: < 1 }) + PacketSender.SendAdminAction(new KickAction(playerName)); + } + + private void WarpMeToPlayerButtonOnClicked(Base @base, MouseButtonState mouseButtonState) + { + if (PlayerName is not { Length: > 0 } playerName) { return; } - PacketSender.SendAdminAction(new SetAccessAction(name, power)); + PacketSender.SendAdminAction(new WarpMeToAction(playerName)); } - public void SetName(string name) + public string? PlayerName { - _textboxName.Text = name; + get => _nameInput.Text?.Trim(); + set => _nameInput.Text = value; } - private void UpdateMapList() + private void FaceTexturePickerOnSubmitted(TexturePicker sender, ValueChangedEventArgs arguments) { - _mapList?.Dispose(); - var mapListY = 330; - var mapListHeight = (_innerPanel?.Height ?? Height) - (mapListY + 4); - _mapList = new TreeControl(this, nameof(_mapList)) - { - X = 4, - Y = mapListY, - Width = Width - 8, - Height = mapListHeight, - MaximumSize = new Point(4096, 999999), - Font = Current.GetFont("sourcesansproblack", 10), - TextColorOverride = Color.White, - }; - _mapList.SelectionChanged += MapList_SelectionChanged; + if (PlayerName is not { Length: > 0 } playerName) + { + return; + } - AddMapListToTree(MapList.List, null); + var textureName = arguments.Value?.Trim(); + PacketSender.SendAdminAction(new SetFaceAction(playerName, textureName)); } - private void AddMapListToTree(MapList mapList, TreeNode? parent) + private void SpriteTexturePickerOnSubmitted(TexturePicker sender, ValueChangedEventArgs arguments) { - if (_mapList == default) + if (PlayerName is not { Length: > 0 } playerName) { return; } - TreeNode tmpNode; - if (_checkboxChronological.IsChecked) + var textureName = arguments.Value?.Trim(); + PacketSender.SendAdminAction(new SetSpriteAction(playerName, textureName)); + } + + private void LeaveInstanceButtonOnClicked(Base @base, MouseButtonState mouseButtonState) + { + if (PlayerName is not { Length: > 0 } playerName) { - for (var i = MapList.OrderedMaps.Count - 1; i >= 0; i--) - { - tmpNode = _mapList.AddNode(MapList.OrderedMaps[i].Name); - tmpNode.UserData = MapList.OrderedMaps[i].MapId; - } + return; + } + PacketSender.SendAdminAction(new ReturnToOverworldAction(playerName)); + } + + private void AccessSetButtonOnClicked(Base @base, MouseButtonState mouseButtonState) + { + if (PlayerName is not { Length: > 0 } playerName) + { return; } - foreach (var item in mapList.Items) + var power = _accessDropdown.SelectedItem?.UserData?.ToString()?.Trim(); + if (power is null or { Length: < 1 }) { - switch (item) - { - case MapListFolder folder: - tmpNode = parent?.AddNode(item.Name) ?? _mapList.AddNode(item.Name); - tmpNode.UserData = folder; - AddMapListToTree(folder.Children, tmpNode); - break; - case MapListMap map: - tmpNode = parent?.AddNode(item.Name) ?? _mapList.AddNode(item.Name); - tmpNode.UserData = map.MapId; - break; - } + return; } - } - #region Action Handlers + PacketSender.SendAdminAction(new SetAccessAction(playerName, power)); + } - private void banButton_Clicked(Base sender, MouseButtonState arguments) + private void BanButtonOnClicked(Base sender, MouseButtonState arguments) { - if (string.IsNullOrWhiteSpace(_textboxName.Text)) + if (PlayerName is not { Length: > 0 } playerName) { return; } - var name = _textboxName.Text?.Trim(); - if (string.Equals(name, Globals.Me?.Name, StringComparison.CurrentCultureIgnoreCase)) + if (string.Equals(playerName, Globals.Me?.Name, StringComparison.CurrentCultureIgnoreCase)) { return; } _banOrMuteWindow = new BanMuteBox( - Strings.Admin.BanCaption.ToString(name), - Strings.Admin.BanPrompt.ToString(_textboxName.Text), + Strings.AdminWindow.BanCaption.ToString(playerName), + Strings.AdminWindow.BanPrompt.ToString(playerName), (_, _) => { PacketSender.SendAdminAction( new BanAction( - _textboxName.Text, + playerName, _banOrMuteWindow?.GetDuration() ?? 0, _banOrMuteWindow?.GetReason() ?? string.Empty, _banOrMuteWindow?.BanIp() ?? false @@ -377,27 +464,26 @@ private void banButton_Clicked(Base sender, MouseButtonState arguments) ); } - private void muteButton_Clicked(Base sender, MouseButtonState arguments) + private void MuteButtonOnClicked(Base sender, MouseButtonState arguments) { - if (string.IsNullOrWhiteSpace(_textboxName.Text)) + if (PlayerName is not { Length: > 0 } playerName) { return; } - var name = _textboxName.Text?.Trim(); - if (string.Equals(name, Globals.Me?.Name, StringComparison.CurrentCultureIgnoreCase)) + if (string.Equals(playerName, Globals.Me?.Name, StringComparison.CurrentCultureIgnoreCase)) { return; } _banOrMuteWindow = new BanMuteBox( - Strings.Admin.MuteCaption.ToString(name), - Strings.Admin.MutePrompt.ToString(_textboxName.Text), + Strings.AdminWindow.MuteCaption.ToString(playerName), + Strings.AdminWindow.MutePrompt.ToString(playerName), (_, _) => { PacketSender.SendAdminAction( new MuteAction( - _textboxName.Text, + playerName, _banOrMuteWindow?.GetDuration() ?? 0, _banOrMuteWindow?.GetReason() ?? string.Empty, _banOrMuteWindow?.BanIp() ?? false @@ -409,67 +495,59 @@ private void muteButton_Clicked(Base sender, MouseButtonState arguments) ); } - private void _dropdownSprite_ItemSelected(Base sender, ItemSelectedEventArgs arguments) + private void MapSortCheckboxOnCheckChanged(Base @base, EventArgs eventArgs) { - if (_dropdownSprite.Text is not { } spriteName) - { - return; - } + UpdateMapList(); + } - var spriteTexture = Globals.ContentManager.GetTexture(TextureType.Entity, spriteName); + private void UpdateMapList() + { + _mapTree?.Dispose(); - if (spriteTexture == null) + _mapTree = new TreeControl(_mapListPanel, nameof(_mapTree)) { - return; - } + Dock = Pos.Fill, Font = _defaultFont, + }; - var textFrameWidth = spriteTexture.Width / Options.Instance.Sprites.NormalFrames; - var textFrameHeight = spriteTexture.Height / Options.Instance.Sprites.Directions; - _spritePanel.SetTextureRect( - 0, - 0, - textFrameWidth, - textFrameHeight - ); - _ = _spritePanel.SetSize(Math.Min(textFrameWidth, 46), Math.Min(textFrameHeight, 46)); - Align.Center(_spritePanel); + _mapTree.SelectionChanged += MapTreeSelectionChanged; + + AddMapListToTree(MapList.List, _mapTree); } - private void _dropdownFace_ItemSelected(Base sender, ItemSelectedEventArgs arguments) + private void AddMapListToTree(MapList mapList, TreeNode parent) { - if (_dropdownFace.Text is not { } faceName) + if (_mapSortCheckbox.IsChecked) { + foreach (var mapListMap in MapList.OrderedMaps) + { + _ = parent.AddNode(mapListMap.Name, mapListMap.MapId); + } + return; } - var faceTexture = Globals.ContentManager.GetTexture(TextureType.Face, faceName); - _facePanel.Texture = faceTexture; - - if (faceTexture == null) + foreach (var item in mapList.Items) { - return; + switch (item) + { + case MapListFolder folder: + AddMapListToTree(folder.Children, parent.AddNode(item.Name, folder)); + break; + case MapListMap map: + parent.AddNode(item.Name, map.MapId); + break; + } } - - var textFrameWidth = faceTexture.Width; - var textFrameHeight = faceTexture.Height; - _facePanel.SetTextureRect( - 0, - 0, - textFrameWidth, - textFrameHeight - ); - _ = _facePanel.SetSize(Math.Min(textFrameWidth, 46), Math.Min(textFrameHeight, 46)); - Align.Center(_facePanel); } - private void MapList_SelectionChanged(Base sender, EventArgs arguments) + private static void MapTreeSelectionChanged(Base sender, EventArgs arguments) { if (sender is not TreeNode treeNode) { ApplicationContext.Context.Value?.Logger.LogDebug( "MapList selection triggered by a sender of type {SenderType} instead of a {TreeNodeType}", - sender.GetType().GetName(qualified: true), - typeof(TreeNode).GetName(qualified: true) + sender.GetType().GetName(true), + typeof(TreeNode).GetName(true) ); return; } @@ -498,6 +576,7 @@ private void MapList_SelectionChanged(Base sender, EventArgs arguments) treeNode.Text ); } + return; } diff --git a/Intersect.Client.Core/Interface/Game/Admin/TexturePicker.cs b/Intersect.Client.Core/Interface/Game/Admin/TexturePicker.cs new file mode 100644 index 0000000000..221b818d9d --- /dev/null +++ b/Intersect.Client.Core/Interface/Game/Admin/TexturePicker.cs @@ -0,0 +1,236 @@ +using Intersect.Client.Framework.Content; +using Intersect.Client.Framework.File_Management; +using Intersect.Client.Framework.Graphics; +using Intersect.Client.Framework.Gwen; +using Intersect.Client.Framework.Gwen.Control; +using Intersect.Client.Framework.Gwen.Control.EventArguments; +using Intersect.Client.Localization; + +namespace Intersect.Client.Interface.Game.Admin; + +public class TexturePicker : Panel, ITextContainer +{ + private readonly GameFont? _defaultFont; + + private readonly Panel _previewPanel; + private readonly ImagePanel _preview; + private readonly Panel _inputPanel; + private readonly LabeledComboBox _textureSelector; + private readonly Button _submitButton; + + private TextureType _textureType; + private bool _selectorDirty; + private bool _allowNone; + private MenuItem? _noneItem; + + public event GwenEventHandler? Selected; + public event GwenEventHandler>? Submitted; + + public TexturePicker(Base parent, string? name = nameof(TexturePicker)) : base(parent: parent, name: name) + { + _allowNone = true; + _defaultFont = GameContentManager.Current.GetFont(name: "sourcesansproblack", size: 12); + + DockChildSpacing = new Padding(8); + ShouldDrawBackground = false; + + _previewPanel = new Panel(this, name: nameof(_previewPanel)) + { + Dock = Pos.Left, + MinimumSize = new Point(68, 68), + Padding = new Padding(4), + }; + + _preview = new ImagePanel(_previewPanel, name: nameof(_preview)) + { + Alignment = [Alignments.Center], + MaintainAspectRatio = true, + }; + + _inputPanel = new Panel(this, name: nameof(_inputPanel)) + { + Dock = Pos.Fill, + DockChildSpacing = new Padding(8), + ShouldDrawBackground = false, + }; + + _textureSelector = new LabeledComboBox(_inputPanel, name: nameof(_textureSelector)) + { + AutoSizeToContents = false, + Dock = Pos.Fill, + Font = _defaultFont, + TextPadding = new Padding(8, 4, 0, 4), + }; + _textureSelector.ItemSelected += TextureSelectorOnItemSelected; + + _submitButton = new Button(_inputPanel, name: nameof(_submitButton)) + { + Dock = Pos.Bottom, + Font = _defaultFont, + Padding = new Padding(8, 4), + }; + _submitButton.Clicked += SubmitButtonOnClicked; + + SizeToChildren(recursive: true); + } + + private void TextureSelectorOnItemSelected(Base sender, ItemSelectedEventArgs arguments) + { + if (arguments.SelectedUserData is not string textureName) + { + _preview.Texture = null; + return; + } + + var texture = GameContentManager.Current.GetTexture(_textureType, textureName); + _preview.Texture = texture; + + if (texture == null) + { + return; + } + + var frameWidth = texture.Width; + var frameHeight = texture.Height; + + if (_textureType == TextureType.Entity) + { + frameWidth = texture.Width / Options.Instance.Sprites.NormalFrames; + frameHeight = texture.Height / Options.Instance.Sprites.Directions; + } + + _preview.SetTextureRect( + (_preview.Width - frameWidth) / 2, + (_preview.Height - frameHeight) / 2, + frameWidth, + frameHeight + ); + _ = _preview.SetSize(Math.Min(frameWidth, 48), Math.Min(frameHeight, 48)); + + Selected?.Invoke(this, arguments); + } + + private void SubmitButtonOnClicked(Base sender, MouseButtonState arguments) + { + Submitted?.Invoke( + this, + new ValueChangedEventArgs + { + Value = _textureSelector.SelectedItem?.UserData as string, + } + ); + } + + public bool AllowNone + { + get => _allowNone; + set => SetAndDoIfChanged(ref _allowNone, value, UpdateNone); + } + + public GameFont? ButtonFont + { + get => _submitButton.Font; + set => _submitButton.Font = value; + } + + public GameFont? LabelFont + { + get => _textureSelector.LabelFont; + set => _textureSelector.LabelFont = value; + } + + public GameFont? SelectorFont + { + get => _textureSelector.ItemFont; + set => _textureSelector.ItemFont = value; + } + + public GameFont? Font + { + get => _textureSelector.Font; + set + { + _submitButton.Font = value; + _textureSelector.Font = value; + } + } + + public string? ButtonText + { + get => _submitButton.Text; + set => _submitButton.Text = value; + } + + public string? LabelText + { + get => _textureSelector.Label; + set => _textureSelector.Label = value; + } + + public TextureType TextureType + { + get => _textureType; + set => SetAndDoIfChanged(ref _textureType, value, InvalidateSelector); + } + + string? ITextContainer.Text + { + get => LabelText; + set => LabelText = value; + } + + public Color? TextPaddingDebugColor { get; set; } + + private void UpdateNone() + { + if (_allowNone) + { + _noneItem ??= AddNone(); + } + else if (_noneItem is not null) + { + RemoveChild(_noneItem, dispose: true); + _noneItem = null; + } + } + + public void InvalidateSelector() + { + if (!_selectorDirty) + { + _selectorDirty = true; + } + + Invalidate(); + } + + private MenuItem AddNone() + { + var noneItem = _textureSelector.AddItem(label: Strings.General.None); + noneItem.SendToBack(); + return noneItem; + } + + protected override void Layout(Framework.Gwen.Skin.Base skin) + { + if (_selectorDirty) + { + _selectorDirty = false; + + var textureNames = GameContentManager.Current.GetTextureNames(_textureType); + Array.Sort(textureNames, new AlphanumComparatorFast()); + + _noneItem = null; + _textureSelector.ClearItems(); + + UpdateNone(); + + foreach (var textureName in textureNames) + { + _ = _textureSelector.AddItem(label: textureName, userData: textureName); + } + } + + base.Layout(skin); + } +} \ No newline at end of file diff --git a/Intersect.Client.Core/Interface/Game/Bag/BagItem.cs b/Intersect.Client.Core/Interface/Game/Bag/BagItem.cs index c51c786479..2fc7708b0b 100644 --- a/Intersect.Client.Core/Interface/Game/Bag/BagItem.cs +++ b/Intersect.Client.Core/Interface/Game/Bag/BagItem.cs @@ -69,7 +69,7 @@ private void Pnl_DoubleClicked(Base sender, MouseButtonState arguments) { if (Globals.InBag) { - Globals.Me.TryRetreiveBagItem(mMySlot, -1); + Globals.Me.TryRetrieveItemFromBag(mMySlot, -1); } } @@ -142,8 +142,8 @@ public FloatRect RenderBounds() { var rect = new FloatRect() { - X = Pnl.LocalPosToCanvas(new Point(0, 0)).X, - Y = Pnl.LocalPosToCanvas(new Point(0, 0)).Y, + X = Pnl.ToCanvas(new Point(0, 0)).X, + Y = Pnl.ToCanvas(new Point(0, 0)).Y, Width = Pnl.Width, Height = Pnl.Height }; @@ -202,23 +202,23 @@ public void Update() { if (mMouseX == -1 || mMouseY == -1) { - mMouseX = InputHandler.MousePosition.X - Pnl.LocalPosToCanvas(new Point(0, 0)).X; - mMouseY = InputHandler.MousePosition.Y - Pnl.LocalPosToCanvas(new Point(0, 0)).Y; + mMouseX = InputHandler.MousePosition.X - Pnl.ToCanvas(new Point(0, 0)).X; + mMouseY = InputHandler.MousePosition.Y - Pnl.ToCanvas(new Point(0, 0)).Y; } else { var xdiff = mMouseX - - (InputHandler.MousePosition.X - Pnl.LocalPosToCanvas(new Point(0, 0)).X); + (InputHandler.MousePosition.X - Pnl.ToCanvas(new Point(0, 0)).X); var ydiff = mMouseY - - (InputHandler.MousePosition.Y - Pnl.LocalPosToCanvas(new Point(0, 0)).Y); + (InputHandler.MousePosition.Y - Pnl.ToCanvas(new Point(0, 0)).Y); if (Math.Sqrt(Math.Pow(xdiff, 2) + Math.Pow(ydiff, 2)) > 5) { IsDragging = true; mDragIcon = new Draggable( - Pnl.LocalPosToCanvas(new Point(0, 0)).X + mMouseX, - Pnl.LocalPosToCanvas(new Point(0, 0)).X + mMouseY, Pnl.Texture, Pnl.RenderColor + Pnl.ToCanvas(new Point(0, 0)).X + mMouseX, + Pnl.ToCanvas(new Point(0, 0)).X + mMouseY, Pnl.Texture, Pnl.RenderColor ); } } @@ -295,7 +295,7 @@ public void Update() if (bestIntersectIndex > -1) { - Globals.Me.TryRetreiveBagItem(mMySlot, bestIntersectIndex); + Globals.Me.TryRetrieveItemFromBag(mMySlot, bestIntersectIndex); } } } diff --git a/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs b/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs index 311a98b655..e68348dd6e 100644 --- a/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs @@ -79,7 +79,7 @@ private void MWithdrawContextItem_Clicked(Base sender, Framework.Gwen.Control.Ev if (Globals.InBag) { var slot = (int) sender.Parent.UserData; - Globals.Me.TryRetreiveBagItem(slot, -1); + Globals.Me.TryRetrieveItemFromBag(slot, -1); } } @@ -180,8 +180,8 @@ public FloatRect RenderBounds() { var rect = new FloatRect() { - X = mBagWindow.LocalPosToCanvas(new Point(0, 0)).X - sItemXPadding / 2, - Y = mBagWindow.LocalPosToCanvas(new Point(0, 0)).Y - sItemYPadding / 2, + X = mBagWindow.ToCanvas(new Point(0, 0)).X - sItemXPadding / 2, + Y = mBagWindow.ToCanvas(new Point(0, 0)).Y - sItemYPadding / 2, Width = mBagWindow.Width + sItemXPadding, Height = mBagWindow.Height + sItemYPadding }; diff --git a/Intersect.Client.Core/Interface/Game/Bank/BankItem.cs b/Intersect.Client.Core/Interface/Game/Bank/BankItem.cs index 8daa132030..58cac8e957 100644 --- a/Intersect.Client.Core/Interface/Game/Bank/BankItem.cs +++ b/Intersect.Client.Core/Interface/Game/Bank/BankItem.cs @@ -74,7 +74,7 @@ private void Pnl_DoubleClicked(Base sender, MouseButtonState arguments) { if (Globals.InputManager.IsKeyDown(Keys.Shift)) { - Globals.Me.TryWithdrawItem( + Globals.Me.TryRetrieveItemFromBank( mMySlot, skipPrompt: true ); @@ -82,7 +82,7 @@ private void Pnl_DoubleClicked(Base sender, MouseButtonState arguments) else { var slot = Globals.Bank[mMySlot]; - Globals.Me.TryWithdrawItem( + Globals.Me.TryRetrieveItemFromBank( mMySlot, slot, quantityHint: slot.Quantity, @@ -160,8 +160,8 @@ public FloatRect RenderBounds() { var rect = new FloatRect() { - X = Pnl.LocalPosToCanvas(new Point(0, 0)).X, - Y = Pnl.LocalPosToCanvas(new Point(0, 0)).Y, + X = Pnl.ToCanvas(new Point(0, 0)).X, + Y = Pnl.ToCanvas(new Point(0, 0)).Y, Width = Pnl.Width, Height = Pnl.Height }; @@ -221,23 +221,23 @@ public void Update() { if (mMouseX == -1 || mMouseY == -1) { - mMouseX = InputHandler.MousePosition.X - Pnl.LocalPosToCanvas(new Point(0, 0)).X; - mMouseY = InputHandler.MousePosition.Y - Pnl.LocalPosToCanvas(new Point(0, 0)).Y; + mMouseX = InputHandler.MousePosition.X - Pnl.ToCanvas(new Point(0, 0)).X; + mMouseY = InputHandler.MousePosition.Y - Pnl.ToCanvas(new Point(0, 0)).Y; } else { var xdiff = mMouseX - - (InputHandler.MousePosition.X - Pnl.LocalPosToCanvas(new Point(0, 0)).X); + (InputHandler.MousePosition.X - Pnl.ToCanvas(new Point(0, 0)).X); var ydiff = mMouseY - - (InputHandler.MousePosition.Y - Pnl.LocalPosToCanvas(new Point(0, 0)).Y); + (InputHandler.MousePosition.Y - Pnl.ToCanvas(new Point(0, 0)).Y); if (Math.Sqrt(Math.Pow(xdiff, 2) + Math.Pow(ydiff, 2)) > 5) { IsDragging = true; mDragIcon = new Draggable( - Pnl.LocalPosToCanvas(new Point(0, 0)).X + mMouseX, - Pnl.LocalPosToCanvas(new Point(0, 0)).X + mMouseY, Pnl.Texture, Pnl.RenderColor + Pnl.ToCanvas(new Point(0, 0)).X + mMouseX, + Pnl.ToCanvas(new Point(0, 0)).X + mMouseY, Pnl.Texture, Pnl.RenderColor ); } } @@ -348,7 +348,7 @@ public void Update() if (bestIntersectIndex > -1) { var slot = Globals.Bank[mMySlot]; - Globals.Me.TryWithdrawItem( + Globals.Me.TryRetrieveItemFromBank( mMySlot, inventorySlotIndex: bestIntersectIndex, quantityHint: slot.Quantity, diff --git a/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs b/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs index a41239cffa..c7a4f26706 100644 --- a/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs @@ -96,7 +96,7 @@ public void OpenContextMenu(int slot) private void MWithdrawContextItem_Clicked(Base sender, Framework.Gwen.Control.EventArguments.MouseButtonState arguments) { var slot = (int)sender.Parent.UserData; - Globals.Me.TryWithdrawItem(slot); + Globals.Me.TryRetrieveItemFromBank(slot); } public void Close() @@ -240,8 +240,8 @@ public FloatRect RenderBounds() { var rect = new FloatRect() { - X = mBankWindow.LocalPosToCanvas(new Point(0, 0)).X - sItemXPadding / 2, - Y = mBankWindow.LocalPosToCanvas(new Point(0, 0)).Y - sItemYPadding / 2, + X = mBankWindow.ToCanvas(new Point(0, 0)).X - sItemXPadding / 2, + Y = mBankWindow.ToCanvas(new Point(0, 0)).Y - sItemYPadding / 2, Width = mBankWindow.Width + sItemXPadding, Height = mBankWindow.Height + sItemYPadding }; diff --git a/Intersect.Client.Core/Interface/Game/Character/EquipmentItem.cs b/Intersect.Client.Core/Interface/Game/Character/EquipmentItem.cs index a4e7c1e3ad..15130a5db2 100644 --- a/Intersect.Client.Core/Interface/Game/Character/EquipmentItem.cs +++ b/Intersect.Client.Core/Interface/Game/Character/EquipmentItem.cs @@ -115,8 +115,8 @@ public FloatRect RenderBounds() { var rect = new FloatRect() { - X = Pnl.LocalPosToCanvas(new Point(0, 0)).X, - Y = Pnl.LocalPosToCanvas(new Point(0, 0)).Y, + X = Pnl.ToCanvas(new Point(0, 0)).X, + Y = Pnl.ToCanvas(new Point(0, 0)).Y, Width = Pnl.Width, Height = Pnl.Height }; diff --git a/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs b/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs index e480440c1f..64aab3c569 100644 --- a/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs +++ b/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs @@ -3,6 +3,7 @@ using Intersect.Client.Framework.File_Management; using Intersect.Client.Framework.GenericClasses; using Intersect.Client.Framework.Graphics; +using Intersect.Client.Framework.Gwen; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Gwen.Control.EventArguments; using Intersect.Client.Framework.Gwen.ControlInternal; @@ -11,9 +12,11 @@ using Intersect.Client.Localization; using Intersect.Client.Networking; using Intersect.Configuration; +using Intersect.Core; using Intersect.Enums; using Intersect.Localization; using Intersect.Utilities; +using Microsoft.Extensions.Logging; namespace Intersect.Client.Interface.Game.Chat; @@ -97,7 +100,10 @@ public Chatbox(Canvas gameCanvas, GameInterface gameUi) //Chatbox Window mChatboxWindow = new ImagePanel(gameCanvas, "ChatboxWindow"); - mChatboxMessages = new ListBox(mChatboxWindow, "MessageList"); + mChatboxMessages = new ListBox(mChatboxWindow, "MessageList") + { + Dock = Pos.Fill, + }; mChatboxMessages.EnableScroll(false, true); mChatboxWindow.ShouldCacheToTexture = true; @@ -193,6 +199,8 @@ public Chatbox(Canvas gameCanvas, GameInterface gameUi) mGuildInviteContextItem = mContextMenu.AddItem(Strings.ChatContextMenu.GuildInvite); mGuildInviteContextItem.Clicked += MGuildInviteContextItem_Clicked; mContextMenu.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); + + mChatboxWindow.BeforeLayout += ChatboxWindowOnBeforeLayout; } public void OpenContextMenu(string name) @@ -345,7 +353,7 @@ private void SetChannelToTab(ChatboxTab tab) } //Update - public void Update() + private void Update() { var vScrollBar = mChatboxMessages.GetVerticalScrollBar(); var scrollAmount = vScrollBar.ScrollAmount; @@ -353,34 +361,42 @@ public void Update() var scrollToBottom = vScrollBar.ScrollAmount == 1 || !scrollBarVisible; // Did the tab change recently? If so, we need to reset a few things to make it work... - if (mLastTab != mCurrentTab) + var currentTab = mCurrentTab; + if (mLastTab != currentTab) { mChatboxMessages.Clear(); - mChatboxMessages.HorizontalScrollBar.SetScrollAmount(0); + mChatboxMessages.HorizontalScrollBar.ScrollAmount = 0; + mChatboxMessages.VerticalScrollBar.ScrollAmount = 1; mMessageIndex = 0; mReceivedMessage = true; - mLastTab = mCurrentTab; + mLastTab = currentTab; } - var messages = ChatboxMsg.GetMessages(mCurrentTab); + var scrollPosition = mChatboxMessages.VerticalScroll; + var messages = ChatboxMsg.GetMessages(currentTab); for (var i = mMessageIndex; i < messages.Count; i++) { var msg = messages[i]; - var lines = Text.WrapText( + string[] lines = [msg.Message];/*Text.WrapText( msg.Message, mChatboxMessages.Width - vScrollBar.Width - 8, mChatboxText.Font, Graphics.Renderer ?? throw new InvalidOperationException("No renderer") - ); + );*/ foreach (var line in lines) { - var row = mChatboxMessages.AddRow(line.Trim()); + var row = mChatboxMessages.AddRow(line.Trim(), name: $"Message:{currentTab}#{mMessageIndex}", userData: msg.Target); + row.ShouldDrawBackground = false; + row.Padding = new Padding(2, 0); row.Font = mChatboxText.Font; row.SetTextColor(msg.Color); - row.ShouldDrawBackground = false; - row.UserData = msg.Target; + if (row.GetCellContents(0) is Label label) + { + label.WrappingBehavior = WrappingBehavior.Wrapped; + } row.Clicked += ChatboxRow_Clicked; + mReceivedMessage = true; while (mChatboxMessages.RowCount > ClientConfiguration.Instance.ChatLines) @@ -395,15 +411,34 @@ public void Update() // ReSharper disable once InvertIf if (mReceivedMessage) { + // mChatboxMessages.SizeToContents(); + if (scrollToBottom) { - mChatboxMessages.ScrollToBottom(); + mChatboxMessages.Defer(mChatboxMessages.ScrollToBottom); + } + else + { + mChatboxMessages.Defer(() => + { + ApplicationContext.CurrentContext.Logger.LogTrace( + "Scrolling chat to {ScrollY}", + scrollPosition + ); + mChatboxMessages.ScrollToY(scrollPosition); + } + ); } - // vScrollBar.SetScrollAmount(scrollToBottom ? 1 : scrollAmount); + mReceivedMessage = false; } } + private void ChatboxWindowOnBeforeLayout(Base sender, EventArgs arguments) + { + Update(); + } + public void SetChatboxText(string msg) { mChatboxInput.Text = msg; diff --git a/Intersect.Client.Core/Interface/Game/Crafting/CraftingWindow.cs b/Intersect.Client.Core/Interface/Game/Crafting/CraftingWindow.cs index 8d7b6f8a5c..b18fc70cc0 100644 --- a/Intersect.Client.Core/Interface/Game/Crafting/CraftingWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Crafting/CraftingWindow.cs @@ -1,4 +1,5 @@ using Intersect.Client.Core; +using Intersect.Client.Entities; using Intersect.Client.Framework.File_Management; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Gwen.Control.EventArguments; @@ -6,230 +7,230 @@ using Intersect.Client.Interface.Game.Chat; using Intersect.Client.Localization; using Intersect.Client.Networking; +using Intersect.Core; +using Intersect.Enums; +using Intersect.Framework.Reflection; using Intersect.GameObjects; using Intersect.GameObjects.Crafting; using Intersect.Utilities; +using Microsoft.Extensions.Logging; namespace Intersect.Client.Interface.Game.Crafting; - -public partial class CraftingWindow +public partial class CraftingWindow : Window { - private ImagePanel mBar; - - private ImagePanel mBarContainer; - - private long mBarTimer; - - private RecipeItem mCombinedItem; - - private Label mCombinedValue; + private readonly ImagePanel mBar; - private Button mCraft; + private readonly ImagePanel mBarContainer; - private Button mCraftAll; + private readonly Button mCraft; - private Guid mAutoCraftId = Guid.Empty; + private readonly Button mCraftAll; - private int mRemainingCrafts = 0; + private readonly ScrollControl mItemContainer; - private Guid mCraftId; + private readonly List mItems = []; - //Controls - private WindowControl mCraftWindow; + private readonly Label mLblCraftingChance; - private bool mInitialized = false; + private readonly Label mLblCraftingTime; - private ScrollControl mItemContainer; + private readonly Label mLblDestroyMaterialsChance; - private List mItems = new List(); + private readonly Label mLblIngredients; - private Label mLblIngredients; + private readonly Label mLblProduct; - private Label mLblProduct; - - private Label mLblRecipes; - - private Label mLblCraftingChance; - - private Label mLblDestroyMaterialsChance; - - private Label mLblCraftingTime; + private readonly Label mLblRecipes; //Objects - private ListBox mRecipes; + private readonly ListBox mRecipes; - private List