From 6beebf3e7294995239e103e90e03e74a824b57de Mon Sep 17 00:00:00 2001
From: Arufonsu <17498701+Arufonsu@users.noreply.github.com>
Date: Sun, 17 Nov 2024 19:14:18 -0300
Subject: [PATCH 1/3] feature: simplified escape menu
+ In game interface setting to toggle this feature.
+ Fix: "InvalidOperationException: Collection was modified (during iteration);" client crash was happening upon selecting 'yes' on Combat Warning prompt when logging out/exiting.
---
.../Database/GameDatabase.cs | 4 +
.../Gwen/Control/Base.cs | 5 +-
Intersect.Client/Core/Input.cs | 11 +-
.../Interface/Game/GameInterface.cs | 3 +
Intersect.Client/Interface/Game/Menu.cs | 13 +-
.../Interface/Game/SimplifiedEscapeMenu.cs | 167 ++++++++++++++++++
.../Interface/Shared/SettingsWindow.cs | 9 +
Intersect.Client/Localization/Strings.cs | 3 +
8 files changed, 211 insertions(+), 4 deletions(-)
create mode 100644 Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs
diff --git a/Intersect.Client.Framework/Database/GameDatabase.cs b/Intersect.Client.Framework/Database/GameDatabase.cs
index e772fcb64..ef1de0c3f 100644
--- a/Intersect.Client.Framework/Database/GameDatabase.cs
+++ b/Intersect.Client.Framework/Database/GameDatabase.cs
@@ -58,6 +58,8 @@ public abstract partial class GameDatabase
public bool ShowHealthAsPercentage { get; set; }
public bool ShowManaAsPercentage { get; set; }
+
+ public bool SimplifiedEscapeMenu { get; set; }
public TypewriterBehavior TypewriterBehavior { get; set; }
@@ -130,6 +132,7 @@ public virtual void LoadPreferences()
ShowExperienceAsPercentage = LoadPreference(nameof(ShowExperienceAsPercentage), true);
ShowHealthAsPercentage = LoadPreference(nameof(ShowHealthAsPercentage), false);
ShowManaAsPercentage = LoadPreference(nameof(ShowManaAsPercentage), false);
+ SimplifiedEscapeMenu = LoadPreference(nameof(SimplifiedEscapeMenu), false);
TypewriterBehavior = LoadPreference(nameof(TypewriterBehavior), TypewriterBehavior.Word);
UIScale = LoadPreference(nameof(UIScale), 1.0f);
WorldZoom = LoadPreference(nameof(WorldZoom), 1.0f);
@@ -166,6 +169,7 @@ public virtual void SavePreferences()
SavePreference(nameof(ShowExperienceAsPercentage), ShowExperienceAsPercentage);
SavePreference(nameof(ShowHealthAsPercentage), ShowHealthAsPercentage);
SavePreference(nameof(ShowManaAsPercentage), ShowManaAsPercentage);
+ SavePreference(nameof(SimplifiedEscapeMenu), SimplifiedEscapeMenu);
SavePreference(nameof(TypewriterBehavior), TypewriterBehavior);
SavePreference(nameof(UIScale), UIScale);
SavePreference(nameof(WorldZoom), WorldZoom);
diff --git a/Intersect.Client.Framework/Gwen/Control/Base.cs b/Intersect.Client.Framework/Gwen/Control/Base.cs
index 628a82e50..39fbc1a9a 100644
--- a/Intersect.Client.Framework/Gwen/Control/Base.cs
+++ b/Intersect.Client.Framework/Gwen/Control/Base.cs
@@ -765,7 +765,10 @@ public virtual void Dispose()
Gwen.ToolTip.ControlDeleted(this);
Animation.Cancel(this);
- mChildren?.ForEach(child => child?.Dispose());
+ // [Fix]: "InvalidOperationException: Collection was modified (during iteration); enumeration operation may not execute".
+ // (Creates a copy of the children list to avoid modifying the collection during iteration).
+ var childrenCopy = new List(mChildren);
+ childrenCopy.ForEach(child => child.Dispose());
mChildren?.Clear();
mInnerPanel?.Dispose();
diff --git a/Intersect.Client/Core/Input.cs b/Intersect.Client/Core/Input.cs
index 4d6948b66..02964631c 100644
--- a/Intersect.Client/Core/Input.cs
+++ b/Intersect.Client/Core/Input.cs
@@ -133,7 +133,16 @@ public static void OnKeyPressed(Keys modifier, Keys key)
}
else
{
- Interface.Interface.GameUi?.EscapeMenu?.ToggleHidden();
+ var simplifiedEscapeMenuSetting = Globals.Database.SimplifiedEscapeMenu;
+
+ if (!simplifiedEscapeMenuSetting)
+ {
+ Interface.Interface.GameUi?.EscapeMenu?.ToggleHidden();
+ }
+ else
+ {
+ Interface.Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
+ }
}
}
diff --git a/Intersect.Client/Interface/Game/GameInterface.cs b/Intersect.Client/Interface/Game/GameInterface.cs
index e55e36ec3..a59008ce9 100644
--- a/Intersect.Client/Interface/Game/GameInterface.cs
+++ b/Intersect.Client/Interface/Game/GameInterface.cs
@@ -93,6 +93,7 @@ public GameInterface(Canvas canvas) : base(canvas)
{
GameCanvas = canvas;
EscapeMenu = new EscapeMenu(GameCanvas) {IsHidden = true};
+ SimplifiedEscapeMenu = new SimplifiedEscapeMenu(GameCanvas) {IsHidden = true};
AnnouncementWindow = new AnnouncementWindow(GameCanvas) { IsHidden = true };
InitGameGui();
@@ -101,6 +102,8 @@ public GameInterface(Canvas canvas) : base(canvas)
public Canvas GameCanvas { get; }
public EscapeMenu EscapeMenu { get; }
+
+ public SimplifiedEscapeMenu SimplifiedEscapeMenu { get; }
public AnnouncementWindow AnnouncementWindow { get; }
diff --git a/Intersect.Client/Interface/Game/Menu.cs b/Intersect.Client/Interface/Game/Menu.cs
index 6e1698e89..aae01d550 100644
--- a/Intersect.Client/Interface/Game/Menu.cs
+++ b/Intersect.Client/Interface/Game/Menu.cs
@@ -37,7 +37,7 @@ public partial class Menu
private readonly ImagePanel mMenuBackground;
- private readonly Button mMenuButton;
+ public readonly Button mMenuButton;
//Menu Container
private readonly ImagePanel mMenuContainer;
@@ -357,7 +357,16 @@ public bool HasWindowsOpen()
//Input Handlers
private static void MenuButtonClicked(Base sender, ClickedEventArgs arguments)
{
- Interface.GameUi?.EscapeMenu?.ToggleHidden();
+ var simplifiedEscapeMenuSetting = Globals.Database.SimplifiedEscapeMenu;
+
+ if (!simplifiedEscapeMenuSetting)
+ {
+ Interface.GameUi?.EscapeMenu?.ToggleHidden();
+ }
+ else
+ {
+ Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
+ }
}
private void PartyBtn_Clicked(Base sender, ClickedEventArgs arguments)
diff --git a/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs b/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs
new file mode 100644
index 000000000..31ff568fb
--- /dev/null
+++ b/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs
@@ -0,0 +1,167 @@
+using Intersect.Client.Core;
+using Intersect.Client.Framework.File_Management;
+using Intersect.Client.Framework.Gwen;
+using Intersect.Client.Framework.Gwen.Control;
+using Intersect.Client.Framework.Gwen.Control.EventArguments;
+using Intersect.Client.General;
+using Intersect.Client.Interface.Shared;
+using Intersect.Client.Localization;
+using Intersect.Utilities;
+
+namespace Intersect.Client.Interface.Game;
+
+public sealed partial class SimplifiedEscapeMenu : Framework.Gwen.Control.Menu
+{
+ private readonly SettingsWindow _settingsWindow;
+ private readonly MenuItem _settings;
+ private readonly MenuItem _character;
+ private readonly MenuItem _logout;
+ private readonly MenuItem _exit;
+
+ public SimplifiedEscapeMenu(Canvas gameCanvas) : base(gameCanvas, nameof(SimplifiedEscapeMenu))
+ {
+ IsHidden = true;
+ IconMarginDisabled = true;
+ _settingsWindow = new SettingsWindow(gameCanvas, null, null);
+
+ Children.Clear();
+
+ _settings = AddItem(Strings.EscapeMenu.Settings);
+ _character = AddItem(Strings.EscapeMenu.CharacterSelect);
+ _logout = AddItem(Strings.EscapeMenu.Logout);
+ _exit = AddItem(Strings.EscapeMenu.ExitToDesktop);
+
+ _settings.Clicked += OpenSettingsWindow;
+ _character.Clicked += LogoutToCharacterSelectSelectClicked;
+ _logout.Clicked += LogoutToMainToMainMenuClicked;
+ _exit.Clicked += ExitToDesktopToDesktopClicked;
+
+ LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer?.GetResolutionString());
+ }
+
+ public override void ToggleHidden()
+ {
+ if (!_settingsWindow.IsHidden)
+ {
+ return;
+ }
+
+ if (this.IsHidden)
+ {
+ // Position the context menu within the game canvas if near borders.
+ var menuPosX = Interface.GameUi.GameMenu.mMenuButton.LocalPosToCanvas(new Point(0, 0)).X;
+ var menuPosY = Interface.GameUi.GameMenu.mMenuButton.LocalPosToCanvas(new Point(0, 0)).Y;
+ var newX = menuPosX;
+ var newY = menuPosY + Interface.GameUi.GameMenu.mMenuButton.Height + 6;
+
+ if (newX + Width >= Canvas?.Width)
+ {
+ newX = menuPosX - Width + Interface.GameUi.GameMenu.mMenuButton.Width;
+ }
+
+ if (newY + Height >= Canvas?.Height)
+ {
+ newY = menuPosY - Height - 6;
+ }
+
+ SizeToChildren();
+ Open(Pos.None);
+ SetPosition(newX, newY);
+ }
+ else
+ {
+ Close();
+ }
+ }
+
+ private void LogoutToCharacterSelectSelectClicked(Base sender, ClickedEventArgs arguments)
+ {
+ if (Globals.Me?.CombatTimer > Timing.Global.Milliseconds)
+ {
+ _ = new InputBox(
+ title: Strings.Combat.WarningTitle,
+ prompt: Strings.Combat.WarningCharacterSelect,
+ inputType: InputBox.InputType.YesNo,
+ onSuccess: LogoutToCharacterSelect
+ );
+ }
+ else
+ {
+ LogoutToCharacterSelect(null, null);
+ }
+ }
+
+ private void LogoutToMainToMainMenuClicked(Base sender, ClickedEventArgs arguments)
+ {
+ if (Globals.Me?.CombatTimer > Timing.Global.Milliseconds)
+ {
+ _ = new InputBox(
+ title: Strings.Combat.WarningTitle,
+ prompt: Strings.Combat.WarningLogout,
+ inputType: InputBox.InputType.YesNo,
+ onSuccess: LogoutToMainMenu
+ );
+ }
+ else
+ {
+ LogoutToMainMenu(null, null);
+ }
+ }
+
+ private void ExitToDesktopToDesktopClicked(Base sender, ClickedEventArgs arguments)
+ {
+ if (Globals.Me?.CombatTimer > Timing.Global.Milliseconds)
+ {
+ _ = new InputBox(
+ title: Strings.Combat.WarningTitle,
+ prompt: Strings.Combat.WarningExitDesktop,
+ inputType: InputBox.InputType.YesNo,
+ onSuccess: ExitToDesktop
+ );
+ }
+ else
+ {
+ ExitToDesktop(null, null);
+ }
+ }
+
+ private void OpenSettingsWindow(object? sender, EventArgs? e)
+ {
+ if (!_settingsWindow.IsHidden)
+ {
+ return;
+ }
+
+ _settingsWindow.Show();
+ }
+
+ private static void LogoutToCharacterSelect(object? sender, EventArgs? e)
+ {
+ if (Globals.Me != null)
+ {
+ Globals.Me.CombatTimer = 0;
+ }
+
+ Main.Logout(true);
+ }
+
+ private static void LogoutToMainMenu(object? sender, EventArgs? e)
+ {
+ if (Globals.Me != null)
+ {
+ Globals.Me.CombatTimer = 0;
+ }
+
+ Main.Logout(false);
+ }
+
+ private static void ExitToDesktop(object? sender, EventArgs? e)
+ {
+ if (Globals.Me != null)
+ {
+ Globals.Me.CombatTimer = 0;
+ }
+
+ Globals.IsRunning = false;
+ }
+}
\ No newline at end of file
diff --git a/Intersect.Client/Interface/Shared/SettingsWindow.cs b/Intersect.Client/Interface/Shared/SettingsWindow.cs
index b7cf28fb8..ff9baa3cf 100644
--- a/Intersect.Client/Interface/Shared/SettingsWindow.cs
+++ b/Intersect.Client/Interface/Shared/SettingsWindow.cs
@@ -45,6 +45,7 @@ public partial class SettingsWindow : ImagePanel
private readonly LabeledCheckBox _showExperienceAsPercentageCheckbox;
private readonly LabeledCheckBox _showHealthAsPercentageCheckbox;
private readonly LabeledCheckBox _showManaAsPercentageCheckbox;
+ private readonly LabeledCheckBox _simplifiedEscapeMenu;
// Game Settings - Information.
private readonly ScrollControl _informationSettings;
@@ -180,6 +181,12 @@ public SettingsWindow(Base parent, MainMenu? mainMenu, EscapeMenu? escapeMenu) :
{
Text = Strings.Settings.ShowManaAsPercentage
};
+
+ // Game Settings - Interface: simplified escape menu.
+ _simplifiedEscapeMenu = new LabeledCheckBox(_interfaceSettings, "SimplifiedEscapeMenu")
+ {
+ Text = Strings.Settings.SimplifiedEscapeMenu
+ };
// Game Settings - Information.
_informationSettings = new ScrollControl(_gameSettingsContainer, "InformationSettings");
@@ -730,6 +737,7 @@ public void Show(bool returnToMenu = false)
_showHealthAsPercentageCheckbox.IsChecked = Globals.Database.ShowHealthAsPercentage;
_showManaAsPercentageCheckbox.IsChecked = Globals.Database.ShowManaAsPercentage;
_showExperienceAsPercentageCheckbox.IsChecked = Globals.Database.ShowExperienceAsPercentage;
+ _simplifiedEscapeMenu.IsChecked = Globals.Database.SimplifiedEscapeMenu;
_friendOverheadInfoCheckbox.IsChecked = Globals.Database.FriendOverheadInfo;
_guildMemberOverheadInfoCheckbox.IsChecked = Globals.Database.GuildMemberOverheadInfo;
_myOverheadInfoCheckbox.IsChecked = Globals.Database.MyOverheadInfo;
@@ -910,6 +918,7 @@ private void SettingsApplyBtn_Clicked(Base sender, ClickedEventArgs arguments)
Globals.Database.ShowExperienceAsPercentage = _showExperienceAsPercentageCheckbox.IsChecked;
Globals.Database.ShowHealthAsPercentage = _showHealthAsPercentageCheckbox.IsChecked;
Globals.Database.ShowManaAsPercentage = _showManaAsPercentageCheckbox.IsChecked;
+ Globals.Database.SimplifiedEscapeMenu = _simplifiedEscapeMenu.IsChecked;
Globals.Database.FriendOverheadInfo = _friendOverheadInfoCheckbox.IsChecked;
Globals.Database.GuildMemberOverheadInfo = _guildMemberOverheadInfoCheckbox.IsChecked;
Globals.Database.MyOverheadInfo = _myOverheadInfoCheckbox.IsChecked;
diff --git a/Intersect.Client/Localization/Strings.cs b/Intersect.Client/Localization/Strings.cs
index 3b44e1ada..99c146558 100644
--- a/Intersect.Client/Localization/Strings.cs
+++ b/Intersect.Client/Localization/Strings.cs
@@ -1932,6 +1932,9 @@ public partial struct Settings
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public static LocalizedString ShowPlayerOverheadInformation = @"Show players overhead information";
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public static LocalizedString SimplifiedEscapeMenu = @"Simplified escape menu";
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public static LocalizedString StickyTarget = @"Sticky Target";
From c3f32e8632438fd643b41cae42bec1858b048476 Mon Sep 17 00:00:00 2001
From: Arufonsu <17498701+Arufonsu@users.noreply.github.com>
Date: Sat, 23 Nov 2024 14:54:31 -0300
Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E2=99=BB=EF=B8=8F=20Apply=20review?=
=?UTF-8?q?=20changes=20(I)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Intersect.Client.Framework/Gwen/Control/Base.cs | 10 +++++++---
Intersect.Client/Core/Input.cs | 6 +++---
Intersect.Client/Interface/Game/Menu.cs | 6 +++---
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/Intersect.Client.Framework/Gwen/Control/Base.cs b/Intersect.Client.Framework/Gwen/Control/Base.cs
index 39fbc1a9a..c730eb207 100644
--- a/Intersect.Client.Framework/Gwen/Control/Base.cs
+++ b/Intersect.Client.Framework/Gwen/Control/Base.cs
@@ -766,9 +766,13 @@ public virtual void Dispose()
Animation.Cancel(this);
// [Fix]: "InvalidOperationException: Collection was modified (during iteration); enumeration operation may not execute".
- // (Creates a copy of the children list to avoid modifying the collection during iteration).
- var childrenCopy = new List(mChildren);
- childrenCopy.ForEach(child => child.Dispose());
+ // (Creates an array copy of the children to avoid modifying the collection during iteration).
+ var children = mChildren.ToArray();
+ foreach (var child in children)
+ {
+ child.Dispose();
+ }
+
mChildren?.Clear();
mInnerPanel?.Dispose();
diff --git a/Intersect.Client/Core/Input.cs b/Intersect.Client/Core/Input.cs
index 02964631c..2367f4f8d 100644
--- a/Intersect.Client/Core/Input.cs
+++ b/Intersect.Client/Core/Input.cs
@@ -135,13 +135,13 @@ public static void OnKeyPressed(Keys modifier, Keys key)
{
var simplifiedEscapeMenuSetting = Globals.Database.SimplifiedEscapeMenu;
- if (!simplifiedEscapeMenuSetting)
+ if (simplifiedEscapeMenuSetting)
{
- Interface.Interface.GameUi?.EscapeMenu?.ToggleHidden();
+ Interface.Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
}
else
{
- Interface.Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
+ Interface.Interface.GameUi?.EscapeMenu?.ToggleHidden();
}
}
}
diff --git a/Intersect.Client/Interface/Game/Menu.cs b/Intersect.Client/Interface/Game/Menu.cs
index aae01d550..48ebc8556 100644
--- a/Intersect.Client/Interface/Game/Menu.cs
+++ b/Intersect.Client/Interface/Game/Menu.cs
@@ -359,13 +359,13 @@ private static void MenuButtonClicked(Base sender, ClickedEventArgs arguments)
{
var simplifiedEscapeMenuSetting = Globals.Database.SimplifiedEscapeMenu;
- if (!simplifiedEscapeMenuSetting)
+ if (simplifiedEscapeMenuSetting)
{
- Interface.GameUi?.EscapeMenu?.ToggleHidden();
+ Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
}
else
{
- Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
+ Interface.GameUi?.EscapeMenu?.ToggleHidden();
}
}
From b33cc08c375289bece8327ca0a0fb1cc725dcdc7 Mon Sep 17 00:00:00 2001
From: Arufonsu <17498701+Arufonsu@users.noreply.github.com>
Date: Tue, 26 Nov 2024 19:15:02 -0300
Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E2=99=BB=EF=B8=8F=20Apply=20review?=
=?UTF-8?q?=20changes=20(II)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Intersect.Client/Interface/Game/Menu.cs | 6 +++---
.../Interface/Game/SimplifiedEscapeMenu.cs | 12 ++++++------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/Intersect.Client/Interface/Game/Menu.cs b/Intersect.Client/Interface/Game/Menu.cs
index 48ebc8556..7b41abcc3 100644
--- a/Intersect.Client/Interface/Game/Menu.cs
+++ b/Intersect.Client/Interface/Game/Menu.cs
@@ -37,7 +37,7 @@ public partial class Menu
private readonly ImagePanel mMenuBackground;
- public readonly Button mMenuButton;
+ private readonly Button mMenuButton;
//Menu Container
private readonly ImagePanel mMenuContainer;
@@ -355,13 +355,13 @@ public bool HasWindowsOpen()
}
//Input Handlers
- private static void MenuButtonClicked(Base sender, ClickedEventArgs arguments)
+ private void MenuButtonClicked(Base sender, ClickedEventArgs arguments)
{
var simplifiedEscapeMenuSetting = Globals.Database.SimplifiedEscapeMenu;
if (simplifiedEscapeMenuSetting)
{
- Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden();
+ Interface.GameUi?.SimplifiedEscapeMenu?.ToggleHidden(mMenuButton);
}
else
{
diff --git a/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs b/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs
index 31ff568fb..3e315befc 100644
--- a/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs
+++ b/Intersect.Client/Interface/Game/SimplifiedEscapeMenu.cs
@@ -39,9 +39,9 @@ public SimplifiedEscapeMenu(Canvas gameCanvas) : base(gameCanvas, nameof(Simplif
LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer?.GetResolutionString());
}
- public override void ToggleHidden()
+ public void ToggleHidden(Button? target)
{
- if (!_settingsWindow.IsHidden)
+ if (!_settingsWindow.IsHidden || target == null)
{
return;
}
@@ -49,14 +49,14 @@ public override void ToggleHidden()
if (this.IsHidden)
{
// Position the context menu within the game canvas if near borders.
- var menuPosX = Interface.GameUi.GameMenu.mMenuButton.LocalPosToCanvas(new Point(0, 0)).X;
- var menuPosY = Interface.GameUi.GameMenu.mMenuButton.LocalPosToCanvas(new Point(0, 0)).Y;
+ var menuPosX = target.LocalPosToCanvas(new Point(0, 0)).X;
+ var menuPosY = target.LocalPosToCanvas(new Point(0, 0)).Y;
var newX = menuPosX;
- var newY = menuPosY + Interface.GameUi.GameMenu.mMenuButton.Height + 6;
+ var newY = menuPosY + target.Height + 6;
if (newX + Width >= Canvas?.Width)
{
- newX = menuPosX - Width + Interface.GameUi.GameMenu.mMenuButton.Width;
+ newX = menuPosX - Width + target.Width;
}
if (newY + Height >= Canvas?.Height)