From 9ab5a000ec6c0f8083bc4b92f76c5a550d7bd7ad Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Sun, 25 Jan 2026 01:01:00 +0000 Subject: [PATCH 01/11] Use alias for cvar controls --- ...heckButton.cs => CvarNegateCheckButton.cs} | 7 +++--- Game.UI/{CCvarSlider.cs => CvarSlider.cs} | 11 +++++----- ...heckButton.cs => CvarToggleCheckButton.cs} | 7 +++--- ...dComboBox.cs => LabeledCommandComboBox.cs} | 7 +++--- Game.UI/OptionsDialog.cs | 22 ++++++++++++++----- Game.UI/OptionsSubAudio.cs | 6 ++--- Game.UI/OptionsSubMouse.cs | 22 +++++++++---------- Game.UI/OptionsSubVoice.cs | 6 ++--- Source.GUI.Controls/CheckButton.cs | 2 +- Source.GUI.Controls/ComboBox.cs | 2 +- Source.GUI.Controls/Panel.cs | 2 ++ 11 files changed, 55 insertions(+), 39 deletions(-) rename Game.UI/{CCvarNegateCheckButton.cs => CvarNegateCheckButton.cs} (87%) rename Game.UI/{CCvarSlider.cs => CvarSlider.cs} (91%) rename Game.UI/{CCvarToggleCheckButton.cs => CvarToggleCheckButton.cs} (87%) rename Game.UI/{CLabeledCommandComboBox.cs => LabeledCommandComboBox.cs} (91%) diff --git a/Game.UI/CCvarNegateCheckButton.cs b/Game.UI/CvarNegateCheckButton.cs similarity index 87% rename from Game.UI/CCvarNegateCheckButton.cs rename to Game.UI/CvarNegateCheckButton.cs index 9e3ebfd1..61b979f2 100644 --- a/Game.UI/CCvarNegateCheckButton.cs +++ b/Game.UI/CvarNegateCheckButton.cs @@ -5,14 +5,15 @@ namespace Game.UI; -public class CCvarNegateCheckButton : CheckButton +[PanelAlias("CCvarNegateCheckButton")] +public class CvarNegateCheckButton : CheckButton { - public static Panel Create_CCvarNegateCheckButton() => new CCvarNegateCheckButton(null, null, null, null); + public static Panel Create_CvarNegateCheckButton() => new CvarNegateCheckButton(null, null, null, null); string? CvarName; bool StartState; - public CCvarNegateCheckButton(Panel parent, ReadOnlySpan name, ReadOnlySpan text, ReadOnlySpan cvarName) : base(parent, name, text) { + public CvarNegateCheckButton(Panel? parent, ReadOnlySpan name, ReadOnlySpan text, ReadOnlySpan cvarName) : base(parent, name, text) { CvarName = cvarName.Length > 0 ? new(cvarName) : null; Reset(); AddActionSignalTarget(this); diff --git a/Game.UI/CCvarSlider.cs b/Game.UI/CvarSlider.cs similarity index 91% rename from Game.UI/CCvarSlider.cs rename to Game.UI/CvarSlider.cs index e174c6fe..3f35e359 100644 --- a/Game.UI/CCvarSlider.cs +++ b/Game.UI/CvarSlider.cs @@ -6,11 +6,12 @@ namespace Game.UI; -public class CCvarSlider : Slider +[PanelAlias("CCvarSlider")] +public class CvarSlider : Slider { - public static Panel Create_CCvarSlider() => new CCvarSlider(null, null); + public static Panel Create_CvarSlider() => new CvarSlider(null, null); - [PanelAnimationVar("use_convar_minmax", "0", "bool")] bool UseCvarMinMax; + [PanelAnimationVar("use_convar_minmax", "0", "bool")] protected bool UseCvarMinMax; bool AllowOutOfRange; bool ModifiedOnce; float StartValue; @@ -21,13 +22,13 @@ public class CCvarSlider : Slider bool CreatedInCode; float MinValue; float MaxValue; - public CCvarSlider(Panel? panel, ReadOnlySpan name) : base(panel, name) { + public CvarSlider(Panel? panel, ReadOnlySpan name) : base(panel, name) { SetupSlider(0, 1, "", false); CreatedInCode = false; AddActionSignalTarget(this); } - public CCvarSlider(Panel parent, ReadOnlySpan name, ReadOnlySpan text, float minValue, float maxValue, ReadOnlySpan cvarName, bool allowOutOfRange = false) : base(parent, name) { + public CvarSlider(Panel parent, ReadOnlySpan name, ReadOnlySpan text, float minValue, float maxValue, ReadOnlySpan cvarName, bool allowOutOfRange = false) : base(parent, name) { AddActionSignalTarget(this); SetupSlider(minValue, maxValue, cvarName, allowOutOfRange); CreatedInCode = true; diff --git a/Game.UI/CCvarToggleCheckButton.cs b/Game.UI/CvarToggleCheckButton.cs similarity index 87% rename from Game.UI/CCvarToggleCheckButton.cs rename to Game.UI/CvarToggleCheckButton.cs index 3da7a623..f610a3cb 100644 --- a/Game.UI/CCvarToggleCheckButton.cs +++ b/Game.UI/CvarToggleCheckButton.cs @@ -4,14 +4,15 @@ namespace Game.UI; -public class CCvarToggleCheckButton : CheckButton +[PanelAlias("CCvarToggleCheckButton")] +public class CvarToggleCheckButton : CheckButton { - public static Panel Create_CCvarToggleCheckButton() => new CCvarToggleCheckButton(null, null, "CvarToggleCheckButton", null); + public static Panel Create_CvarToggleCheckButton() => new CvarToggleCheckButton(null, null, "CvarToggleCheckButton", null); string? CvarName; bool StartValue; - public CCvarToggleCheckButton(Panel parent, ReadOnlySpan name, ReadOnlySpan text, ReadOnlySpan cvarName) : base(parent, name, text) { + public CvarToggleCheckButton(Panel? parent, ReadOnlySpan name, ReadOnlySpan text, ReadOnlySpan cvarName) : base(parent, name, text) { CvarName = cvarName.Length > 0 ? new(cvarName) : null; if (CvarName != null) Reset(); diff --git a/Game.UI/CLabeledCommandComboBox.cs b/Game.UI/LabeledCommandComboBox.cs similarity index 91% rename from Game.UI/CLabeledCommandComboBox.cs rename to Game.UI/LabeledCommandComboBox.cs index a0d2d12e..db814c45 100644 --- a/Game.UI/CLabeledCommandComboBox.cs +++ b/Game.UI/LabeledCommandComboBox.cs @@ -4,7 +4,8 @@ namespace Game.UI; -public class CLabeledCommandComboBox : ComboBox +[PanelAlias("CLabeledCommandComboBox")] +public class LabeledCommandComboBox : ComboBox { const int MAX_NAME_LEN = 256; const int MAX_COMMAND_LEN = 256; @@ -20,9 +21,9 @@ struct CommandItem int CurrentSelection; int StartSelection; - public static Panel Create_CLabeledCommandComboBox() => new CLabeledCommandComboBox(null, null); + public static Panel Create_LabeledCommandComboBox() => new LabeledCommandComboBox(null, null); - public CLabeledCommandComboBox(Panel parent, ReadOnlySpan name) : base(parent, name, 0, false) { + public LabeledCommandComboBox(Panel? parent, ReadOnlySpan name) : base(parent, name, 0, false) { AddActionSignalTarget(this); CurrentSelection = -1; StartSelection = -1; diff --git a/Game.UI/OptionsDialog.cs b/Game.UI/OptionsDialog.cs index 99b61abf..19afabbc 100644 --- a/Game.UI/OptionsDialog.cs +++ b/Game.UI/OptionsDialog.cs @@ -21,21 +21,30 @@ public OptionsDialog(Panel? parent) : base(parent, "OptionsDialog") { SetTitle("#GameUI_Options", true); +#if GMOD_DLL // gmod's page order is different + OptionsSubMultiplayer = new OptionsSubMultiplayer(this, "OptionsSubMultiplayer"); + AddPage(OptionsSubMultiplayer, "#GameUI_Multiplayer"); + AddPage(new OptionsSubKeyboard(this, null), "#GameUI_Keyboard"); + AddPage(new OptionsSubMouse(this, null), "#GameUI_Mouse"); + OptionsSubAudio = new(this, null); + AddPage(OptionsSubAudio, "#GameUI_Audio"); + OptionsSubVideo = new(this, null); + AddPage(OptionsSubVideo, "#GameUI_Video"); + AddPage(new OptionsSubVoice(this, null), "#GameUI_Voice"); + // AddPage(new OptionsSubLegal(this, null), "#GameUI_Legal"); +#else #if WIN32 ConVarRef hap_HasDevice = new("hap_HasDevice"); hap_HasDevice.Init("hap_HasDevice", true); - if (hap_HasDevice.GetBool()) { + // if (hap_HasDevice.GetBool()) // AddPage(new OptionsSubHaptics(this), "#GameUI_Haptics_TabTitle"); - } #endif - if (ModInfo.IsSinglePlayerOnly() && !ModInfo.NoDifficulty()) { + // if (ModInfo.IsSinglePlayerOnly() && !ModInfo.NoDifficulty()) // AddPage(new OptionsSubDifficulty(this), "#GameUI_Difficulty"); - } - if (ModInfo.HasPortals()) { + // if (ModInfo.HasPortals()) // AddPage(new OptionsSubPortal(this), "#GameUI_Portal"); - } AddPage(new OptionsSubKeyboard(this, null), "#GameUI_Keyboard"); AddPage(new OptionsSubMouse(this, null), "#GameUI_Mouse"); @@ -53,6 +62,7 @@ public OptionsDialog(Panel? parent) : base(parent, "OptionsDialog") { OptionsSubMultiplayer = new OptionsSubMultiplayer(this, "OptionsSubMultiplayer"); AddPage(OptionsSubMultiplayer, "#GameUI_Multiplayer"); } +#endif SetApplyButtonVisible(true); GetPropertySheet().SetTabWidth(84); diff --git a/Game.UI/OptionsSubAudio.cs b/Game.UI/OptionsSubAudio.cs index d6833563..81970bf2 100644 --- a/Game.UI/OptionsSubAudio.cs +++ b/Game.UI/OptionsSubAudio.cs @@ -16,14 +16,14 @@ public class OptionsSubAudio : PropertyPage { ComboBox SpeakerSetupCombo; ComboBox SoundQualityCombo; - CCvarSlider SFXSlider; - CCvarSlider MusicSlider; + CvarSlider SFXSlider; + CvarSlider MusicSlider; ComboBox CloseCaptionCombo; bool RequireRestart; ComboBox SpokenLanguageCombo; // ELanguage CurrentAudioLanguage; // char[] UpdatedAudioLanguage; - CCvarToggleCheckButton SoundMuteLoseFocusCheckButton; + CvarToggleCheckButton SoundMuteLoseFocusCheckButton; public OptionsSubAudio(Panel? parent, ReadOnlySpan name) : base(parent, name) { SFXSlider = new(this, "SFXSlider", "#GameUI_SoundEffectVolume", 0.0f, 1.0f, "volume"); diff --git a/Game.UI/OptionsSubMouse.cs b/Game.UI/OptionsSubMouse.cs index 963f4374..5bb309c2 100644 --- a/Game.UI/OptionsSubMouse.cs +++ b/Game.UI/OptionsSubMouse.cs @@ -7,21 +7,21 @@ namespace Game.UI; public class OptionsSubMouse : PropertyPage { - CCvarNegateCheckButton ReverseMouseCheckBox; - CCvarToggleCheckButton MouseFilterCheckBox; - CCvarToggleCheckButton MouseRawCheckBox; + CvarNegateCheckButton ReverseMouseCheckBox; + CvarToggleCheckButton MouseFilterCheckBox; + CvarToggleCheckButton MouseRawCheckBox; CheckButton MouseAccelerationCheckBox; - CCvarToggleCheckButton JoystickCheckBox; - CCvarToggleCheckButton JoystickSouthpawCheckBox; - CCvarToggleCheckButton QuickInfoCheckBox; - CCvarToggleCheckButton ReverseJoystickCheckBox; - CCvarSlider MouseSensitivitySlider; + CvarToggleCheckButton JoystickCheckBox; + CvarToggleCheckButton JoystickSouthpawCheckBox; + CvarToggleCheckButton QuickInfoCheckBox; + CvarToggleCheckButton ReverseJoystickCheckBox; + CvarSlider MouseSensitivitySlider; TextEntry MouseSensitivityLabel; - CCvarSlider MouseAccelExponentSlider; + CvarSlider MouseAccelExponentSlider; TextEntry MouseAccelExponentLabel; - CCvarSlider JoyYawSensitivitySlider; + CvarSlider JoyYawSensitivitySlider; Label JoyYawSensitivityPreLabel; - CCvarSlider JoyPitchSensitivitySlider; + CvarSlider JoyPitchSensitivitySlider; Label JoyPitchSensitivityPreLabel; public OptionsSubMouse(Panel? parent, ReadOnlySpan name) : base(parent, name) { diff --git a/Game.UI/OptionsSubVoice.cs b/Game.UI/OptionsSubVoice.cs index 25b5d626..e767846e 100644 --- a/Game.UI/OptionsSubVoice.cs +++ b/Game.UI/OptionsSubVoice.cs @@ -31,8 +31,8 @@ public class OptionsSubVoice : PropertyPage Label MicrophoneSliderLabel; Slider MicrophoneVolume; Label ReceiveSliderLabel; - CCvarSlider ReceiveVolume; - CCvarToggleCheckButton VoiceEnableCheckButton; + CvarSlider ReceiveVolume; + CvarToggleCheckButton VoiceEnableCheckButton; int MicVolumeValue; bool MicBoostSelected; @@ -54,7 +54,7 @@ public OptionsSubVoice(Panel? parent, ReadOnlySpan name) : base(parent, na MicrophoneVolume.SetRange(0, 100); MicrophoneVolume.AddActionSignalTarget(this); - VoiceEnableCheckButton = new CCvarToggleCheckButton(this, "voice_modenable", "#GameUI_EnableVoice", "voice_modenable"); + VoiceEnableCheckButton = new CvarToggleCheckButton(this, "voice_modenable", "#GameUI_EnableVoice", "voice_modenable"); MicBoost = new CheckButton(this, "MicBoost", "#GameUI_BoostMicrophone"); MicBoost.AddActionSignalTarget(this); diff --git a/Source.GUI.Controls/CheckButton.cs b/Source.GUI.Controls/CheckButton.cs index 380d5b2d..6df44aae 100644 --- a/Source.GUI.Controls/CheckButton.cs +++ b/Source.GUI.Controls/CheckButton.cs @@ -60,7 +60,7 @@ public class CheckButton : ToggleButton Color DisabledBgColor; Color HighlightFgColor; - public CheckButton(Panel parent, ReadOnlySpan name, ReadOnlySpan text) : base(parent, name, text) { + public CheckButton(Panel? parent, ReadOnlySpan name, ReadOnlySpan text) : base(parent, name, text) { SetContentAlignment(Alignment.West); CheckButtonCheckable = true; UseSmallCheckImage = false; diff --git a/Source.GUI.Controls/ComboBox.cs b/Source.GUI.Controls/ComboBox.cs index a88b7639..69ee6e1a 100644 --- a/Source.GUI.Controls/ComboBox.cs +++ b/Source.GUI.Controls/ComboBox.cs @@ -57,7 +57,7 @@ public class ComboBox : TextEntry int OpenOffsetY; char[] BorderOverride = new char[64]; - public ComboBox(Panel parent, ReadOnlySpan name, int numLines, bool allowEdit) : base(parent, name) { + public ComboBox(Panel? parent, ReadOnlySpan name, int numLines, bool allowEdit) : base(parent, name) { SetEditable(allowEdit); SetHorizontalScrolling(false); diff --git a/Source.GUI.Controls/Panel.cs b/Source.GUI.Controls/Panel.cs index ec709be4..280eea59 100644 --- a/Source.GUI.Controls/Panel.cs +++ b/Source.GUI.Controls/Panel.cs @@ -2672,6 +2672,8 @@ public static void InitializeControls() { if (aliasAttr != null) { UtlSymbol aliasSymbol = new(aliasAttr.Alias); PanelNames[aliasSymbol] = type; + if (method != null) + PanelFactories[aliasSymbol] = method.CreateDelegate(); } count++; From 06f185f554a3f6697e6e286294842637f8727ce3 Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Sun, 25 Jan 2026 04:15:43 +0000 Subject: [PATCH 02/11] BasePanel impl --- Game.ServerBrowser/ServerBrowserDialog.cs | 4 +- Game.UI/BasePanel.cs | 177 +++++++++++++++++----- Game.UI/ControlsListPanel.cs | 1 - Game.UI/GameConsole.cs | 7 +- Source.GUI.Controls/Menu.cs | 2 +- 5 files changed, 144 insertions(+), 47 deletions(-) diff --git a/Game.ServerBrowser/ServerBrowserDialog.cs b/Game.ServerBrowser/ServerBrowserDialog.cs index 311c7f47..6b9375c9 100644 --- a/Game.ServerBrowser/ServerBrowserDialog.cs +++ b/Game.ServerBrowser/ServerBrowserDialog.cs @@ -2,6 +2,7 @@ using Source.Common.GUI; using Source.Common.Input; using Source.Common.ServerBrowser; +using Source.Engine; using Source.GUI.Controls; using Steamworks; @@ -105,8 +106,7 @@ public void Open() { public override void OnTick() { base.OnTick(); - GetAnimationController().UpdateAnimations(System.GetFrameTime()); - SetAlpha(255);// FIXME ^ is not working :( + GetAnimationController().UpdateAnimations(Sys.Time); SteamAPI.RunCallbacks(); // FIXME: should not be here } diff --git a/Game.UI/BasePanel.cs b/Game.UI/BasePanel.cs index d827dd48..f43fc31e 100644 --- a/Game.UI/BasePanel.cs +++ b/Game.UI/BasePanel.cs @@ -7,18 +7,17 @@ using Source.Common.GameUI; using Source.Common.GUI; using Source.Common.Input; +using Source.Engine; using Source.GUI.Controls; namespace Game.UI; public class GameMenuItem : MenuItem { - public GameMenuItem(Menu panel, string name, string text) : base(panel, name, text) { - RightAligned = false; - } - bool RightAligned; + public GameMenuItem(Menu panel, ReadOnlySpan name, ReadOnlySpan text) : base(panel, name, text) => RightAligned = false; + public override void ApplySchemeSettings(IScheme scheme) { base.ApplySchemeSettings(scheme); @@ -60,14 +59,14 @@ public enum BackgroundState MainMenu, Level, Disconnected, - Exiting, + Exiting } -public class GameMenu(Panel parent, string name) : Menu(parent, name) +public class GameMenu(Panel parent, ReadOnlySpan name) : Menu(parent, name) { protected override void LayoutMenuBorder() { } public override int AddMenuItem(ReadOnlySpan itemName, ReadOnlySpan itemText, ReadOnlySpan command, Panel? target, KeyValues? userData = null) { - MenuItem item = new GameMenuItem(this, new string(itemName), new string(itemText)); + MenuItem item = new GameMenuItem(this, itemName, itemText); item.AddActionSignalTarget(target); item.SetCommand(command); item.SetText(itemText); @@ -75,7 +74,7 @@ public override int AddMenuItem(ReadOnlySpan itemName, ReadOnlySpan return base.AddMenuItem(item); } public override int AddMenuItem(ReadOnlySpan itemName, ReadOnlySpan itemText, KeyValues command, Panel? target, KeyValues? userData = null) { - MenuItem item = new GameMenuItem(this, new string(itemName), new string(itemText)); + MenuItem item = new GameMenuItem(this, itemName, itemText); item.AddActionSignalTarget(target); item.SetCommand(command); item.SetText(itemText); @@ -105,6 +104,27 @@ public void UpdateMenuItemState(bool isInGame, bool isMultiplayer) { } } + if (!isInGame) { + for (int j = 0; j < GetChildCount() - 2; j++) + MoveMenuItem(j, j + 1); + } + else { + for (int i = 0; i < GetChildCount(); i++) { + for (int j = i; j < GetChildCount() - 2; j++) { + int id1 = GetMenuID(j); + int id2 = GetMenuID(j + 1); + + MenuItem menuItem1 = GetMenuItem(id1)!; + MenuItem menuItem2 = GetMenuItem(id2)!; + KeyValues kv1 = menuItem1.GetUserData()!; + KeyValues kv2 = menuItem2.GetUserData()!; + + if (kv1.GetInt("InGameOrder") > kv2.GetInt("InGameOrder")) + MoveMenuItem(id2, id1); + } + } + } + InvalidateLayout(); } @@ -123,6 +143,7 @@ public override void SetVisible(bool state) { if (!state) MoveToBack(); } + public override void ApplySchemeSettings(IScheme scheme) { base.ApplySchemeSettings(scheme); @@ -130,9 +151,11 @@ public override void ApplySchemeSettings(IScheme scheme) { SetBgColor(new(0, 0, 0, 0)); SetBorder(null); } + public override void OnSetFocus() { base.OnSetFocus(); } + public override void OnKeyCodePressed(ButtonCode code) { int dir = 0; switch (code) { @@ -168,9 +191,7 @@ public class MainMenuGameLogo : EditablePanel int OffsetX; int OffsetY; - public MainMenuGameLogo(Panel? parent, string name) : base(parent, name) { - - } + public MainMenuGameLogo(Panel? parent, ReadOnlySpan name) : base(parent, name) { } public override void ApplySettings(KeyValues resourceData) { base.ApplySettings(resourceData); @@ -189,12 +210,14 @@ public override void ApplySchemeSettings(IScheme scheme) { LoadControlSettings("resource/GameLogo.res", null, null, conditions); } + + public int GetOffsetX() => OffsetX; + public int GetOffsetY() => OffsetY; } public class BackgroundMenuButton : Button { - public BackgroundMenuButton(Panel parent, string name) : base(parent, name, "") { + public BackgroundMenuButton(Panel parent, ReadOnlySpan name) : base(parent, name, "") { } - } public override void ApplySchemeSettings(IScheme scheme) { base.ApplySchemeSettings(scheme); @@ -213,9 +236,7 @@ public override void ApplySchemeSettings(IScheme scheme) { } public class QuitQueryBox : QueryBox { - public QuitQueryBox(string title, string queryText, Panel? parent = null) : base(title, queryText, parent) { - - } + public QuitQueryBox(ReadOnlySpan title, ReadOnlySpan queryText, Panel? parent = null) : base(title, queryText, parent) { } IGameUI GameUI = Singleton(); @@ -240,18 +261,24 @@ public class BasePanel : Panel { GameMenu? GameMenu; -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. readonly public IFileSystem FileSystem = Singleton(); readonly public GameUI GameUI; readonly public IEngineClient engine = Singleton(); readonly public ModInfo ModInfo = Singleton(); -#pragma warning restore CS8618 TextureID BackgroundImageID = TextureID.INVALID; + TextureID LoadingImageID = TextureID.INVALID; OptionsDialog? OptionsDialog; - static BasePanel? g_BasePanel; + bool FadingInMenus; + TimeUnit_t FadeMenuStartTime; + TimeUnit_t FadeMenuEndTime; + bool RenderingBackgroundTransition; + TimeUnit_t TransitionStartTime; + TimeUnit_t TransitionEndTime; + + static BasePanel? g_BasePanel; public override void OnCommand(ReadOnlySpan command) { RunMenuCommand(command); @@ -259,8 +286,10 @@ public override void OnCommand(ReadOnlySpan command) { [ConCommand("gamemenucommand")] static void Gamemenucommand(in TokenizedCommand args) { - if (args.ArgC() < 2) + if (args.ArgC() < 2) { + Msg("Usage: gamemenucommand \n"); return; + } g_BasePanel?.RunMenuCommand(args[1]); } @@ -268,7 +297,7 @@ static void Gamemenucommand(in TokenizedCommand args) { private void RunMenuCommand(ReadOnlySpan command) { DevMsg($"Incoming BasePanel message '{command}'\n"); switch (command) { - case "OpenGameMenu": break; + case "OpenGameMenu": PostMessage(GameMenu, new("Command", "command", "open")); break; case "OpenPlayerListDialog": break; case "OpenNewGameDialog": break; case "OpenLoadGameDialog": break; @@ -363,7 +392,7 @@ public void OnOpenQuitConfirmationDialog() { } static BackgroundMenuButton CreateMenuButton(BasePanel parent, ReadOnlySpan panelName, ReadOnlySpan panelText) { - BackgroundMenuButton button = new BackgroundMenuButton(parent, new string(panelName)); + BackgroundMenuButton button = new BackgroundMenuButton(parent, panelName); button.SetProportional(true); button.SetCommand("OpenGameMenu"); button.SetText(panelText); @@ -382,7 +411,7 @@ public BasePanel(GameUI gameUI) : base(null, "BaseGameUIPanel") { GameMenuButtons.Add(CreateMenuButton(this, "GameMenuButton2", ModInfo!.GetGameTitle2())); } - [PanelAnimationVar("0")] float BackgroundFillAlpha; + [PanelAnimationVar("0")] protected float BackgroundFillAlpha; IFont? FontTest; @@ -403,8 +432,8 @@ public override void PaintBackground() { public override void PerformLayout() { base.PerformLayout(); - Surface.GetScreenSize(out int wide, out int tall); - GameMenu!.GetSize(out int menuWide, out int menuTall); + Surface.GetScreenSize(out _, out int tall); + GameMenu!.GetSize(out _, out int menuTall); int idealMenuY = GameMenuPos.Y; if (idealMenuY + menuTall + GameMenuInset > tall) idealMenuY = tall - menuTall - GameMenuInset; @@ -416,10 +445,7 @@ public override void PerformLayout() { GameMenuButtons[i].SetPos(GameTitlePos[i].X, GameTitlePos[i].Y + yDiff); } - for (int i = 0; i < GameMenuButtons.Count; i++) { - GameMenuButtons[i].SizeToContents(); - } - + GameLogo?.SetPos(GameMenuPos.X + GameLogo.GetOffsetX(), idealMenuY - GameLogo.GetTall() + GameLogo.GetOffsetY()); GameMenu.SetPos(GameMenuPos.X, idealMenuY); UpdateGameMenus(); @@ -433,6 +459,33 @@ public override void PerformLayout() { BackgroundState BackgroundState; public void SetBackgroundRenderState(BackgroundState state) { + if (state == BackgroundState) + return; + + double frametime = Sys.Time; + + RenderingBackgroundTransition = false; + FadingInMenus = false; + + if (state == BackgroundState.Exiting) { + // todo + } + else if (state == BackgroundState.Disconnected || state == BackgroundState.MainMenu) { + FadingInMenus = true; + FadeMenuStartTime = frametime; + FadeMenuEndTime = frametime + 3.0f; + + if (state == BackgroundState.MainMenu) { + RenderingBackgroundTransition = true; + TransitionStartTime = frametime; + TransitionEndTime = frametime + 3.0f; + } + } + else if (state == BackgroundState.Loading) + SetMenuAlpha(0); + else if (state == BackgroundState.Level) + SetMenuAlpha(255); + BackgroundState = state; } @@ -453,9 +506,9 @@ public void UpdateBackgroundState() { int i; bool haveActiveDialogs = false; - bool bIsInLevel = GameUI.IsInLevel(); + bool isInLevel = GameUI.IsInLevel(); for (i = 0; i < GetChildCount(); ++i) { - IPanel? child = GetChild(i); + Panel? child = GetChild(i); if (child != null && child.IsVisible() && child.IsPopup() && child != GameMenu) haveActiveDialogs = true; } @@ -467,7 +520,7 @@ public void UpdateBackgroundState() { haveActiveDialogs = true; } - bool needDarkenedBackground = (haveActiveDialogs || bIsInLevel); + bool needDarkenedBackground = haveActiveDialogs || isInLevel; if (HaveDarkenedBackground != needDarkenedBackground) { float targetAlpha, duration; if (needDarkenedBackground) { @@ -501,9 +554,9 @@ public void UpdateBackgroundState() { if (GameLogo != null) GetAnimationController().RunAnimationCommand(GameLogo, "alpha", targetTitleAlpha, 0.0f, duration, Interpolators.Linear); - for (i = 0; i < GameMenuButtons.Count; ++i) { + for (i = 0; i < GameMenuButtons.Count; ++i) GetAnimationController().RunAnimationCommand(GameMenuButtons[i], "alpha", targetTitleAlpha, 0.0f, duration, Interpolators.Linear); - } + HaveDarkenedTitleText = bNeedDarkenedTitleText; ForceTitleTextUpdate = false; } @@ -517,6 +570,7 @@ public void UpdateBackgroundState() { public void RunFrame() { InvalidateLayout(); + GetAnimationController().UpdateAnimations(Sys.Time); UpdateBackgroundState(); @@ -584,22 +638,59 @@ public override void ApplySchemeSettings(IScheme scheme) { BackgroundImageID = Surface.CreateNewTextureID(); Surface.DrawSetTextureFile(BackgroundImageID, finalFilename, 0, false); + + if (LoadingImageID == TextureID.INVALID) + LoadingImageID = Surface.CreateNewTextureID(); + + Surface.DrawSetTextureFile(LoadingImageID, "Console/startup_loading", 0, false); } private void DrawBackgroundImage() { + GetSize(out int wide, out int tall); + double frametime = Sys.Time; + int alpha = 255; - GetSize(out int wide, out int tall); + if (RenderingBackgroundTransition) { + alpha = (int)((TransitionEndTime - frametime) / (TransitionEndTime - TransitionStartTime) * 255); + alpha = Math.Clamp(alpha, 0, 255); + } + + if (ExitingFrameCount != 0) { + alpha = (int)((TransitionEndTime - frametime) / (TransitionEndTime - TransitionStartTime) * 255); + alpha = 255 - Math.Clamp(alpha, 0, 255); + } - TextureID imageID = BackgroundImageID; + if (RenderingBackgroundTransition || BackgroundState == BackgroundState.Loading) { + Surface.DrawSetColor(255, 255, 255, alpha); + Surface.DrawSetTexture(LoadingImageID); + Surface.DrawGetTextureSize(LoadingImageID, out int twide, out int ttall); + Surface.DrawTexturedRect(wide - twide, tall - ttall, wide, tall); + } +#if !GMOD_DLL + if (FadingInMenus) { + alpha = (int)((frametime - FadeMenuStartTime) / (FadeMenuEndTime - FadeMenuStartTime) * 255); + alpha = Math.Clamp(alpha, 0, 255); + GameMenu!.SetAlpha(alpha); + if (alpha == 255) + FadingInMenus = false; + } +#else Surface.DrawSetColor(255, 255, 255, alpha); - Surface.DrawSetTexture(imageID); + Surface.DrawSetTexture(BackgroundImageID); Surface.DrawTexturedRect(0, 0, wide, tall); +#endif } private void SetMenuAlpha(int alpha) { GameMenu!.SetAlpha(alpha); + GameLogo?.SetAlpha(alpha); + + for (int i = 0; i < GameMenuButtons.Count; ++i) + GameMenuButtons[i].SetAlpha(alpha); + + ForceTitleTextUpdate = true; } private void CreateGameMenu() { @@ -617,11 +708,12 @@ private void CreateGameMenu() { } public override void OnThink() { + // KeyRepeat todo base.OnThink(); } private GameMenu RecursiveLoadGameMenu(KeyValues datafile) { - GameMenu menu = new GameMenu(this, new string(datafile.Name)); + GameMenu menu = new GameMenu(this, datafile.Name); for (KeyValues? dat = datafile.GetFirstSubKey(); dat != null; dat = dat.GetNextKey()) { ReadOnlySpan label = dat.GetString("label", ""); ReadOnlySpan cmd = dat.GetString("command", null); @@ -640,6 +732,7 @@ private void CreateGameLogo() { GameLogo.MakeReadyForUse(); GameLogo.InvalidateLayout(true, true); + GameLogo.SetAlpha(0); } } @@ -671,11 +764,11 @@ internal void OnLevelLoadingFinished() { LevelLoading = false; } + readonly static KeyValues KV_GameUIHidden = new("GameUIHidden"); internal void OnGameUIHidden() { - + if (OptionsDialog.IsValid()) + PostMessage(OptionsDialog, KV_GameUIHidden); } - public int GetMenuAlpha() { - return GameMenu!.GetAlpha(); - } + public int GetMenuAlpha() => GameMenu!.GetAlpha(); } diff --git a/Game.UI/ControlsListPanel.cs b/Game.UI/ControlsListPanel.cs index 0a94a25e..41c838c6 100644 --- a/Game.UI/ControlsListPanel.cs +++ b/Game.UI/ControlsListPanel.cs @@ -1,4 +1,3 @@ -using Source.Common.Formats.Keyvalues; using Source.Common.GUI; using Source.Common.Input; using Source.GUI.Controls; diff --git a/Game.UI/GameConsole.cs b/Game.UI/GameConsole.cs index ac282812..19ff1fc9 100644 --- a/Game.UI/GameConsole.cs +++ b/Game.UI/GameConsole.cs @@ -1,8 +1,10 @@ -using Source.Common.Engine; +using Source.Common.Commands; +using Source.Common.Engine; using Source.Common.GameUI; using Source.Common.GUI; namespace Game.UI; + public class GameConsole(IEngineAPI engineAPI, ISurface Surface, ISchemeManager Scheme) : IGameConsole { bool Initialized; @@ -52,4 +54,7 @@ public void SetParent(IPanel? parent) { Console!.SetParent(parent); } + + [ConCommand("condump", "dump the text currently in the console to condumpXX.log")] + static void condump() => Msg("not implemented\n"); } \ No newline at end of file diff --git a/Source.GUI.Controls/Menu.cs b/Source.GUI.Controls/Menu.cs index 92c80953..660dee50 100644 --- a/Source.GUI.Controls/Menu.cs +++ b/Source.GUI.Controls/Menu.cs @@ -1305,7 +1305,7 @@ public void AddSeparatorAfterItem(int itemID) { SeparatorPanels.Add(new MenuSeparator(this, "MenuSeparator")); } - public void MoveMenuitem(int itemID, int moveBeforeThisItemID) { + public void MoveMenuItem(int itemID, int moveBeforeThisItemID) { int count = SortedItems.Count; int i; for (i = 0; i < count; i++) From 526b706b236407e884f358fbd9812f612e1e6f73 Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Mon, 26 Jan 2026 02:46:35 +0000 Subject: [PATCH 03/11] Hide these elements for now --- Game.Client/HL2/HudPoisonDamageIdicator.cs | 4 ++-- Game.Client/HL2/HudSuitPower.cs | 11 +++++------ Game.Client/HUD/BaseTimer.cs | 2 +- Source.Engine/DebugOverlay.cs | 4 +--- Source.Engine/VGui_DrawTreePanel.cs | 4 ++++ Source.GUI.Controls/Panel.cs | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Game.Client/HL2/HudPoisonDamageIdicator.cs b/Game.Client/HL2/HudPoisonDamageIdicator.cs index 4e6f3b02..cabd139c 100644 --- a/Game.Client/HL2/HudPoisonDamageIdicator.cs +++ b/Game.Client/HL2/HudPoisonDamageIdicator.cs @@ -7,10 +7,10 @@ namespace Game.Client.HL2; -[DeclareHudElement(Name = "CHudPoisonDamageIndicator")] +// [DeclareHudElement(Name = "CHudPoisonDamageIndicator")] // FIXME: Animations public class HudPoisonDamageIndicator : EditableHudElement, IHudElement { - [PanelAnimationVar("TextFont", "Default", "Font")] protected IFont Font; + [PanelAnimationVar("TextFont", "Default", "HFont")] protected IFont Font; [PanelAnimationVar("TextColor", "FgColor", "Color")] protected Color TextColor; [PanelAnimationVar("text_xpos", "8", "proportional_float")] protected float TextXPos; [PanelAnimationVar("text_ypos", "8", "proportional_float")] protected float TextYPos; diff --git a/Game.Client/HL2/HudSuitPower.cs b/Game.Client/HL2/HudSuitPower.cs index 57e6da98..2622acb6 100644 --- a/Game.Client/HL2/HudSuitPower.cs +++ b/Game.Client/HL2/HudSuitPower.cs @@ -7,7 +7,7 @@ namespace Game.Client.HL2; -[DeclareHudElement(Name = "CHudSuitPower")] +// [DeclareHudElement(Name = "CHudSuitPower")] // FIXME: Animations are not working here? just sits at the topleft of the screen :( public class HudSuitPower : EditableHudElement, IHudElement { [PanelAnimationVar("AuxPowerColor", "255 0 0 255", "Color")] protected Color AuxPowerColor; @@ -18,7 +18,7 @@ public class HudSuitPower : EditableHudElement, IHudElement [PanelAnimationVarAliasType("BarHeight", "10", "proportional_float")] protected float BarHeight; [PanelAnimationVarAliasType("BarChunkWidth", "10", "proportional_float")] protected float BarChunkWidth; [PanelAnimationVarAliasType("BarChunkGap", "2", "proportional_float")] protected float BarChunkGap; - [PanelAnimationVar("TextFont", "Default", "Font")] protected IFont TextFont; + [PanelAnimationVar("TextFont", "Default", "HFont")] protected IFont TextFont; [PanelAnimationVarAliasType("text_xpos", "8", "proportional_float")] protected float text_xpos; [PanelAnimationVarAliasType("text_ypos", "20", "proportional_float")] protected float text_ypos; [PanelAnimationVarAliasType("text2_xpos", "8", "proportional_float")] protected float text2_xpos; @@ -31,9 +31,8 @@ public class HudSuitPower : EditableHudElement, IHudElement const int SUIT_POWER_INIT = -1; public HudSuitPower(string? panelName) : base(null, "HudSuitPower") { - var parent = clientMode.GetViewport(); - SetParent(parent); - + ElementName = panelName; + SetParent(clientMode.GetViewport()); ((IHudElement)this).SetHiddenBits(HideHudBits.Health | HideHudBits.NeedSuit | HideHudBits.PlayerDead); } @@ -50,7 +49,7 @@ public bool ShouldDraw() { if (player == null) return false; - bool needsDraw = (player.HL2Local.SuitPower != SuitPower) || AuxPowerColor[3] > 0; + bool needsDraw = (player.HL2Local.SuitPower != SuitPower) || AuxPowerColor.A > 0; return needsDraw && IHudElement.DefaultShouldDraw(this); } diff --git a/Game.Client/HUD/BaseTimer.cs b/Game.Client/HUD/BaseTimer.cs index edec7dde..f69cbacf 100644 --- a/Game.Client/HUD/BaseTimer.cs +++ b/Game.Client/HUD/BaseTimer.cs @@ -4,7 +4,7 @@ namespace Game.Client.HUD; -[DeclareHudElement(Name = "CHudBaseTimer")] +// [DeclareHudElement(Name = "CHudBaseTimer")] class HudBaseTimer : HudNumericDisplay, IHudElement { int Minutes; diff --git a/Source.Engine/DebugOverlay.cs b/Source.Engine/DebugOverlay.cs index f891a06b..e3d309ed 100644 --- a/Source.Engine/DebugOverlay.cs +++ b/Source.Engine/DebugOverlay.cs @@ -1,10 +1,8 @@ using Source.Common; using Source.Common.Commands; using Source.Common.Engine; -using Source.Common.Formats.BSP; using Source.Common.Mathematics; -using System; using System.Numerics; namespace Source.Engine; @@ -235,7 +233,7 @@ public static void Draw3DOverlays() { DrawAllOverlays(); // if (s_bDrawGrid) - // DrawGridOverlay(); + // DrawGridOverlay(); } } public static void DrawAllOverlays() { diff --git a/Source.Engine/VGui_DrawTreePanel.cs b/Source.Engine/VGui_DrawTreePanel.cs index 990d5320..f3cd1f25 100644 --- a/Source.Engine/VGui_DrawTreePanel.cs +++ b/Source.Engine/VGui_DrawTreePanel.cs @@ -268,7 +268,11 @@ private static void RecursivePrintTree(IPanel current, KeyValues currentPanel, i if (!inputName.IsEmpty) sprintf(name, "%s").S(inputName); else +#if DEBUG + sprintf(name, "%s").S(current.GetClassName()); +#else sprintf(name, "%s").S(""); +#endif if (current.IsMouseInputEnabled()) sprintf(name, "%s, +m").S(name); if (current.IsKeyboardInputEnabled()) sprintf(name, "%s, +k").S(name); diff --git a/Source.GUI.Controls/Panel.cs b/Source.GUI.Controls/Panel.cs index 280eea59..2f9fa8f4 100644 --- a/Source.GUI.Controls/Panel.cs +++ b/Source.GUI.Controls/Panel.cs @@ -1522,7 +1522,7 @@ public void PaintTraverse(bool repaint, bool allowForce = true) { internal void VisualizeLayout(Panel panel) => LayoutVisualizations[panel] = System.GetCurrentTime() + 0.3; #endif - static readonly ConVar sdn_vgui_debug = new("sdn_vgui_debug", 0, "Show debug info for panels under the mouse cursor."); + static readonly ConVar sdn_vgui_debug = new("0", FCvar.None, "Show debug info for panels under the mouse cursor."); private void DebugVisualize() { if (sdn_vgui_debug.GetInt() == 0) return; From b0267b9acd68459ef862b1b8b6cbcfe6e4eb6945 Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Mon, 26 Jan 2026 22:12:56 +0000 Subject: [PATCH 04/11] HudHistoryResource impl --- Game.Client/C_BasePlayer.cs | 18 +- Game.Client/HL2/HudWeaponSelection.cs | 5 +- Game.Client/HistoryResource.cs | 307 +++++++++++++++++++++++++- Game.Client/WeaponsResource.cs | 23 +- 4 files changed, 324 insertions(+), 29 deletions(-) diff --git a/Game.Client/C_BasePlayer.cs b/Game.Client/C_BasePlayer.cs index 38787502..e1faa8c0 100644 --- a/Game.Client/C_BasePlayer.cs +++ b/Game.Client/C_BasePlayer.cs @@ -108,9 +108,7 @@ public override void OnDataChanged(DataUpdateType updateType) { if (pWeaponData == null || (pWeaponData.Flags & WeaponFlags.NoAmmoPickups) == 0) { // We got more ammo for this ammo index. Add it to the ammo history HudHistoryResource? pHudHR = GET_HUDELEMENT(); - // if (pHudHR != null) - //pHudHR.AddToHistory(HISTSLOT_AMMO, i, abs(GetAmmoCount(i) - m_iOldAmmo[i])); - + pHudHR?.AddToHistory(HRType.Ammo, i, Math.Abs(GetAmmoCount(i) - OldAmmo[i])); } } } @@ -131,7 +129,7 @@ public void SetViewAngles(in QAngle angles) { public override void ReceiveMessage(int classID, bf_read msg) { if (classID != GetClientClass().ClassID) { // message is for subclass - + base.ReceiveMessage(classID, msg); return; } @@ -335,10 +333,10 @@ public void SetLocalViewAngles(in QAngle angles) { } public BaseCombatWeapon? GetLastWeapon() => LastWeapon.Get(); - public static readonly ConVar cl_customsounds = new( "cl_customsounds", "0", 0, "Enable customized player sound playback" ); - public static readonly ConVar spec_track = new( "spec_track", "0", 0, "Tracks an entity in spec mode" ); - public static readonly ConVar cl_smooth = new( "cl_smooth", "1", 0, "Smooth view/eye origin after prediction errors" ); - public static readonly ConVar cl_smoothtime = new( + public static readonly ConVar cl_customsounds = new("cl_customsounds", "0", 0, "Enable customized player sound playback"); + public static readonly ConVar spec_track = new("spec_track", "0", 0, "Tracks an entity in spec mode"); + public static readonly ConVar cl_smooth = new("cl_smooth", "1", 0, "Smooth view/eye origin after prediction errors"); + public static readonly ConVar cl_smoothtime = new( "cl_smoothtime", "0.1", 0, @@ -350,7 +348,7 @@ public void SetLocalViewAngles(in QAngle angles) { public virtual bool CreateMove(TimeUnit_t inputSampleTime, ref UserCmd cmd) { return true; } - public void GetPredictionErrorSmoothingVector(out Vector3 offset){ + public void GetPredictionErrorSmoothingVector(out Vector3 offset) { if (engine.IsPlayingDemo() || cl_smooth.GetInt() == 0 || cl_predict.GetInt() == 0 || engine.IsPaused()) { offset = default; return; @@ -371,7 +369,7 @@ public void GetPredictionErrorSmoothingVector(out Vector3 offset){ public Vector3 PredictionError; public Vector3 PreviouslyPredictedOrigin; public TimeUnit_t PredictionErrorTime; - public void NotePredictionError(in Vector3 delta){ + public void NotePredictionError(in Vector3 delta) { if (!IsAlive()) return; diff --git a/Game.Client/HL2/HudWeaponSelection.cs b/Game.Client/HL2/HudWeaponSelection.cs index 38b6c805..d0c3bb20 100644 --- a/Game.Client/HL2/HudWeaponSelection.cs +++ b/Game.Client/HL2/HudWeaponSelection.cs @@ -7,8 +7,6 @@ using Source.Common.MaterialSystem; using Source.GUI.Controls; -using System.Security.Cryptography; - namespace Game.Client.HL2; [DeclareHudElement(Name = "CHudWeaponSelection")] @@ -90,7 +88,7 @@ public override void SetWeaponSelected() { void OnWeaponPickup(BaseCombatWeapon weapon) { HudHistoryResource? hr = gHUD.FindElement("CHudHistoryResource") as HudHistoryResource; - // hr?.AddToHistory(weapon); + hr?.AddToHistory(weapon); } public override void OnThink() { @@ -174,6 +172,7 @@ void ActivateFastswitchWeaponDisplay(BaseCombatWeapon selectedWeapon) { cWeapons++; } } + if (lastSelectedWeaponBox == -1) LastWeapon = null; diff --git a/Game.Client/HistoryResource.cs b/Game.Client/HistoryResource.cs index e95c120a..3c4667f6 100644 --- a/Game.Client/HistoryResource.cs +++ b/Game.Client/HistoryResource.cs @@ -1,8 +1,311 @@ +using Game.Client; using Game.Client.HUD; +using Game.Shared; -class HudHistoryResource : EditableHudElement +using Source; +using Source.Common.Bitbuffers; +using Source.Common.GUI; +using Source.GUI.Controls; + +enum HRType +{ + Empty, + Ammo, + Weapon, + Item, + AmmoDenied +} + +[DeclareHudElement(Name = "CHudHistoryResource")] +class HudHistoryResource : EditableHudElement, IHudElement { - public HudHistoryResource(string elementName) : base(null, elementName) { + struct HistItem(TimeUnit_t displayTime) + { + public HRType? Type; + public TimeUnit_t DisplayTime = displayTime; + public int Count; + public int Id; + public BaseCombatWeapon? Weapon; + public HudTexture? Icon; + } + readonly List PickupHistory = []; + + int HistoryGap; + int CurrentHistorySlot; + bool DoNotDraw; + InlineArray16 AmmoFullMsg; + bool NeedsDraw; + + [PanelAnimationVarAliasType("history_gap", "42", "proportional_float")] protected float fHistoryGap; + [PanelAnimationVarAliasType("icon_inset", "28", "proportional_float")] protected float IconInset; + [PanelAnimationVarAliasType("text_inset", "26", "proportional_float")] protected float TextInset; + [PanelAnimationVar("NumberFont", "HudNumbersSmall", "HFont")] protected IFont NumberFont; + [PanelAnimationVar("TextFont", "Default", "HFont")] protected IFont TextFont; + + public HudHistoryResource(string elementName) : base(null, "HudHistoryResource") { + ElementName = elementName; + SetParent(clientMode.GetViewport()); + + DoNotDraw = true; + AmmoFullMsg[0] = '\0'; + NeedsDraw = false; + + ((IHudElement)this).SetHiddenBits(HideHudBits.MiscStatus); + } + + public override void ApplySchemeSettings(IScheme scheme) { + base.ApplySchemeSettings(scheme); + SetPaintBackgroundEnabled(false); + + ReadOnlySpan wsc = localize.Find("#hl2_AmmoFull"); + if (!wsc.IsEmpty) + strcpy(AmmoFullMsg, wsc); + } + + public override void Init() { + IHudElement.HookMessage("ItemPickup", MsgFunc_ItemPickup); + IHudElement.HookMessage("AmmoDenied", MsgFunc_AmmoDenied); + Reset(); + } + + public void Reset() { + PickupHistory.Clear(); + CurrentHistorySlot = 0; + DoNotDraw = false; + } + + public virtual void SetHistoryGap(int gap) { } + + public void AddToHistory(BaseCombatWeapon weapon) { + if ((weapon.GetWpnData().Flags & WeaponFlags.Exhaustible) != 0) + return; + + int index = weapon.EntIndex(); + + for (int i = 0; index < PickupHistory.Count; i++) { + if (PickupHistory[i].Id == index) + return; + } + + AddIconToHistory(HRType.Weapon, index, weapon, 0, null); + } + + public void AddToHistory(HRType type, int id, int count = 0) { + if (type == HRType.Ammo) { + if (count == 0) + return; + + for (int i = 0; i < PickupHistory.Count; i++) { + var item = PickupHistory[i]; + if (item.Type == HRType.AmmoDenied && item.Id == id) { + item.DisplayTime = 0; + CurrentHistorySlot = i; + break; + } + } + } + + AddIconToHistory(type, id, null, count, null); + } + + private void AddToHistory(HRType type, ReadOnlySpan name, int count = 0) { + if (type != HRType.Item) + return; + + HudTexture? icon = gHUD.GetIcon(name); + if (icon == null) + return; + + AddIconToHistory(type, 1, null, count, icon); + } + + private void AddIconToHistory(HRType type, int id, BaseCombatWeapon? weapon, int count, HudTexture? icon) { + NeedsDraw = true; + + if ((fHistoryGap * (CurrentHistorySlot + 1)) > GetTall()) + CurrentHistorySlot = 0; + + if (CurrentHistorySlot == 0) + clientMode.GetViewportAnimationController()!.StartAnimationSequence("HintMessageLower"); + + while (PickupHistory.Count <= CurrentHistorySlot) + PickupHistory.Add(new HistItem(0)); + + HistItem freeslot = PickupHistory[CurrentHistorySlot]; + + if (type == HRType.AmmoDenied && freeslot.DisplayTime != 0) + return; + + freeslot.Id = id; + freeslot.Icon = icon; + freeslot.Type = type; + freeslot.Weapon = weapon; + freeslot.Count = count; + + if (type == HRType.AmmoDenied) + freeslot.DisplayTime = gpGlobals.CurTime + (BaseHudWeaponSelection.hud_drawhistory_time.GetFloat() / 2.0f); + else + freeslot.DisplayTime = gpGlobals.CurTime + BaseHudWeaponSelection.hud_drawhistory_time.GetFloat(); + + ++CurrentHistorySlot; + } + + private void MsgFunc_ItemPickup(bf_read msg) { + Span name = stackalloc char[1024]; + msg.ReadString(name); + AddToHistory(HRType.Item, name); + } + + private void MsgFunc_AmmoDenied(bf_read msg) { + int ammo = msg.ReadShort(); + + for (int i = 0; i < PickupHistory.Count; i++) { + var item = PickupHistory[i]; + if (item.Type == HRType.Ammo && item.Id == ammo) + break; + } + + for (int i = 0; i < PickupHistory.Count; i++) { + var item = PickupHistory[i]; + if (item.Type == HRType.AmmoDenied && item.Id == ammo) { + item.DisplayTime = gpGlobals.CurTime + (BaseHudWeaponSelection.hud_drawhistory_time.GetFloat() / 2.0f); + CurrentHistorySlot = i; + break; + } + } + + AddToHistory(HRType.AmmoDenied, ammo, 0); + } + + void CheckClearHistory() { + foreach (var item in PickupHistory) + if (item.Type != null) + return; + + CurrentHistorySlot = 0; + clientMode.GetViewportAnimationController()!.StartAnimationSequence("HintMessageRaise"); + } + + public bool ShouldDraw() => (CurrentHistorySlot > 0 || NeedsDraw) && IHudElement.DefaultShouldDraw(this); + + public override void Paint() { + if (DoNotDraw) { + DoNotDraw = false; + return; + } + + NeedsDraw = false; + + GetSize(out int wide, out int tall); + + for (int i = 0; i < PickupHistory.Count; i++) { + var item = PickupHistory[i]; + if (item.Type != null) { + item.DisplayTime = Math.Min(item.DisplayTime, gpGlobals.CurTime + BaseHudWeaponSelection.hud_drawhistory_time.GetFloat()); + if (item.DisplayTime <= gpGlobals.CurTime) { + PickupHistory[i] = new HistItem(0); + CheckClearHistory(); + continue; + } + + TimeUnit_t elapsed = item.DisplayTime - gpGlobals.CurTime; + float scale = (float)(elapsed * 80); + Color clr = gHUD.ClrNormal; + clr[3] = (byte)MathF.Min(scale, 255); + + bool useAmmoFullMsg = false; + + HudTexture? itemIcon = null; + HudTexture? itemAmmoIcon = null; + int amount = 0; + bool halfHeight = true; + + switch (item.Type) { + case HRType.Ammo: { +#if !HL2MP + var wpnInfo = gWR.GetWeaponFromAmmo(item.Id); + if (wpnInfo != null && (wpnInfo.MaxClip1 >= 0 || wpnInfo.MaxClip2 >= 0)) { + itemIcon = wpnInfo.IconSmall; + itemAmmoIcon = gWR.GetAmmoIconFromWeapon(item.Id); + } + else +#endif + { + itemIcon = gWR.GetAmmoIconFromWeapon(item.Id); + itemAmmoIcon = null; + } + + amount = item.Count; + } + break; + case HRType.AmmoDenied: { + itemIcon = gWR.GetAmmoIconFromWeapon(item.Id); + amount = 0; + useAmmoFullMsg = true; + clr = gHUD.ClrCaution; + clr[3] = (byte)MathF.Min(scale, 255); + } + break; + case HRType.Weapon: { + var weapon = item.Weapon; + if (weapon == null) + return; + + if (!weapon.HasAmmo()) { + clr = gHUD.ClrCaution; + clr[3] = (byte)MathF.Min(scale, 255); + } + + itemIcon = weapon.GetSpriteInactive(); + halfHeight = false; + } + break; + case HRType.Item: { + if (item.Id == 0) + continue; + itemIcon = item.Icon; + halfHeight = false; + } + break; + default: + Assert(false); + break; + } + + if (itemIcon == null) + continue; + + if (clr[3] != 0) + NeedsDraw = true; + + int ypos = tall - (HistoryGap * (i + 1)); + int xpos = wide - itemIcon.Width() - (int)IconInset; + if (halfHeight) + ypos += itemIcon.Height() / 2; + + itemIcon.DrawSelf(xpos, ypos, clr); + itemAmmoIcon?.DrawSelf((int)(xpos - (itemAmmoIcon.Width() * 1.25f)), ypos, clr); + + if (amount != 0) { + Span text = stackalloc char[16]; + sprintf(text, "%i").I(amount); + + ypos -= (Surface.GetFontTall(NumberFont) - itemIcon.Height()) / 2; + + Surface.DrawSetTextFont(NumberFont); + Surface.DrawSetTextColor(clr); + Surface.DrawSetTextPos(wide - (int)TextInset, ypos); + Surface.DrawString(text); + } + else if (useAmmoFullMsg) { + ypos -= (Surface.GetFontTall(TextFont) - itemIcon.Height()) / 2; + Surface.DrawSetTextFont(TextFont); + Surface.DrawSetTextColor(clr); + Surface.DrawSetTextPos(wide - (int)TextInset, ypos); + Surface.DrawString(AmmoFullMsg); + } + } + } } } \ No newline at end of file diff --git a/Game.Client/WeaponsResource.cs b/Game.Client/WeaponsResource.cs index b9d3b939..e9f611fb 100644 --- a/Game.Client/WeaponsResource.cs +++ b/Game.Client/WeaponsResource.cs @@ -91,32 +91,27 @@ internal void LoadWeaponSprites(WEAPON_FILE_INFO_HANDLE weaponFileInfo) { weaponInfo.IconZoomedAutoaim = weaponInfo.IconZoomedCrosshair; //default to zoomed crosshair } - // HudHistoryResource? pHudHR = GET_HUDELEMENT(); - object? pHudHR = null; // ^^ Todo, when HudHistoryResource is available... - if (pHudHR != null) { + HudHistoryResource? hudHR = GET_HUDELEMENT(); + if (hudHR != null) { p = FindHudTextureInDict(tempList, "weapon"); if (p != null) { weaponInfo.IconInactive = gHUD.AddUnsearchableHudIconToList(p); if (weaponInfo.IconInactive != null) { weaponInfo.IconInactive.Precache(); - // pHudHR.SetHistoryGap(weaponInfo.IconInactive.Height()); + hudHR.SetHistoryGap(weaponInfo.IconInactive.Height()); } } p = FindHudTextureInDict(tempList, "weapon_s"); if (p != null) { weaponInfo.IconActive = gHUD.AddUnsearchableHudIconToList(p); - if (weaponInfo.IconActive != null) { - weaponInfo.IconActive.Precache(); - } + weaponInfo.IconActive?.Precache(); } p = FindHudTextureInDict(tempList, "weapon_small"); if (p != null) { weaponInfo.IconSmall = gHUD.AddUnsearchableHudIconToList(p); - if (weaponInfo.IconSmall != null) { - weaponInfo.IconSmall.Precache(); - } + weaponInfo.IconSmall?.Precache(); } p = FindHudTextureInDict(tempList, "ammo"); @@ -124,7 +119,7 @@ internal void LoadWeaponSprites(WEAPON_FILE_INFO_HANDLE weaponFileInfo) { weaponInfo.IconAmmo = gHUD.AddUnsearchableHudIconToList(p); if (weaponInfo.IconAmmo != null) { weaponInfo.IconAmmo.Precache(); - // pHudHR.SetHistoryGap(weaponInfo.IconAmmo.Height()); + hudHR.SetHistoryGap(weaponInfo.IconAmmo.Height()); } } @@ -133,7 +128,7 @@ internal void LoadWeaponSprites(WEAPON_FILE_INFO_HANDLE weaponFileInfo) { weaponInfo.IconAmmo2 = gHUD.AddUnsearchableHudIconToList(p); if (weaponInfo.IconAmmo2 != null) { weaponInfo.IconAmmo2.Precache(); - // pHudHR.SetHistoryGap(weaponInfo.IconAmmo2.Height()); + hudHR.SetHistoryGap(weaponInfo.IconAmmo2.Height()); } } } @@ -172,9 +167,9 @@ public void Reset() { } if (weapon == null) continue; - if (weapon.GetPrimaryAmmoType() == ammoId) + if (weapon.GetPrimaryAmmoType() == ammoId) return weapon.GetWpnData(); - else if (weapon.GetSecondaryAmmoType() == ammoId) + else if (weapon.GetSecondaryAmmoType() == ammoId) return weapon.GetWpnData(); } From ce0c4e32bb41f9c91bb0814a42d9a5c03ff31613 Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Mon, 26 Jan 2026 22:15:17 +0000 Subject: [PATCH 05/11] Add gmod's extra ConsoleDialog stuff --- .../hl2/resource/ConsoleExtraItems.res | 154 +++++++++++++++ Source.GUI.Controls/ConsoleDialog.cs | 178 +++++++++++++++++- .../CvarToggleCheckButton.cs | 3 +- Source.GUI.Controls/MenuItem.cs | 2 +- 4 files changed, 326 insertions(+), 11 deletions(-) create mode 100644 Game.Assets/hl2/resource/ConsoleExtraItems.res rename {Game.UI => Source.GUI.Controls}/CvarToggleCheckButton.cs (97%) diff --git a/Game.Assets/hl2/resource/ConsoleExtraItems.res b/Game.Assets/hl2/resource/ConsoleExtraItems.res new file mode 100644 index 00000000..03025584 --- /dev/null +++ b/Game.Assets/hl2/resource/ConsoleExtraItems.res @@ -0,0 +1,154 @@ +"ConsoleExtraItems" +{ + "0" + { + "type" "Cvar" + "text" "Enable Developer Mode (Extra console prints)" + "cmd" "developer" + "value" "1" + } + "1" + { + "type" "Cvar" + "text" "Extra Developer Mode (Map I/O and others)" + "cmd" "developer" + "value" "3" + } + "2" + { + "type" "Separator" + } + "3" + { + "type" "Cvar" + "text" "Visualize GUI Layouts" + "cmd" "vgui_visualizelayout" + } + "4" + { + "type" "Cvar" + "text" "(Cheat) Show Hitboxes" + "cmd" "cl_showhitboxes" + } + "5" + { + "type" "Cvar" + "text" "Show Prediction Errors in Console" + "cmd" "cl_showerror" + "value" "2" + } + "6" + { + "type" "Cvar" + "text" "(Cheat) Show Active Sounds" + "cmd" "snd_show" + } + "7" + { + "type" "Cvar" + "text" "(Cheat) Log Sound Playback" + "cmd" "snd_showstart" + } + "8" + { + "type" "Cvar" + "text" "Show Network Graph with Extra Info" + "cmd" "net_graph" + } + "9" + { + "type" "Cvar" + "text" "Show Player Position and Velocity" + "cmd" "cl_showpos" + } + "10" + { + "type" "Cvar" + "text" "(Cheat) Show Particle Bounding Boxes" + "cmd" "cl_particle_show_bbox" + } + "11" + { + "type" "Cvar" + "text" "(Cheat) Show Collision Models" + "cmd" "vcollide_wireframe" + } + "12" + { + "type" "Separator" + } + "13" + { + "type" "Cvar" + "text" "Show Bullet Impacts" + "cmd" "sv_showimpacts" + } + "14" + { + "type" "Cvar" + "text" "Turn on sv_cheats" + "cmd" "sv_cheats" + } + "15" + { + "type" "Separator" + } + "16" + { + "type" "Command" + "text" "Open Color Correction UI" + "cmd" "colorcorrectionui" + } + "17" + { + "type" "Command" + "text" "Open Fog UI" + "cmd" "fogui" + } + "18" + { + "type" "Command" + "text" "Open Texture List UI" + "cmd" "+mat_texture_list" + } + "19" + { + "type" "Command" + "text" "Open Derma Icon Browser" + "cmd" "derma_icon_browser" + } + "20" + { + "type" "Separator" + } + "21" + { + "type" "Command" + "text" "Print Used Material List" + "cmd" "mat_showmaterials" + } + "22" + { + "type" "Command" + "text" "Print Used Texture List" + "cmd" "mat_showtextures" + } + "23" + { + "type" "Command" + "text" "Print Used Model List" + "cmd" "" + } + "24" + { + "type" "Command" + "text" "Print Stringtable Usage" + "cmd" "" + } + "25" + { + "type" "Command" + "text" "Print All Entity Classes on the Map" + "cmd" "" + } +} \ No newline at end of file diff --git a/Source.GUI.Controls/ConsoleDialog.cs b/Source.GUI.Controls/ConsoleDialog.cs index 380018b7..1abe6ff7 100644 --- a/Source.GUI.Controls/ConsoleDialog.cs +++ b/Source.GUI.Controls/ConsoleDialog.cs @@ -672,14 +672,134 @@ public class ConsoleDialog : Frame { protected ConsolePanel ConsolePanel; +#if GMOD_DLL + internal TextEntry ConsoleFilter; + internal CvarToggleCheckButton ConsoleFilterToggle; + internal Button ClearConsoleButton; + internal Button ExtraConsoleSettingsBtn; +#endif + public ConsoleDialog(Panel? parent, ReadOnlySpan name, bool statusVersion) : base(parent, name) { SetVisible(false); SetTitle("#Console_Title", true); ConsolePanel = new ConsolePanel(this, "ConsolePage", statusVersion); ConsolePanel.AddActionSignalTarget(this); + +#if GMOD_DLL + ConsoleFilter = new(this, "ConsoleFilter"); + ConsoleFilter.SetMaximumCharCount(128); + + ConVarRef filterCvar = new("con_filter_text"); + if (filterCvar.IsValid()) { + ReadOnlySpan text = filterCvar.GetString(); + ConsoleFilter.SetText(text[..Math.Min(text.Length, 128)]); + } + + ConsoleFilterToggle = new(this, "ConsoleFilterToggle", "Remove new entries containing:", "con_filter_enable"); + + ClearConsoleButton = new(this, "ClearConsoleButton", "Clear All"); + + ClearConsoleButton.SetCommand("Clear"); + + ExtraConsoleSettingsBtn = new(this, "ExtraConsoleSettingsBtn", "Extra"); + ExtraConsoleSettingsBtn.SetCommand("OpenExtra"); +#endif + } + +#if GMOD_DLL + private enum ExtraItemType + { + Cvar, + Command, + Separator + } + + private struct ExtraItem + { + public ExtraItemType Type; + public string Text; + public string Cmd; + public int? Value; + } + + private List? ExtraItems = null; + + private void OnOpenExtra() { + Menu menu = new(this, "ConsoleExtraOptions"); + menu.AddActionSignalTarget(this); + + if (ExtraItems == null) { + ExtraItems = []; + KeyValues kv = new(); + if (kv.LoadFromFile(fileSystem, "resource/ConsoleExtraItems.res", "GAME")) { + for (KeyValues? itemKv = kv.GetFirstSubKey(); itemKv != null; itemKv = itemKv.GetNextKey()) { + ExtraItem item = new(); + ReadOnlySpan type = itemKv.GetString("type"); + + if (type == "Cvar") item.Type = ExtraItemType.Cvar; + else if (type == "Command") item.Type = ExtraItemType.Command; + else if (type == "Separator") item.Type = ExtraItemType.Separator; + + item.Text = itemKv.GetString("text").ToString(); + item.Cmd = itemKv.GetString("cmd").ToString(); + + ReadOnlySpan val = itemKv.GetString("value"); + if (!val.IsEmpty) item.Value = int.Parse(val); + + ExtraItems.Add(item); + } + } + } + + foreach (ExtraItem item in ExtraItems) { + if (item.Type == ExtraItemType.Separator) + menu.AddSeparator(); + else if (item.Type == ExtraItemType.Cvar) { + KeyValues kv = new("ExtraCmd"); + kv.SetString("cvar", item.Cmd); + kv.SetInt("value", item.Value ?? -1); + + int itemId = menu.AddCheckableMenuItem(item.Text, kv, this); + + ConVarRef cvar = new(item.Cmd); + if (cvar.IsValid()) + menu.SetMenuItemChecked(itemId, cvar.GetInt() == (item.Value ?? 1)); + } + else + menu.AddMenuItem(item.Text, new KeyValues("ExtraCmd", "cmd", item.Cmd), this, null); + } + + menu.SetPos(GetX() + ExtraConsoleSettingsBtn.GetX(), GetY() + ExtraConsoleSettingsBtn.GetY() + ExtraConsoleSettingsBtn.GetTall()); + menu.SetVisible(true); + menu.MoveToFront(); } + private void OnExtraCmd(KeyValues msg) { + if (!msg.GetString("cvar").IsEmpty) { + ReadOnlySpan cvarName = msg.GetString("cvar"); + ConVarRef var = new(cvarName); + if (!var.IsValid()) + return; + + int value = msg.GetInt("value", -1); + int curValue = var.GetInt(); + + // This is for cases where the cvar is not just a 0/1 toggle + if (value != -1) { + if (curValue != value) var.SetValue(value); + else var.SetValue(0); + } + else { + if (curValue == 0) var.SetValue(1); + else var.SetValue(0); + } + } + else if (!msg.GetString("cmd").IsEmpty) + PostActionSignal(new("CommandSubmitted", "command", msg.GetString("cmd"))); + } +#endif + public override void OnMessage(KeyValues message, IPanel? from) { switch (message.Name) { case "CommandSubmitted": @@ -688,11 +808,43 @@ public override void OnMessage(KeyValues message, IPanel? from) { case "Activate": Activate(); return; +#if GMOD_DLL + case "ExtraCmd": + OnExtraCmd(message); + break; +#endif } base.OnMessage(message, from); } +#if GMOD_DLL + public override void OnCommand(ReadOnlySpan command) { + switch (command.ToString()) { + case "Clear": + ConsolePanel.Clear(); + return; + case "OpenExtra": + OnOpenExtra(); + return; + } + base.OnCommand(command); + } + + public override void OnKeyCodeTyped(ButtonCode code) { + if (Input.GetFocus() == ConsoleFilter && code == ButtonCode.KeyEnter) { + ConVarRef filterText = new("con_filter_text"); + Span filterValue = stackalloc char[128]; + ConsoleFilter.GetText(filterValue); + filterText.SetValue(filterValue.SliceNullTerminatedString()); + ConsoleFilterToggle.SetSelected(true); + return; + } + + base.OnKeyCodeTyped(code); + } +#endif + protected virtual void OnCommandSubmitted(ReadOnlySpan command) { PostActionSignal(new KeyValues("CommandSubmitted", "command", command)); } @@ -702,6 +854,20 @@ public override void PerformLayout() { GetClientArea(out int x, out int y, out int w, out int h); ConsolePanel.SetBounds(x, y, w, h); + +#if GMOD_DLL + ConsoleFilter.SetSize(200, 20); + ConsoleFilter.SetPos(w - 218, 9); + + ConsoleFilterToggle.SetSize(210, 20); + ConsoleFilterToggle.SetPos(w - 432, 8); + + ClearConsoleButton.SetSize(64, 20); + ClearConsoleButton.SetPos(w - 500, 9); + + ExtraConsoleSettingsBtn.SetSize(48, 20); + ExtraConsoleSettingsBtn.SetPos(w - 556, 9); +#endif } public override void Activate() { @@ -717,12 +883,8 @@ public void Hide() { ConsolePanel.Hide(); } - public void Print(ReadOnlySpan msg) { } - public void DPrint(ReadOnlySpan msg) { } - public void ColorPrint(in Color clr, ReadOnlySpan msg) { } - public void DumpConsoleTextToFile() { } - - public override void OnKeyCodePressed(ButtonCode code) { - base.OnKeyCodePressed(code); - } + public void Print(ReadOnlySpan msg) => ConsolePanel.Print(msg); + public void DPrint(ReadOnlySpan msg) => ConsolePanel.DPrint(msg); + public void ColorPrint(in Color clr, ReadOnlySpan msg) => ConsolePanel.ColorPrint(in clr, msg); + public void DumpConsoleTextToFile() => throw new NotImplementedException(); } diff --git a/Game.UI/CvarToggleCheckButton.cs b/Source.GUI.Controls/CvarToggleCheckButton.cs similarity index 97% rename from Game.UI/CvarToggleCheckButton.cs rename to Source.GUI.Controls/CvarToggleCheckButton.cs index f610a3cb..00d219c2 100644 --- a/Game.UI/CvarToggleCheckButton.cs +++ b/Source.GUI.Controls/CvarToggleCheckButton.cs @@ -1,8 +1,7 @@ using Source.Common.Commands; using Source.Common.Formats.Keyvalues; -using Source.GUI.Controls; -namespace Game.UI; +namespace Source.GUI.Controls; [PanelAlias("CCvarToggleCheckButton")] public class CvarToggleCheckButton : CheckButton diff --git a/Source.GUI.Controls/MenuItem.cs b/Source.GUI.Controls/MenuItem.cs index afc5d427..123c7451 100644 --- a/Source.GUI.Controls/MenuItem.cs +++ b/Source.GUI.Controls/MenuItem.cs @@ -16,7 +16,7 @@ public override void Paint() { DrawSetTextFont(GetFont()); DrawSetTextColor(MenuItem.GetBgColor()); - DrawPrintChar(0, 0, 'g'); + // DrawPrintChar(0, 0, 'g'); if (MenuItem.IsChecked()) { if (MenuItem.IsEnabled()) { From a5f64fade01ed03a9b035d79bb883c9820692bb9 Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Wed, 28 Jan 2026 02:52:26 +0000 Subject: [PATCH 06/11] Video options impl --- Game.UI/BasePanel.cs | 2 +- Game.UI/OptionsSubVideo.cs | 284 ++++++++++++++++-- .../MaterialSystem/IMaterialSystem.cs | 7 +- Source.GUI.Controls/Panel.cs | 5 + Source.MaterialSystem/MaterialSystem.cs | 6 +- 5 files changed, 266 insertions(+), 38 deletions(-) diff --git a/Game.UI/BasePanel.cs b/Game.UI/BasePanel.cs index f43fc31e..d9f2620e 100644 --- a/Game.UI/BasePanel.cs +++ b/Game.UI/BasePanel.cs @@ -278,7 +278,7 @@ public class BasePanel : Panel TimeUnit_t TransitionStartTime; TimeUnit_t TransitionEndTime; - static BasePanel? g_BasePanel; + public static BasePanel? g_BasePanel; public override void OnCommand(ReadOnlySpan command) { RunMenuCommand(command); diff --git a/Game.UI/OptionsSubVideo.cs b/Game.UI/OptionsSubVideo.cs index 8fc1dbe5..0b2f90f0 100644 --- a/Game.UI/OptionsSubVideo.cs +++ b/Game.UI/OptionsSubVideo.cs @@ -1,53 +1,57 @@ using Source.Common; +using Source.Common.Engine; using Source.Common.Formats.Keyvalues; +using Source.Common.Input; +using Source.Common.MaterialSystem; +using Source.Engine; using Source.GUI.Controls; namespace Game.UI; -public struct RadioToAspectMode +struct RatioToAspectMode { - public int anamorphic; - public float aspectRatio; + public int Anamorphic; + public float AspectRatio; } -struct AAode +struct AAMode { int NumSamples; int QualityLevel; } -// RadioToAspectMode[] RadioToAspectModes = [ -// new () { anamorphic = 0, aspectRatio = 4.0f / 3.0f }, -// new () { anamorphic = 1, aspectRatio = 16.0f / 9.0f }, -// new () { anamorphic = 2, aspectRatio = 16.0f / 10.0f }, -// new () { anamorphic = 3, aspectRatio = 1.0f } -// ]; - -// int[] g_DirectXLevels = { -// 70, -// 80, -// 81, -// 90, -// // DX_TO_GL_ABSTRACTION -> 92 -// 95 -// }; - -class CGammaDialog : Frame +class GammaDialog : Frame { - public CGammaDialog(Panel? parent, ReadOnlySpan name) : base(parent, name) { + public GammaDialog(Panel? parent, ReadOnlySpan name) : base(parent, name) { + } } -class COptionsSubVideoAvancedDlg : Frame +class OptionsSubVideoAvancedDlg : Frame { - public COptionsSubVideoAvancedDlg(Panel? parent, ReadOnlySpan name) : base(parent, name) { + public OptionsSubVideoAvancedDlg(Panel? parent, ReadOnlySpan name) : base(parent, name) { } } public class OptionsSubVideo : PropertyPage { - readonly public ModInfo ModInfo = Singleton(); //r + readonly public ModInfo ModInfo = Singleton(); + + readonly RatioToAspectMode[] RatioToAspectModes = [ + new () { Anamorphic = 0, AspectRatio = 4.0f / 3.0f }, + new () { Anamorphic = 1, AspectRatio = 16.0f / 9.0f }, + new () { Anamorphic = 2, AspectRatio = 16.0f / 10.0f }, + new () { Anamorphic = 3, AspectRatio = 1.0f } + ]; + + int[] DirectXLevels = [ + 70, + 80, + 81, + 90, + 95 + ]; int SelectedMode; bool DisplayedVRModeMessage; @@ -56,24 +60,24 @@ public class OptionsSubVideo : PropertyPage ComboBox Windowed; ComboBox AspectRatio; ComboBox VRMode; - Button GammeButton; + Button GammaButton; Button Advanced; Button Benchmark; CheckButton HDContent; OptionsSubKeyboardAdvancedDlg OptionsSubKeyboardAdvancedDlg; - CGammaDialog GammaDialog; + OptionsSubVideoThirdPartyCreditsDlg OptionsSubVideoThirdPartyCreditsDlg; + GammaDialog GammaDialog; bool RequiredRestart; URLButton ThirdPartyCredits; - // COptionsSubVideoThirdPartyCreditsDlg OptionsSubVideoThirdPartyCreditsDlg // Messages -> ControlModified, TextChanged, OpenAdvanced, LaunchBenchmark, OpenGammDialog, OpenThirdPartVideoCreditsDialog readonly static KeyValues KV_LaunchBenchmark = new("LaunchBenchmark"); readonly static KeyValues KV_OpenAdvanced = new("OpenAdvanced"); public OptionsSubVideo(Panel? parent, ReadOnlySpan name) : base(parent, null) { - GammeButton = new(this, "GammaButton", "#GameUI_AdjustGamma"); + GammaButton = new(this, "GammaButton", "#GameUI_AdjustGamma"); Mode = new(this, "Resolution", 8, false); AspectRatio = new(this, "AspectRatio", 6, false); VRMode = new(this, "VRMode", 2, false); @@ -85,15 +89,35 @@ public OptionsSubVideo(Panel? parent, ReadOnlySpan name) : base(parent, nu ThirdPartyCredits.SetCommand(new KeyValues("OpenThirdPartyVideoCreditsDialog"));//static HDContent = new(this, "HDContentButton", "#GameUI_HDContent"); - ReadOnlySpan aspect1 = Localize.Find("#GameUI_AspectRatio4x3"); - ReadOnlySpan aspect2 = Localize.Find("#GameUI_AspectRatio16x9"); - ReadOnlySpan aspect3 = Localize.Find("#GameUI_AspectRatio16x10"); + ReadOnlySpan aspect1 = Localize.Find("#GameUI_AspectNormal"); + ReadOnlySpan aspect2 = Localize.Find("#GameUI_AspectWide16x9"); + ReadOnlySpan aspect3 = Localize.Find("#GameUI_AspectWide16x10"); int NormalItemID = AspectRatio.AddItem(aspect1, null); int i16x9ItemID = AspectRatio.AddItem(aspect2, null); int i16x10ItemID = AspectRatio.AddItem(aspect3, null); - // todo materials.GetCurrentConfigForVideoCard(); + MaterialSystem_Config config = Materials.GetCurrentConfigForVideoCard(); + + int aspectMode = GetScreenAspectMode(config.VideoMode.Width, config.VideoMode.Height); + switch (aspectMode) { + case 0: + AspectRatio.ActivateItem(NormalItemID); + break; + case 1: + AspectRatio.ActivateItem(i16x9ItemID); + break; + case 2: + AspectRatio.ActivateItem(i16x10ItemID); + break; + } + + + Windowed = new(this, "DisplayModeCombo", 5, false); + Windowed.AddItem("#GameUI_Fullscreen", null); + Windowed.AddItem("#GameUI_Windowed", null); + + PrepareResolutionList(); LoadControlSettings("resource/OptionsSubVideo.res"); @@ -101,4 +125,198 @@ public OptionsSubVideo(Panel? parent, ReadOnlySpan name) : base(parent, nu if (!ModInfo.SupportsVR()) VRMode.SetVisible(false); if (!ModInfo.HasHDContent()) HDContent.SetVisible(false); } + + // FIXME #37 + public override void Dispose() { + base.Dispose(); + OptionsSubVideoThirdPartyCreditsDlg?.MarkForDeletion(); + } + + private void PrepareResolutionList() { + Span sz = stackalloc char[256]; + Mode.GetText(sz); + + new ScanF(sz, "%i x %i").Read(out int currentWidth).Read(out int currentHeight); + + Mode.RemoveAll(); + AspectRatio.SetItemEnabled(1, false); + AspectRatio.SetItemEnabled(2, false); + + VMode[] list = []; + // gameuifuncs.GetVideoModes // todo + + MaterialSystem_Config config = Materials.GetCurrentConfigForVideoCard(); + + bool windowed = Windowed.GetActiveItem() >= (Windowed.GetItemCount() - 1); + int desktopWidth = 1600, desktopHeight = 900; // todo gameuifuncs.GetDesktopResolution + + bool newFullscreenDisplay = !windowed && (/*todo*/ 0 != Windowed.GetActiveItem()); + if (newFullscreenDisplay) { + currentWidth = desktopWidth; + currentHeight = desktopHeight; + } + + bool foundWidescreen = false; + int selectedItemID = -1; + foreach (VMode mode in list) { + if (mode.Width > desktopWidth || mode.Height > desktopHeight) + continue; + + GetResolutionName(mode, sz, desktopWidth, desktopHeight); + + int itemID = -1; + int aspectMode = GetScreenAspectMode(mode.Width, mode.Height); + if (aspectMode > 0) { + AspectRatio.SetItemEnabled(aspectMode, true); + foundWidescreen = true; + } + + if (aspectMode == AspectRatio.GetActiveItem()) + itemID = Mode.AddItem(sz, null); + + if (mode.Width == currentWidth && mode.Height == currentHeight) + selectedItemID = itemID; + else if (selectedItemID == -1 && mode.Width == config.VideoMode.Width && mode.Height == config.VideoMode.Height) + selectedItemID = itemID; + } + + AspectRatio.SetEnabled(foundWidescreen); + + SelectedMode = selectedItemID; + + if (selectedItemID != -1) + Mode.ActivateItem(selectedItemID); + else { + int width = config.VideoMode.Width; + int height = config.VideoMode.Height; + + if (newFullscreenDisplay || (width > desktopWidth) || (height > desktopHeight)) { + width = desktopWidth; + height = desktopHeight; + } + + sprintf(sz, "%i x %i").I(width).I(height); + Mode.SetText(sz); + } + } + + private bool BUseHDContent() { + throw new NotImplementedException(); + } + + private void SetUseHDContent(bool use) { + throw new NotImplementedException(); + } + + public override void OnResetData() { + + } + + private void SetCurrentResolutionComboItem() { + throw new NotImplementedException(); + } + + public override void OnApplyChanges() { + + } + + public override void PerformLayout() { + base.PerformLayout(); + + if (GammaButton != null) { + MaterialSystem_Config config = Materials.GetCurrentConfigForVideoCard(); + GammaButton.SetEnabled(!config.Windowed()); + } + } + + public override void OnTextChanged(Panel from) { + + } + + private void OnDataChanged() => PostActionSignal(new KeyValues("ApplyButtonEnabled"));//static + + private bool RequiresRestart() { + throw new NotImplementedException(); + } + + private void OpenAdvanced() { + OptionsSubKeyboardAdvancedDlg ??= new(BasePanel.g_BasePanel!.FindChildByName("OptionsDialog")); + OptionsSubKeyboardAdvancedDlg.Activate(); + } + + private void OpenGammaDialog() { + GammaDialog ??= new(this, "GammaDialog"); + GammaDialog.Activate(); + } + + private void LaunchBenchmark() { + // BasePanel.g_BasePanel?.OnOpenBenchmarkDialog(); // todo + } + + private void OpenThirdPartyVideoCreditsDialog() { + OptionsSubVideoThirdPartyCreditsDlg ??= new(this); + OptionsSubVideoThirdPartyCreditsDlg.Activate(); + } + + private void GetNameForDXLevel(int level, Span name) { + if (level >= 92 && level <= 95) + strcpy(name, "DirectX v9.0+"); + else { + strcpy(name, $"DirectX v{level / 10.0f:F1}"); + } + } + + private int GetScreenAspectMode(int width, int height) { + float aspectRatio = width / height; + float closestAspectRatioDist = 99999.0f; + int closestAnamorphic = 0; + + for (int i = 0; i < RatioToAspectModes.Length; i++) { + float dist = MathF.Abs(RatioToAspectModes[i].AspectRatio - aspectRatio); + if (dist < closestAspectRatioDist) { + closestAspectRatioDist = dist; + closestAnamorphic = RatioToAspectModes[i].Anamorphic; + } + } + + return closestAnamorphic; + } + + private void GetResolutionName(VMode mode, Span name, int desktopWidth, int desktopHeight) + => sprintf(name, "%i x %i%s").I(mode.Width).I(mode.Height).S(mode.Width == desktopWidth && mode.Height == desktopHeight ? " (native)" : ""); + + FileStream FOpenGameHDFile(FileMode mode) { + // ReadOnlySpan gameDir = engine.GetGameDirectory(); // todo + + // Span modSteamInfPath = stackalloc char[1024]; + // sprintf(modSteamInfPath, "{0}/game_hd.txt").S(gameDir); + + // return File.Open(modSteamInfPath.ToString(), mode); + + throw new NotImplementedException(); + } +} + +class OptionsSubVideoThirdPartyCreditsDlg : Frame +{ + public OptionsSubVideoThirdPartyCreditsDlg(Panel? parent) : base(null, null) { + SetTitle("#GameUI_ThirdPartyVideo_Title", true); + SetSize(500, 200); + LoadControlSettings("resource/OptionsSubVideoThirdPartyDlg.res"); + MoveToCenterOfScreen(); + SetSizeable(false); + SetDeleteSelfOnClose(true); + } + + public override void Activate() { + base.Activate(); + Input.SetAppModalSurface(this); + } + + public override void OnKeyCodeTyped(ButtonCode code) { + if (code == ButtonCode.KeyEscape) + Close(); + else + base.OnKeyCodeTyped(code); + } } \ No newline at end of file diff --git a/Source.Common/MaterialSystem/IMaterialSystem.cs b/Source.Common/MaterialSystem/IMaterialSystem.cs index d9dc9cd3..eb2956f1 100644 --- a/Source.Common/MaterialSystem/IMaterialSystem.cs +++ b/Source.Common/MaterialSystem/IMaterialSystem.cs @@ -153,14 +153,16 @@ public enum MaterialRenderTargetDepth Only } -public enum MaterialPropertyTypes { +public enum MaterialPropertyTypes +{ NeedsLightmap, Opacity, Reflectivity, NeedsBumpedLightmaps } -public struct StandardLightmap { +public struct StandardLightmap +{ public const int White = -1; public const int WhiteBump = -2; public const int UserDefined = -3; @@ -187,6 +189,7 @@ public interface IMaterialSystem void BeginFrame(double frameTime); void EndFrame(); void SwapBuffers(); + MaterialSystem_Config GetCurrentConfigForVideoCard(); bool SetMode(IWindow window, MaterialSystem_Config config); IMaterial CreateMaterial(ReadOnlySpan name, ReadOnlySpan textureGroupName, KeyValues keyValues); IMaterial CreateMaterial(ReadOnlySpan name, KeyValues keyValues); diff --git a/Source.GUI.Controls/Panel.cs b/Source.GUI.Controls/Panel.cs index 2f9fa8f4..08bd264e 100644 --- a/Source.GUI.Controls/Panel.cs +++ b/Source.GUI.Controls/Panel.cs @@ -2471,6 +2471,11 @@ public virtual void OnMessage(KeyValues message, IPanel? from) { case "Close": OnClose(); break; case "OnRequestFocus": OnRequestFocus(message.GetPtr("subFocus")!, message.GetPtr("defaultPanel")); break; case "Command": OnCommand(message.GetString("command")); break; +#if DEBUG + default: + DevMsg(3, $"Unhandled message '{message.Name}' from {from}"); + break; +#endif } if (vgui_print_messages.GetBool()) if (vgui_print_messages.GetInt() == 2 || (!message.Name.Contains("Ticked") && !message.Name.Contains("Moved"))) diff --git a/Source.MaterialSystem/MaterialSystem.cs b/Source.MaterialSystem/MaterialSystem.cs index ad3c93ba..7a416b1f 100644 --- a/Source.MaterialSystem/MaterialSystem.cs +++ b/Source.MaterialSystem/MaterialSystem.cs @@ -180,6 +180,8 @@ public void SwapBuffers() { FrameNum++; } + public MaterialSystem_Config GetCurrentConfigForVideoCard() => Config; + ThreadLocal matContext; public IMatRenderContext GetRenderContext() => matContext!.Value!; @@ -207,7 +209,7 @@ public bool SetMode(IWindow window, MaterialSystem_Config config) { } private void AllocateStandardTextures() { - + } private void ConvertModeStruct(MaterialSystem_Config config, out ShaderDeviceInfo mode) { @@ -511,7 +513,7 @@ public void SetMaterialProxyFactory(IMaterialProxyFactory? factory) { private void UncacheAllMaterials() { // todo: finish me!! - foreach(var material in MaterialDict){ + foreach (var material in MaterialDict) { material.Uncache(); } } From eeb735102c04a6f0560fa64fccbd947a29761ebc Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Wed, 28 Jan 2026 02:53:32 +0000 Subject: [PATCH 07/11] Some VideoMode impl --- Source.Common/Engine/IVideoMode.cs | 1 + Source.Engine/Game.cs | 2 - Source.Engine/Mod.cs | 8 +- Source.Engine/SourceDLLMain.cs | 1 + Source.Engine/SysModes.cs | 219 +++++++++++++++++++++++++++-- 5 files changed, 217 insertions(+), 14 deletions(-) diff --git a/Source.Common/Engine/IVideoMode.cs b/Source.Common/Engine/IVideoMode.cs index 17e1e68b..8d3f04a7 100644 --- a/Source.Common/Engine/IVideoMode.cs +++ b/Source.Common/Engine/IVideoMode.cs @@ -8,6 +8,7 @@ public struct ViewRect public interface IVideoMode { + bool Init(); void DrawStartupGraphic(); bool CreateGameWindow(int width, int height, bool windowed); void SetGameWindow(nint window); diff --git a/Source.Engine/Game.cs b/Source.Engine/Game.cs index b953c348..7c7f3cd7 100644 --- a/Source.Engine/Game.cs +++ b/Source.Engine/Game.cs @@ -161,8 +161,6 @@ private void DispatchInputEvent(in InputEvent ev) { } } - - public void GetDesktopInfo(out int width, out int height, out int refreshrate) { throw new NotImplementedException(); } diff --git a/Source.Engine/Mod.cs b/Source.Engine/Mod.cs index 23501608..06b30e73 100644 --- a/Source.Engine/Mod.cs +++ b/Source.Engine/Mod.cs @@ -15,7 +15,7 @@ public bool Init(string initialMod, string initialGame) { host_parms.Mod = initialMod; host_parms.Game = initialGame; - if(cl != null) { + if (cl != null) { cl.RestrictServerCommands = false; cl.RestrictClientCommands = false; } @@ -23,10 +23,12 @@ public bool Init(string initialMod, string initialGame) { // Temporary - we need to reconsider MaterialSystem_Config int width = commandLine.ParmValue("-width", 1600); width = commandLine.ParmValue("-w", width); - int height = commandLine.ParmValue("-height", 900); + int height = commandLine.ParmValue("-height", 900); height = commandLine.ParmValue("-h", height); bool windowed = true; + videomode.Init(); + return videomode.CreateGameWindow(width, height, windowed); } @@ -43,7 +45,7 @@ public IMod.Result Run() { else { eng.SetQuitting(IEngine.Quit.NotQuitting); - if(eng.Load(false, host_parms.BaseDir)) { + if (eng.Load(false, host_parms.BaseDir)) { #if !SWDS if (engineAPI.MainLoop()) res = IMod.Result.RunRestart; diff --git a/Source.Engine/SourceDLLMain.cs b/Source.Engine/SourceDLLMain.cs index 2da3dc4f..adc9331f 100644 --- a/Source.Engine/SourceDLLMain.cs +++ b/Source.Engine/SourceDLLMain.cs @@ -42,6 +42,7 @@ public static class SourceDllMain [Dependency] public static Cbuf cbuf { get; private set; } = null!; [Dependency] public static ICvar cvar { get; private set; } = null!; [Dependency] public static IMaterialSystemHardwareConfig HardwareConfig { get; private set; } = null!; + [Dependency] public static ICommandLine commandLine { get; private set; } = null!; [KeyedDependency(Key = Realm.Client)] public static NetworkStringTableContainer networkStringTableContainerClient { get; private set; } = null!; [KeyedDependency(Key = Realm.Server)] public static NetworkStringTableContainer networkStringTableContainerServer { get; private set; } = null!; diff --git a/Source.Engine/SysModes.cs b/Source.Engine/SysModes.cs index 225b1f90..24db8d26 100644 --- a/Source.Engine/SysModes.cs +++ b/Source.Engine/SysModes.cs @@ -15,6 +15,11 @@ namespace Source.Engine; public class VideoMode_Common(Sys Sys, IServiceProvider services, IFileSystem fileSystem, IMaterialSystem materials, RenderUtils renderUtils, ICommandLine CommandLine) : IVideoMode { + public const int MAX_MODE_LIST = 512; + const int VIDEO_MODE_DEFAULT = -1; + const int VIDEO_MODE_REQUESTED_WINDOW_SIZE = -2; + const int CUSTOM_VIDEO_MODES = 2; + VMode mode = new(); bool Windowed; bool ClientViewRectDirty = true; @@ -26,9 +31,17 @@ public class VideoMode_Common(Sys Sys, IServiceProvider services, IFileSystem fi int StereoWidth; int StereoHeight; + public int NumModes; + public readonly VMode[] ModeList = new VMode[MAX_MODE_LIST]; + readonly VMode[] CustomModeList = new VMode[CUSTOM_VIDEO_MODES]; + bool Initialized; + public bool PlayedStartupVideo; + // View rects. Never shrinks readonly ViewRects ClientViewRect = new([new()]); + public virtual bool Init() => true; + public ViewRects GetClientViewRect() { RecomputeClientViewRect(); return ClientViewRect; @@ -55,9 +68,8 @@ private void RecomputeClientViewRect() { ClientViewRectDirty = true; } - public ref VMode RequestedWindowVideoMode() { - return ref mode; - } + public ref VMode DefaultVideoMode() => ref ModeList[-VIDEO_MODE_DEFAULT - 1]; + public ref VMode RequestedWindowVideoMode() => ref mode; public void ResetCurrentModeForNewResolution(int width, int height, bool windowed) { ref VMode mode = ref RequestedWindowVideoMode(); @@ -78,6 +90,73 @@ public void ResetCurrentModeForNewResolution(int width, int height, bool windowe public int GetModeUIWidth() => UIWidth; public int GetModeUIHeight() => UIHeight; + public VMode GetMode(int num) { + if (num < 0) + return CustomModeList[-num - 1]; + + if (num >= NumModes) + return DefaultVideoMode(); + + return ModeList[num]; + } + + public int GetModeCount() => NumModes; + + static int VideModeCompare(in VMode m1, in VMode m2) { + if (m1.Width < m2.Width) + return -1; + + if (m1.Width == m2.Width) { + if (m1.Height < m2.Height) + return -1; + + if (m1.Height > m2.Height) + return 1; + + return 0; + } + + return 1; + } + + int FindVideoMode(int desiredWidth, int desiredHeight, bool windowed) { + VMode defaultMode = DefaultVideoMode(); + + if (desiredWidth == defaultMode.Width && desiredHeight == defaultMode.Height) + return VIDEO_MODE_DEFAULT; + + if (windowed) { + if (desiredWidth == mode.Width && desiredHeight == mode.Height) + return VIDEO_MODE_REQUESTED_WINDOW_SIZE; + } + + int i; + int iOK = VIDEO_MODE_DEFAULT; + + for (i = 0; i < NumModes; i++) { + VMode mode = ModeList[i]; + + if (mode.Width != desiredWidth) + continue; + + iOK = i; + + if (mode.Height != desiredHeight) + continue; + + break; + } + + if (i >= NumModes) { + if (iOK != VIDEO_MODE_DEFAULT) + i = iOK; + else + i = 0; + } + + return i; + } + public void AdjustWindow(int width, int height, int bpp, bool windowed) { IGame game = services.GetRequiredService(); ILauncherManager launcherMgr = services.GetRequiredService(); @@ -248,7 +327,7 @@ private void SetupStartupGraphic() { if (!texture.Unserialize(handle)) { Error($"Invalid or corrupt texture {material}\n"); } - // texture.ConvertImageFormat(ImageFormat.RGBA8888, false); + // FIXME texture.ConvertImageFormat(ImageFormat.RGBA8888, false); return texture; } @@ -262,22 +341,99 @@ public void SetGameWindow(nint window) { public virtual bool SetMode(int width, int height, bool windowed) { return false; } + + public void SetInitialized(bool initialized) => Initialized = initialized; + public bool GetInitialized() => Initialized; } public class VideoMode_MaterialSystem(Sys Sys, IMaterialSystem materials, IGame game, IServiceProvider services, IFileSystem fileSystem, RenderUtils renderUtils, ICommandLine commandLine) : VideoMode_Common(Sys, services, fileSystem, materials, renderUtils, commandLine) { + MaterialSystem_Config config = services.GetRequiredService(); + bool SetModeOnce; + +#if WIN32 + int LastCDSWidth = 0; + int LastCDSHeight = 0; + int LastCDSBPP = 0; + int LastCDSFreq = 0; +#endif + + public override bool Init() { + SetModeOnce = false; + PlayedStartupVideo = false; + + int bitsPerPixel = 32; + bool allowSmallModes = false; + + if (commandLine.FindParm("-small") != 0) + allowSmallModes = true; + + int adapter = 0;//materials.GetCurrentAdapter(); + int modeCount = 1;//materials.GetModeCount(adapter); + +#if false // TODO v + game.GetDesktopInfo(out int desktopWidth, out int desktopHeight, out int desktopRefresh); + + for (int i = 0; i < modeCount; i++) { + MaterialVideoMode info = new(); + // materials.GetModeInfo(adapter, i, out info); + + if (info.Width < 640 || info.Height < 480) { + if (!allowSmallModes) + continue; + } + + bool alreadyInList = false; + for (int j = 0; j < NumModes; j++) { + VMode mode = ModeList[j]; + if (mode.Width == info.Width && mode.Height == info.Height) { + if (info.RefreshRate <= desktopRefresh && (mode.RefreshRate > desktopRefresh || mode.RefreshRate < info.RefreshRate)) + mode.RefreshRate = info.RefreshRate; + + alreadyInList = true; + break; + } + } + + if (alreadyInList) + continue; + + ModeList[NumModes].Width = info.Width; + ModeList[NumModes].Height = info.Height; + ModeList[NumModes].BitsPerPixel = bitsPerPixel; + ModeList[NumModes].RefreshRate = info.RefreshRate; + + if (++NumModes >= MAX_MODE_LIST) + break; + } + + if (NumModes > 1) { + // todo sort + } + + // materials.AddModeChangeCallBack(VideoMode_AdjustForModeChange); + SetInitialized(true); +#endif + + return true; + } + public override bool SetMode(int width, int height, bool windowed) { - ref VMode mode = ref RequestedWindowVideoMode(); - MaterialSystem_Config config = services.GetRequiredService(); + ref VMode mode = ref RequestedWindowVideoMode(); // todo FindVideoMode/GetMode config.VideoMode.Width = mode.Width; config.VideoMode.Height = mode.Height; + +#if SWDS + config.VideoMode.RefreshRate = 60; +#else config.VideoMode.RefreshRate = mode.RefreshRate; +#endif if (!SetModeOnce) { - if (!materials.SetMode(game.GetMainDeviceWindow(), config)) { + if (!materials.SetMode(game.GetMainDeviceWindow(), config)) + return false; - } SetModeOnce = true; InitStartupScreen(); @@ -287,7 +443,52 @@ public override bool SetMode(int width, int height, bool windowed) { return true; } - private void InitStartupScreen() { + private void InitStartupScreen() { } + + void AdjustForModeChange() { + int oldUIWidth = GetModeUIWidth(); + int oldUIHeight = GetModeUIHeight(); + + int newWidth = config.VideoMode.Width; + int newHeight = config.VideoMode.Height; + bool windowed = config.Windowed(); + + using MatRenderContextPtr renderContext = new(materials); + + ResetCurrentModeForNewResolution(newWidth, newHeight, windowed); + AdjustWindow(GetModeWidth(), GetModeHeight(), GetModeBPP(), IsWindowedMode()); + MarkClientViewRectDirty(); + renderContext.Viewport(0, 0, GetModeStereoWidth(), GetModeStereoHeight()); + + // Surface.OnScreenSizeChanged(oldUIWidth, oldUIHeight) TODO + + g_ClientDLL!.HudVidInit(); + } + + void SetGameWindow() { + + } + + void ReleaseVideo() { + if (IsWindowedMode()) + return; + + ReleaseFullScreen(); + } + + void RestoreVideo() { + + } + + void ReleaseFullScreen() { + + } + + void ChangeDisplaySettingsToFullscreen() { + + } + + void ReadScreenPixels() { } } \ No newline at end of file From 98564b5db66765f3ffe0db32d8b2f1cc5b24b0fd Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Wed, 28 Jan 2026 11:48:01 +0000 Subject: [PATCH 08/11] Material System config --- .../MaterialSystem/IMaterialSystem.cs | 1 + .../MaterialSystem/MaterialSystem_Config.cs | 2 +- Source.Common/ShaderAPI/IShaderDevice.cs | 1 + Source.MaterialSystem/MaterialSystem.cs | 323 +++++++++++++++++- Source.ShaderAPI.Gl46/ShaderAPIGl46.cs | 3 + 5 files changed, 315 insertions(+), 15 deletions(-) diff --git a/Source.Common/MaterialSystem/IMaterialSystem.cs b/Source.Common/MaterialSystem/IMaterialSystem.cs index eb2956f1..48b36aa3 100644 --- a/Source.Common/MaterialSystem/IMaterialSystem.cs +++ b/Source.Common/MaterialSystem/IMaterialSystem.cs @@ -190,6 +190,7 @@ public interface IMaterialSystem void EndFrame(); void SwapBuffers(); MaterialSystem_Config GetCurrentConfigForVideoCard(); + bool OverrideConfig(MaterialSystem_Config config, bool forceUpdate); bool SetMode(IWindow window, MaterialSystem_Config config); IMaterial CreateMaterial(ReadOnlySpan name, ReadOnlySpan textureGroupName, KeyValues keyValues); IMaterial CreateMaterial(ReadOnlySpan name, KeyValues keyValues); diff --git a/Source.Common/MaterialSystem/MaterialSystem_Config.cs b/Source.Common/MaterialSystem/MaterialSystem_Config.cs index 5066a31b..256f38d3 100644 --- a/Source.Common/MaterialSystem/MaterialSystem_Config.cs +++ b/Source.Common/MaterialSystem/MaterialSystem_Config.cs @@ -82,7 +82,7 @@ public class MaterialSystem_Config public bool MotionBlur; public bool SupportFlashlight; - void SetFlag(MaterialSystem_Config_Flags flag, bool val) { + public void SetFlag(MaterialSystem_Config_Flags flag, bool val) { if (val) { Flags |= (uint)flag; } diff --git a/Source.Common/ShaderAPI/IShaderDevice.cs b/Source.Common/ShaderAPI/IShaderDevice.cs index d5f11fe8..bf97202c 100644 --- a/Source.Common/ShaderAPI/IShaderDevice.cs +++ b/Source.Common/ShaderAPI/IShaderDevice.cs @@ -62,6 +62,7 @@ public interface IShaderDevice IMesh CreateStaticMesh(VertexFormat format, ReadOnlySpan textureGroup, IMaterial? material); bool IsDeactivated(); bool IsUsingGraphics(); + int GetCurrentAdapter(); void Present(); void ReacquireResources(); void ReleaseResources(); diff --git a/Source.MaterialSystem/MaterialSystem.cs b/Source.MaterialSystem/MaterialSystem.cs index 7a416b1f..4ee6f323 100644 --- a/Source.MaterialSystem/MaterialSystem.cs +++ b/Source.MaterialSystem/MaterialSystem.cs @@ -3,6 +3,7 @@ using Source.Common; using Source.Common.Bitmap; using Source.Common.Commands; +using Source.Common.Engine; using Source.Common.Filesystem; using Source.Common.Formats.Keyvalues; using Source.Common.GUI; @@ -36,6 +37,57 @@ public class MaterialSystem : IMaterialSystem, IShaderUtil public readonly IMaterialSystemHardwareConfig HardwareConfig; public readonly MaterialSystem_Config Config; + readonly static ConVar mat_vsync = new("mat_vsync", "0", 0, "Force sync to vertical retrace", 0, 1); + readonly static ConVar mat_forcehardwaresync = new(IsPC() ? "1" : "0", 0); + readonly static ConVar mat_trilinear = new("0", 0); + readonly static ConVar mat_forceaniso = new("1", FCvar.Archive); // 0 = Bilinear, 1 = Trilinear, 2+ = Aniso + readonly static ConVar mat_filterlightmaps = new("1", 0); + readonly static ConVar mat_filtertextures = new("1", 0); + readonly static ConVar mat_mipmaptextures = new("1", 0); + readonly static ConVar mat_vrmode_adapter = new("-1", 0); + readonly static ConVar mat_showmiplevels = new("0", FCvar.Cheat, "color-code miplevels 2: normalmaps, 1: everything else"); // , callback: mat_showmiplevels_Callback_f + readonly static ConVar mat_specular = new("1", 0, "Enable/Disable specularity for perf testing. Will cause a material reload upon change."); + readonly static ConVar mat_bumpmap = new("1", 0); + readonly static ConVar mat_phong = new("1", 0); + readonly static ConVar mat_parallaxmap = new("1", FCvar.Hidden | 0); + readonly static ConVar mat_reducefillrate = new("0", 0); + readonly static ConVar mat_picmip = new("0", FCvar.Archive, "", -1, 4); + readonly static ConVar mat_slopescaledepthbias_normal = new("0.0f", FCvar.Cheat); + readonly static ConVar mat_depthbias_normal = new("0.0f", FCvar.Cheat | 0); + readonly static ConVar mat_slopescaledepthbias_decal = new("-0.5", FCvar.Cheat); // Reciprocals of these biases sent to API + readonly static ConVar mat_depthbias_decal = new("-262144", FCvar.Cheat | 0); + readonly static ConVar mat_slopescaledepthbias_shadowmap = new("16", FCvar.Cheat); + readonly static ConVar mat_depthbias_shadowmap = new("0.0005", FCvar.Cheat); + readonly static ConVar mat_monitorgamma = new("2.2", FCvar.Archive, "monitor gamma (typically 2.2 for CRT and 1.7 for LCD)", 1.6f, 2.6f); + readonly static ConVar mat_monitorgamma_tv_range_min = new("16", 0); + readonly static ConVar mat_monitorgamma_tv_range_max = new("255", 0); + readonly static ConVar mat_monitorgamma_tv_exp = new("2.5", 0, "", 1.0f, 4.0f); + readonly static ConVar mat_monitorgamma_tv_enabled = new("0", FCvar.Archive, ""); + readonly static ConVar mat_antialias = new("0", FCvar.Archive); + readonly static ConVar mat_aaquality = new("0", FCvar.Archive); + readonly static ConVar mat_diffuse = new("1", FCvar.Cheat); + readonly static ConVar mat_showlowresimage = new("0", FCvar.Cheat); + readonly static ConVar mat_fullbright = new("0", FCvar.Cheat); + readonly static ConVar mat_normalmaps = new("0", FCvar.Cheat); + readonly static ConVar mat_measurefillrate = new("0", FCvar.Cheat); + readonly static ConVar mat_fillrate = new("0", FCvar.Cheat); + readonly static ConVar mat_reversedepth = new("0", FCvar.Cheat); + readonly static ConVar mat_bufferprimitives = new("1", 0); + readonly static ConVar mat_drawflat = new("0", FCvar.Cheat); + readonly static ConVar mat_softwarelighting = new("0", 0); + readonly static ConVar mat_proxy = new("0", FCvar.Cheat, ""); // , callback: MatProxyCallback + readonly static ConVar mat_norendering = new("0", FCvar.Cheat); + readonly static ConVar mat_compressedtextures = new("1", 0); + readonly static ConVar mat_fastspecular = new("1", 0, "Enable/Disable specularity for visual testing. Will not reload materials and will not affect perf."); + readonly static ConVar mat_fastnobump = new("0", FCvar.Cheat); // Binds 1-texel normal map for quick internal testing + + // These are not controlled by the material system, but are limited by settings in the material system + readonly static ConVar r_shadowrendertotexture = new("0", FCvar.Archive); + readonly static ConVar r_flashlightdepthtexture = new("1", 0); + readonly static ConVar r_waterforceexpensive = new("0", FCvar.Archive); + readonly static ConVar r_waterforcereflectentities = new("0", 0); + readonly static ConVar mat_motion_blur_enabled = new("0", FCvar.Archive); + public static void DLLInit(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(x => x.GetRequiredService()); @@ -85,21 +137,262 @@ private bool UpdateConfig(bool forceUpdate) { return OverrideConfig(config, forceUpdate); } - private bool OverrideConfig(MaterialSystem_Config config, bool forceUpdate) { + public bool OverrideConfig(MaterialSystem_Config config, bool forceUpdate) { + bool redownloadLightmaps = false; + bool redownloadTextures = false; + bool recomputeSnapshots = false; + bool reloadMaterials = false; + bool resetAnisotropy = false; + bool setStandardVertexShaderConstants = false; + bool monitorGammaChanged = false; + bool videoModeChange = false; + bool resetTextureFilter = false; + + if (!ShaderDevice.IsUsingGraphics()) { + // Config = config; + ColorSpace.SetGamma(2.2f, 2.2f, IMaterialSystem.OVERBRIGHT, Config.AllowCheats, false); + + return redownloadLightmaps; } - return true; + + // ShaderAPI.SetDefaultState(); + + // if (config.HDREnabled() != Config.HDREnabled()) { + // forceUpdate = true; + // reloadMaterials = true; + // } + + if (config.ShadowDepthTexture != Config.ShadowDepthTexture) { + forceUpdate = true; + reloadMaterials = true; + recomputeSnapshots = true; + } + + // Don't use compressed textures for the moment if we don't support them + if (!HardwareConfig.SupportsCompressedTextures()) + config.CompressedTextures = false; + + if (forceUpdate) { + MatLightmaps.EnableLightmapFiltering(config.FilterLightmaps); + recomputeSnapshots = true; + redownloadLightmaps = true; + redownloadTextures = true; + resetAnisotropy = true; + setStandardVertexShaderConstants = true; + } + + // toggle bump mapping + if (config.DisableSpecular() != Config.DisableSpecular() || config.DisablePhong() != Config.DisablePhong()) { + recomputeSnapshots = true; + reloadMaterials = true; + resetAnisotropy = true; + } + + // toggle specularity + if (config.DisableSpecular() != Config.DisableSpecular()) { + recomputeSnapshots = true; + reloadMaterials = true; + resetAnisotropy = true; + } + + // toggle parallax mapping + if (config.EnableParallaxMapping() != Config.EnableParallaxMapping()) + reloadMaterials = true; + + // Reload materials if we want reduced fillrate + if (config.ReduceFillrate() != Config.ReduceFillrate()) + reloadMaterials = true; + + // toggle reverse depth + if (config.ReverseDepth != Config.ReverseDepth) { + recomputeSnapshots = true; + resetAnisotropy = true; + } + + // toggle no transparency + if (config.NoTransparency != Config.NoTransparency) { + recomputeSnapshots = true; + resetAnisotropy = true; + } + + // toggle lightmap filtering + if (config.FilterLightmaps != Config.FilterLightmaps) + MatLightmaps.EnableLightmapFiltering(config.FilterLightmaps); + + // toggle software lighting + if (config.SoftwareLighting != Config.SoftwareLighting) + reloadMaterials = true; + + // generic things that cause us to redownload textures + if (config.AllowCheats != Config.AllowCheats || + config.SkipMipLevels != Config.SkipMipLevels || + config.ShowMipLevels != Config.ShowMipLevels || + ((config.CompressedTextures != Config.CompressedTextures) && HardwareConfig.SupportsCompressedTextures()) || + config.ShowLowResImage != Config.ShowLowResImage) { + + redownloadTextures = true; + recomputeSnapshots = true; + resetAnisotropy = true; + } + + if (config.ForceTrilinear() != Config.ForceTrilinear()) + resetTextureFilter = true; + + if (config.ForceAnisotropicLevel != Config.ForceAnisotropicLevel) { + resetAnisotropy = true; + resetTextureFilter = true; + } + + if (config.MonitorGamma != Config.MonitorGamma || config.GammaTVRangeMin != Config.GammaTVRangeMin || + config.GammaTVRangeMax != Config.GammaTVRangeMax || config.GammaTVExponent != Config.GammaTVExponent || + config.GammaTVEnabled != Config.GammaTVEnabled) + monitorGammaChanged = true; + + if (config.VideoMode.Width != Config.VideoMode.Width || + config.VideoMode.Height != Config.VideoMode.Height || + config.VideoMode.RefreshRate != Config.VideoMode.RefreshRate || + config.AASamples != Config.AASamples || + config.AAQuality != Config.AAQuality || + config.Windowed() != Config.Windowed() || + config.Stencil() != Config.Stencil()) + videoModeChange = true; + + if (!config.Windowed() && (config.WaitForVSync() != Config.WaitForVSync())) + videoModeChange = true; + + // Config = config; + + if (redownloadTextures || redownloadLightmaps) + ColorSpace.SetGamma(2.2f, 2.2f, IMaterialSystem.OVERBRIGHT, Config.AllowCheats, false); + + if (resetAnisotropy || recomputeSnapshots || redownloadLightmaps || + redownloadTextures || resetAnisotropy || videoModeChange || + setStandardVertexShaderConstants || resetTextureFilter) { + // ForceSingleThreaded(); + } + + if (reloadMaterials) + ReloadMaterials(); + + if (redownloadTextures) { + if (ShaderAPI.CanDownloadTextures()) { + TextureSystem.RestoreRenderTargets(); + TextureSystem.RestoreNonRenderTargetTextures(); + } + } + else if (resetTextureFilter) { + // TextureSystem.ResetTextureFilteringState(); + } + + // Recompute all state snapshots + if (recomputeSnapshots) + RecomputeAllStateSnapshots(); + + // if (resetAnisotropy) + // ShaderAPI.SetAnisotropicLevel(config.ForceAnisotropicLevel); + + // if (setStandardVertexShaderConstants) + // ShaderAPI.SetStandardVertexShaderConstants(IMaterialSystem.OVERBRIGHT); + + if (monitorGammaChanged) { + // ShaderDevice.SetHardwareGammaRamp(config.MonitorGamma, config.GammaTVRangeMin, config.GammaTVRangeMax, config.GammaTVExponent, config.GammaTVEnabled); + } + + if (videoModeChange) { + ConvertModeStruct(config, out ShaderDeviceInfo info); + // ShaderAPI.ChangeVideoMode(info); + } + + // if (videoModeChange) + // ForceSingleThreaded(); + + return redownloadLightmaps; } private void ReadConfigFromConVars(MaterialSystem_Config config) { - + config.SetFlag(MaterialSystem_Config_Flags.NoWaitForVSync, !mat_vsync.GetBool()); + config.SetFlag(MaterialSystem_Config_Flags.ForceTrilinear, mat_trilinear.GetBool()); + config.SetFlag(MaterialSystem_Config_Flags.DisableSpecular, !mat_specular.GetBool()); + config.SetFlag(MaterialSystem_Config_Flags.DisableBumpmap, !mat_bumpmap.GetBool()); + config.SetFlag(MaterialSystem_Config_Flags.DisableBumpmap, !mat_phong.GetBool()); + config.SetFlag(MaterialSystem_Config_Flags.EnableParallaxMapping, mat_parallaxmap.GetBool()); + config.SetFlag(MaterialSystem_Config_Flags.ReduceFillrate, mat_reducefillrate.GetBool()); + config.ForceAnisotropicLevel = Math.Max(mat_forceaniso.GetInt(), 1); + config.SkipMipLevels = mat_picmip.GetInt(); + config.SetFlag(MaterialSystem_Config_Flags.ForceHardwareSync, mat_forcehardwaresync.GetBool()); + config.SlopeScaleDepthBias_Decal = mat_slopescaledepthbias_decal.GetFloat(); + config.DepthBias_Decal = mat_depthbias_decal.GetFloat(); + config.SlopeScaleDepthBias_Normal = mat_slopescaledepthbias_normal.GetFloat(); + config.DepthBias_Normal = mat_depthbias_normal.GetFloat(); + config.SlopeScaleDepthBias_ShadowMap = mat_slopescaledepthbias_shadowmap.GetFloat(); + config.DepthBias_ShadowMap = mat_depthbias_shadowmap.GetFloat(); + config.MonitorGamma = mat_monitorgamma.GetFloat(); + config.GammaTVRangeMin = mat_monitorgamma_tv_range_min.GetFloat(); + config.GammaTVRangeMax = mat_monitorgamma_tv_range_max.GetFloat(); + config.GammaTVExponent = mat_monitorgamma_tv_exp.GetFloat(); + config.GammaTVEnabled = mat_monitorgamma_tv_enabled.GetBool(); + config.AASamples = mat_antialias.GetInt(); + config.AAQuality = mat_aaquality.GetInt(); + config.ShowDiffuse = mat_diffuse.GetBool(); + config.ShowNormalMap = mat_normalmaps.GetBool(); + config.ShowLowResImage = mat_showlowresimage.GetBool(); + config.MeasureFillRate = mat_measurefillrate.GetBool(); + config.VisualizeFillRate = mat_fillrate.GetBool(); + config.FilterLightmaps = mat_filterlightmaps.GetBool(); + config.FilterTextures = mat_filtertextures.GetBool(); + config.MipMapTextures = mat_mipmaptextures.GetBool(); + config.ShowMipLevels = (sbyte)mat_showmiplevels.GetInt(); + config.ReverseDepth = mat_reversedepth.GetBool(); + config.BufferPrimitives = mat_bufferprimitives.GetBool(); + config.DrawFlat = mat_drawflat.GetBool(); + config.SoftwareLighting = mat_softwarelighting.GetBool(); + config.ProxiesTestMode = (byte)mat_proxy.GetInt(); + config.SuppressRendering = mat_norendering.GetInt() != 0; + config.CompressedTextures = mat_compressedtextures.GetBool(); + config.ShowSpecular = mat_fastspecular.GetBool(); + config.Fullbright = (byte)mat_fullbright.GetInt(); + config.FastNoBump = mat_fastnobump.GetInt() != 0; + config.MotionBlur = mat_motion_blur_enabled.GetBool(); + config.SupportFlashlight = true; // mat_supportflashlight.GetBool(); + config.ShadowDepthTexture = r_flashlightdepthtexture.GetBool(); + config.SetFlag(MaterialSystem_Config_Flags.ENABLE_HDR, HardwareConfig.GetHDREnabled()); + } + + public int GetDisplayAdapterCount() { + throw new NotImplementedException(); } + public int GetCurrentAdapter() => ShaderDevice.GetCurrentAdapter(); + + public int GetModeCount(int adapter) => throw new NotImplementedException(); + public void ModShutdown() { } +#if !SWDS + [ConCommand("mat_setvideomode", "sets the width, height, windowed state of the material system")] + static void mat_setvideomode(in TokenizedCommand args) { + if (args.ArgC() != 4) + return; + + int width = args.Arg(1, 0); + int height = args.Arg(2, 0); + int windowed = args.Arg(3, 0); + + Singleton().SetMode(width, height, windowed != 0); + } +#endif + + [ConCommand("mat_savechanges", "saves current video configuration to the registry")] + static void mat_savechanges(in TokenizedCommand args) { + // commandLine.RemoveParm("-safe"); + // UpdateMaterialSystemConfig(); + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] unsafe static void raylibSpew(int logLevel, sbyte* text, sbyte* args) { //var message = Logging.GetLogMessage((nint)text, (nint)args); @@ -115,20 +408,10 @@ unsafe static void raylibSpew(int logLevel, sbyte* text, sbyte* args) { }, "raylib", 1, new Color(255, 255, 255), message + "\n");*/ } - public static ConVar mat_fullbright = new("0", FCvar.Cheat); - public static ConVar mat_normalmaps = new("0", FCvar.Cheat); - - - public static ConVar mat_specular = new("1", 0, "Enable/Disable specularity for perf testing. Will cause a material reload upon change."); - public static ConVar mat_bumpmap = new("1", 0); - public static ConVar mat_phong = new("1", 0); - public static ConVar mat_parallaxmap = new("1", FCvar.Hidden); - public static ConVar mat_reducefillrate = new("0", 0); - public uint DebugVarsSignature; public ITexture GetErrorTexture() => TextureSystem.ErrorTexture(); - public unsafe void BeginFrame(double frameTime) { + public void BeginFrame(double frameTime) { if (!ThreadInMainThread() || IsInFrame()) return; @@ -518,6 +801,18 @@ private void UncacheAllMaterials() { } } + void ReloadTextures() { + throw new NotImplementedException(); + } + + void ReloadMaterials() { + throw new NotImplementedException(); + } + + void RecomputeAllStateSnapshots() { + throw new NotImplementedException(); + } + public event Action? Restore; public IMaterialInternal errorMaterial; diff --git a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs index eb7b900b..a8436ce0 100644 --- a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs +++ b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs @@ -605,6 +605,9 @@ internal unsafe void GL_LoadExtensions(delegate* unmanaged[Cdecl] public bool IsActive() => Device != null; public bool IsUsingGraphics() => IsActive(); + public int GetCurrentAdapter() { + throw new NotImplementedException(); + } public void Present() { From 8e278848c45e5e75d10f0d0cdb2d8e5b32bd117a Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Wed, 28 Jan 2026 11:49:59 +0000 Subject: [PATCH 09/11] VideoMode & Video Options impl --- Game.UI/OptionsSubVideo.cs | 83 ++++++++++++++++++++++++++++---- Source.Engine/SysModes.cs | 45 +++++++++++++---- Source.GUI.Controls/TextImage.cs | 2 +- 3 files changed, 110 insertions(+), 20 deletions(-) diff --git a/Game.UI/OptionsSubVideo.cs b/Game.UI/OptionsSubVideo.cs index 0b2f90f0..fc897901 100644 --- a/Game.UI/OptionsSubVideo.cs +++ b/Game.UI/OptionsSubVideo.cs @@ -1,8 +1,10 @@ using Source.Common; +using Source.Common.Client; using Source.Common.Engine; using Source.Common.Formats.Keyvalues; using Source.Common.Input; using Source.Common.MaterialSystem; +using Source.Common.Networking; using Source.Engine; using Source.GUI.Controls; @@ -27,9 +29,9 @@ public GammaDialog(Panel? parent, ReadOnlySpan name) : base(parent, name) } } -class OptionsSubVideoAvancedDlg : Frame +class OptionsSubVideoAdvancedDlg : Frame { - public OptionsSubVideoAvancedDlg(Panel? parent, ReadOnlySpan name) : base(parent, name) { + public OptionsSubVideoAdvancedDlg(Panel? parent, ReadOnlySpan name) : base(parent, name) { } } @@ -42,7 +44,7 @@ public class OptionsSubVideo : PropertyPage new () { Anamorphic = 0, AspectRatio = 4.0f / 3.0f }, new () { Anamorphic = 1, AspectRatio = 16.0f / 9.0f }, new () { Anamorphic = 2, AspectRatio = 16.0f / 10.0f }, - new () { Anamorphic = 3, AspectRatio = 1.0f } + new () { Anamorphic = 2, AspectRatio = 1.0f } ]; int[] DirectXLevels = [ @@ -65,7 +67,7 @@ public class OptionsSubVideo : PropertyPage Button Benchmark; CheckButton HDContent; - OptionsSubKeyboardAdvancedDlg OptionsSubKeyboardAdvancedDlg; + OptionsSubVideoAdvancedDlg OptionsSubVideoAdvancedDlg; OptionsSubVideoThirdPartyCreditsDlg OptionsSubVideoThirdPartyCreditsDlg; GammaDialog GammaDialog; @@ -142,8 +144,8 @@ private void PrepareResolutionList() { AspectRatio.SetItemEnabled(1, false); AspectRatio.SetItemEnabled(2, false); - VMode[] list = []; - // gameuifuncs.GetVideoModes // todo + VMode[] list = ((VideoMode_Common)Singleton()).ModeList; + // gameuifuncs.GetVideoModes // todo ^ MaterialSystem_Config config = Materials.GetCurrentConfigForVideoCard(); @@ -159,6 +161,9 @@ private void PrepareResolutionList() { bool foundWidescreen = false; int selectedItemID = -1; foreach (VMode mode in list) { + if (mode.Width == 0 || mode.Height == 0) + continue; + if (mode.Width > desktopWidth || mode.Height > desktopHeight) continue; @@ -216,10 +221,62 @@ private void SetCurrentResolutionComboItem() { throw new NotImplementedException(); } + readonly IEngineClient engine = Singleton(); public override void OnApplyChanges() { + if (RequiresRestart()) { + INetChannelInfo? nci = engine.GetNetChannelInfo(); + if (nci != null) { + ReadOnlySpan addr = nci.GetAddress(); + if (addr.Length > 0) { + if (strncmp(addr, "127.0.0.1", 9) != 0 && strncmp(addr, "localhost", 9) != 0) { + engine.ClientCmd_Unrestricted("retry\n"); + } + else { + engine.ClientCmd_Unrestricted("disconnect\n"); + } + } + } + } + + // OptionsSubVideoAdvancedDlg?.ApplyChanges(); + + Span sz = stackalloc char[256]; + if (SelectedMode == -1) + Mode.GetText(sz); + else + Mode.GetItemText(SelectedMode, sz); + + new ScanF(sz, "%i x %i").Read(out int width).Read(out int height); + + bool configChanged = false; + bool windowed = Windowed.GetActiveItem() == (Windowed.GetItemCount() - 1); + MaterialSystem_Config config = Materials.GetCurrentConfigForVideoCard(); + + bool vrMode = VRMode.GetActiveItem() != 0; + // todo vr mode + if (config.VideoMode.Width != width || config.VideoMode.Height != height || config.Windowed() != windowed) + configChanged = true; + + if (configChanged) { + Span cmd = stackalloc char[256]; + sprintf(cmd, "mat_setvideomode %i %i %i\n").I(width).I(height).I(windowed ? 1 : 0); + engine.ClientCmd_Unrestricted(cmd); + } + + if (ModInfo.HasHDContent()) { + if (BUseHDContent() != HDContent.IsSelected()) { + SetUseHDContent(HDContent.IsSelected()); + MessageBox box = new("#GameUI_OptionsRestartRequired_Title", "#GameUI_HDRestartRequired_Info"); + box.DoModal(); + box.MoveToFront(); + } + } + + engine.ClientCmd_Unrestricted("mat_savechanges\n"); } + public override void PerformLayout() { base.PerformLayout(); @@ -230,18 +287,24 @@ public override void PerformLayout() { } public override void OnTextChanged(Panel from) { - + // TODO + if (from == Mode) { + SelectedMode = Mode.GetActiveItem(); + } } private void OnDataChanged() => PostActionSignal(new KeyValues("ApplyButtonEnabled"));//static private bool RequiresRestart() { - throw new NotImplementedException(); + // if (OptionsSubVideoAdvancedDlg != null && OptionsSubVideoAdvancedDlg.RequiresRestart()) + // return true; + + return RequiredRestart; } private void OpenAdvanced() { - OptionsSubKeyboardAdvancedDlg ??= new(BasePanel.g_BasePanel!.FindChildByName("OptionsDialog")); - OptionsSubKeyboardAdvancedDlg.Activate(); + // OptionsSubVideoAdvancedDlg ??= new(BasePanel.g_BasePanel!.FindChildByName("OptionsDialog")); + // OptionsSubVideoAdvancedDlg.Activate(); } private void OpenGammaDialog() { diff --git a/Source.Engine/SysModes.cs b/Source.Engine/SysModes.cs index 24db8d26..6a4fc432 100644 --- a/Source.Engine/SysModes.cs +++ b/Source.Engine/SysModes.cs @@ -119,7 +119,7 @@ static int VideModeCompare(in VMode m1, in VMode m2) { return 1; } - int FindVideoMode(int desiredWidth, int desiredHeight, bool windowed) { + public int FindVideoMode(int desiredWidth, int desiredHeight, bool windowed) { VMode defaultMode = DefaultVideoMode(); if (desiredWidth == defaultMode.Width && desiredHeight == defaultMode.Height) @@ -338,9 +338,7 @@ public void SetGameWindow(nint window) { throw new NotImplementedException(); } - public virtual bool SetMode(int width, int height, bool windowed) { - return false; - } + public virtual bool SetMode(int width, int height, bool windowed) => false; public void SetInitialized(bool initialized) => Initialized = initialized; public bool GetInitialized() => Initialized; @@ -372,11 +370,24 @@ public override bool Init() { int adapter = 0;//materials.GetCurrentAdapter(); int modeCount = 1;//materials.GetModeCount(adapter); -#if false // TODO v - game.GetDesktopInfo(out int desktopWidth, out int desktopHeight, out int desktopRefresh); + // game.GetDesktopInfo(out int desktopWidth, out int desktopHeight, out int desktopRefresh); + // TODO ^ + int desktopRefresh = 60; + + // // testing + MaterialVideoMode[] test = [ + new () { Width = 1920, Height = 1080, RefreshRate = 60 }, + new () { Width = 1600, Height = 900, RefreshRate = 60 }, + new () { Width = 1366, Height = 768, RefreshRate = 60 }, + new () { Width = 1280, Height = 720, RefreshRate = 60 }, + new () { Width = 1024, Height = 576, RefreshRate = 60 }, + ]; + modeCount = test.Length; for (int i = 0; i < modeCount; i++) { - MaterialVideoMode info = new(); + // MaterialVideoMode info = new(); + MaterialVideoMode info = test[i]; + // materials.GetModeInfo(adapter, i, out info); if (info.Width < 640 || info.Height < 480) { @@ -414,13 +425,18 @@ public override bool Init() { // materials.AddModeChangeCallBack(VideoMode_AdjustForModeChange); SetInitialized(true); -#endif return true; } + static void VideoMode_AdjustForModeChange() { + + } + public override bool SetMode(int width, int height, bool windowed) { - ref VMode mode = ref RequestedWindowVideoMode(); // todo FindVideoMode/GetMode + int foundMode = FindVideoMode(width, height, windowed); + VMode mode = GetMode(foundMode); + config.VideoMode.Width = mode.Width; config.VideoMode.Height = mode.Height; @@ -430,6 +446,8 @@ public override bool SetMode(int width, int height, bool windowed) { config.VideoMode.RefreshRate = mode.RefreshRate; #endif + config.SetFlag(MaterialSystem_Config_Flags.Windowed, windowed); + if (!SetModeOnce) { if (!materials.SetMode(game.GetMainDeviceWindow(), config)) return false; @@ -440,9 +458,18 @@ public override bool SetMode(int width, int height, bool windowed) { return true; } + OverrideMaterialSystemConfig(config); + return true; } + private void OverrideMaterialSystemConfig(MaterialSystem_Config config) { + bool lightmapsNeedReloading = materials.OverrideConfig(config, false); + if (lightmapsNeedReloading) { + + } + } + private void InitStartupScreen() { } void AdjustForModeChange() { diff --git a/Source.GUI.Controls/TextImage.cs b/Source.GUI.Controls/TextImage.cs index fe2ab65e..579f591d 100644 --- a/Source.GUI.Controls/TextImage.cs +++ b/Source.GUI.Controls/TextImage.cs @@ -90,7 +90,7 @@ private void SetText(ReadOnlySpan text, bool clearUnlocalizedSymbol) { public void GetText(Span buffer) { // unicodetoansi - Text.CopyTo(buffer); + Text.SliceNullTerminatedString().CopyTo(buffer); if (AllCaps) { for (int i = 0; i < buffer.Length; i++) From f57612f35a3e6fdab333b29b5db1aaf831fa5f2f Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Fri, 30 Jan 2026 08:59:18 +0000 Subject: [PATCH 10/11] Desktop Info --- .../MaterialSystem/IMaterialSystem.cs | 4 ++ Source.Common/ShaderAPI/IShaderDevice.cs | 1 + Source.Engine/Game.cs | 38 ++++++++++++++++++- Source.Engine/SysModes.cs | 8 ++-- Source.MaterialSystem/MaterialSystem.cs | 31 +++++++++++---- Source.ShaderAPI.Gl46/ShaderAPIGl46.cs | 8 +++- 6 files changed, 74 insertions(+), 16 deletions(-) diff --git a/Source.Common/MaterialSystem/IMaterialSystem.cs b/Source.Common/MaterialSystem/IMaterialSystem.cs index 48b36aa3..066d0df4 100644 --- a/Source.Common/MaterialSystem/IMaterialSystem.cs +++ b/Source.Common/MaterialSystem/IMaterialSystem.cs @@ -190,7 +190,11 @@ public interface IMaterialSystem void EndFrame(); void SwapBuffers(); MaterialSystem_Config GetCurrentConfigForVideoCard(); + bool UpdateConfig(bool forceUpdate); bool OverrideConfig(MaterialSystem_Config config, bool forceUpdate); + int GetDisplayAdapterCount(); + int GetCurrentAdapter(); + int GetModeCount(int adapter); bool SetMode(IWindow window, MaterialSystem_Config config); IMaterial CreateMaterial(ReadOnlySpan name, ReadOnlySpan textureGroupName, KeyValues keyValues); IMaterial CreateMaterial(ReadOnlySpan name, KeyValues keyValues); diff --git a/Source.Common/ShaderAPI/IShaderDevice.cs b/Source.Common/ShaderAPI/IShaderDevice.cs index bf97202c..673fec65 100644 --- a/Source.Common/ShaderAPI/IShaderDevice.cs +++ b/Source.Common/ShaderAPI/IShaderDevice.cs @@ -66,6 +66,7 @@ public interface IShaderDevice void Present(); void ReacquireResources(); void ReleaseResources(); + int GetModeCount(int adapter); } public struct ShaderDisplayMode { diff --git a/Source.Engine/Game.cs b/Source.Engine/Game.cs index 7c7f3cd7..c85ff6ab 100644 --- a/Source.Engine/Game.cs +++ b/Source.Engine/Game.cs @@ -9,6 +9,7 @@ using Source.Common.Launcher; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; namespace Source.Engine; @@ -161,8 +162,38 @@ private void DispatchInputEvent(in InputEvent ev) { } } - public void GetDesktopInfo(out int width, out int height, out int refreshrate) { - throw new NotImplementedException(); + int DesktopWidth; + int DesktopHeight; + int DesktopRefreshRate; + public void GetDesktopInfo(out int width, out int height, out int refreshRate) { + // order of initialization means that this might get called early. In that case go ahead and grab the current + // screen window and setup based on that. + // we need to do this when initializing the base list of video modes, for example + if (DesktopWidth == 0) { + IntPtr dc = GetDC(IntPtr.Zero); + width = GetDeviceCaps(dc, 8); // HORZRES + height = GetDeviceCaps(dc, 10); // VERTRES + refreshRate = GetDeviceCaps(dc, 116); // VREFRESH + return; + } + + width = DesktopWidth; + height = DesktopHeight; + refreshRate = DesktopRefreshRate; + } + + // FIXME: Probably shouldn't be here + [DllImport("user32.dll")] + static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport("gdi32.dll")] + static extern int GetDeviceCaps(IntPtr hdc, int index); + + void UpdateDesktopInformation() { + IntPtr dc = GetDC(window.GetHandle()); + DesktopWidth = GetDeviceCaps(dc, 8); // HORZRES + DesktopHeight = GetDeviceCaps(dc, 10); // VERTRES + DesktopRefreshRate = GetDeviceCaps(dc, 116); // VREFRESH } public IWindow GetMainDeviceWindow() { @@ -205,6 +236,9 @@ public void PlayStartupVideos() { public void SetGameWindow(IWindow window) { this.window = window; + + if (DesktopWidth == 0 || DesktopHeight == 0) + UpdateDesktopInformation(); } public void SetWindowSize(int w, int h) { diff --git a/Source.Engine/SysModes.cs b/Source.Engine/SysModes.cs index 6a4fc432..2ab04509 100644 --- a/Source.Engine/SysModes.cs +++ b/Source.Engine/SysModes.cs @@ -367,12 +367,10 @@ public override bool Init() { if (commandLine.FindParm("-small") != 0) allowSmallModes = true; - int adapter = 0;//materials.GetCurrentAdapter(); - int modeCount = 1;//materials.GetModeCount(adapter); + int adapter = materials.GetCurrentAdapter(); + int modeCount = materials.GetModeCount(adapter); - // game.GetDesktopInfo(out int desktopWidth, out int desktopHeight, out int desktopRefresh); - // TODO ^ - int desktopRefresh = 60; + game.GetDesktopInfo(out int desktopWidth, out int desktopHeight, out int desktopRefresh); // // testing MaterialVideoMode[] test = [ diff --git a/Source.MaterialSystem/MaterialSystem.cs b/Source.MaterialSystem/MaterialSystem.cs index 4ee6f323..6065b050 100644 --- a/Source.MaterialSystem/MaterialSystem.cs +++ b/Source.MaterialSystem/MaterialSystem.cs @@ -130,7 +130,7 @@ public void ModInit() { UpdateConfig(false); } - private bool UpdateConfig(bool forceUpdate) { + public bool UpdateConfig(bool forceUpdate) { MaterialSystem_Config config = new(); Config.CopyInstantiatedReferenceTo(config); ReadConfigFromConVars(config); @@ -366,7 +366,7 @@ public int GetDisplayAdapterCount() { public int GetCurrentAdapter() => ShaderDevice.GetCurrentAdapter(); - public int GetModeCount(int adapter) => throw new NotImplementedException(); + public int GetModeCount(int adapter) => ShaderDevice.GetModeCount(adapter); public void ModShutdown() { @@ -389,7 +389,18 @@ static void mat_setvideomode(in TokenizedCommand args) { [ConCommand("mat_savechanges", "saves current video configuration to the registry")] static void mat_savechanges(in TokenizedCommand args) { // commandLine.RemoveParm("-safe"); - // UpdateMaterialSystemConfig(); + UpdateMaterialSystemConfig(); + } + + static void UpdateMaterialSystemConfig() { + // if (host_state.worldbrush && !host_state.worldbrush->lightdata) { + // mat_fullbright.SetValue(1); + // } + + bool lightmapsNeedReloading = Singleton().UpdateConfig(false); //fixme + if (lightmapsNeedReloading) { + + } } @@ -683,7 +694,11 @@ public void CreateDebugMaterials() { throw new NotImplementedException(); } - public void RestoreShaderObjects(IServiceProvider services, int changeFlags) { + void ReleaseShaderObjects() { + // todo + } + + public void RestoreShaderObjects(IServiceProvider? services, int changeFlags) { if (services != null) { ShaderAPI = services.GetRequiredService(); ShaderDevice = services.GetRequiredService(); @@ -802,15 +817,15 @@ private void UncacheAllMaterials() { } void ReloadTextures() { - throw new NotImplementedException(); + // todo } - void ReloadMaterials() { - throw new NotImplementedException(); + void ReloadMaterials(ReadOnlySpan subString = default) { + // todo } void RecomputeAllStateSnapshots() { - throw new NotImplementedException(); + // todo } public event Action? Restore; diff --git a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs index a8436ce0..b1cbe158 100644 --- a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs +++ b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs @@ -605,8 +605,14 @@ internal unsafe void GL_LoadExtensions(delegate* unmanaged[Cdecl] public bool IsActive() => Device != null; public bool IsUsingGraphics() => IsActive(); + + public int GetCurrentAdapter() { - throw new NotImplementedException(); + return 0; // TODO + } + + public int GetModeCount(int adapter) { + return 1; // TODO } From 6cc4e65dc4f036380ea6cad111a182e8157336ff Mon Sep 17 00:00:00 2001 From: callumok2004 Date: Mon, 2 Feb 2026 18:33:26 +0000 Subject: [PATCH 11/11] Cleanups --- Game.UI/GameUI.cs | 2 +- Game.UI/LabeledCommandComboBox.cs | 11 ++++++----- Game.UI/LoadingDialog.cs | 12 ++++++------ Game.UI/OptionsDialog.cs | 2 +- Game.UI/OptionsSubKeyboard.cs | 19 +++++++++---------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Game.UI/GameUI.cs b/Game.UI/GameUI.cs index 6f7cb68a..cc3e90ad 100644 --- a/Game.UI/GameUI.cs +++ b/Game.UI/GameUI.cs @@ -104,7 +104,7 @@ public void Initialize(IEngineAPI engineAPI) { localize.AddFile("Resource/gameui_%language%.txt", "GAME", true); engineAPI.GetRequiredService().LoadCurrentGameInfo(); localize.AddFile("Resource/valve_%language%.txt", "GAME", true); - // I have no idea why this one defines needed resource strings... + // I have no idea why this one defines needed resource strings... TODO: Is this still needed? localize.AddFile("Resource/itemtest_%language%.txt", "GAME", true); staticPanel = new BasePanel(this); diff --git a/Game.UI/LabeledCommandComboBox.cs b/Game.UI/LabeledCommandComboBox.cs index db814c45..4ba13ff9 100644 --- a/Game.UI/LabeledCommandComboBox.cs +++ b/Game.UI/LabeledCommandComboBox.cs @@ -1,3 +1,4 @@ +using Source.Common.Client; using Source.Common.Formats.Keyvalues; using Source.Common.GUI; using Source.GUI.Controls; @@ -7,6 +8,8 @@ namespace Game.UI; [PanelAlias("CLabeledCommandComboBox")] public class LabeledCommandComboBox : ComboBox { + readonly public IEngineClient engine = Singleton(); + const int MAX_NAME_LEN = 256; const int MAX_COMMAND_LEN = 256; @@ -38,18 +41,16 @@ public void AddItem(ReadOnlySpan text, ReadOnlySpan command) { CommandItem newItem = new CommandItem { name = new char[MAX_NAME_LEN], command = new char[MAX_COMMAND_LEN], + comboBoxID = base.AddItem(text, null) }; - newItem.comboBoxID = base.AddItem(text, null); items.Add(newItem); text.CopyTo(newItem.name); if (text[0] == '#') { ReadOnlySpan localized = Localize.Find(text); - if (!localized.IsEmpty) { - // todo UnicodeToANSI + if (!localized.IsEmpty) localized.CopyTo(newItem.name); - } } command.CopyTo(newItem.command); @@ -95,7 +96,7 @@ public void ApplyChanges() { Assert(CurrentSelection < items.Count); CommandItem item = items[CurrentSelection]; - // engine.ClientCmd_Unrestricted(item.command); + engine.ClientCmd_Unrestricted(item.command); StartSelection = CurrentSelection; } diff --git a/Game.UI/LoadingDialog.cs b/Game.UI/LoadingDialog.cs index 4b18404d..f4f48dbc 100644 --- a/Game.UI/LoadingDialog.cs +++ b/Game.UI/LoadingDialog.cs @@ -12,6 +12,7 @@ public class LoadingDialog : Frame { readonly public IGameUI GameUI = Singleton(); readonly public ModInfo ModInfo = Singleton(); + readonly IEngineClient engine = Singleton(); ProgressBar Progress; ProgressBar Progress2; @@ -29,8 +30,8 @@ public class LoadingDialog : Frame bool ConsoleStyle; float ProgressFraction; - [PanelAnimationVar("0")] int AdditionalIndentX; - [PanelAnimationVar("0")] int AdditionalIndentY; + [PanelAnimationVar("0")] protected int AdditionalIndentX; + [PanelAnimationVar("0")] protected int AdditionalIndentY; public override void PerformLayout() { if (ConsoleStyle) { @@ -52,11 +53,11 @@ public override void PerformLayout() { else if (Center) MoveToCenterOfScreen(); else { - Surface.GetWorkspaceBounds(out int x, out int y, out int screenWide, out int screenTall); + Surface.GetWorkspaceBounds(out _, out _, out int screenWide, out int screenTall); GetSize(out int wide, out int tall); - x = screenWide - (wide + 10); - y = screenTall - (tall + 10); + int x = screenWide - (wide + 10); + int y = screenTall - (tall + 10); x -= AdditionalIndentX; y -= AdditionalIndentY; @@ -71,7 +72,6 @@ public override void OnClose() { HideOtherDialogs(false); base.OnClose(); } - IEngineClient engine = Singleton(); public override void OnCommand(ReadOnlySpan command) { if (command.Equals("Cancel", StringComparison.OrdinalIgnoreCase)) { engine.ClientCmd_Unrestricted("disconnect\n"); diff --git a/Game.UI/OptionsDialog.cs b/Game.UI/OptionsDialog.cs index 19afabbc..7b92df74 100644 --- a/Game.UI/OptionsDialog.cs +++ b/Game.UI/OptionsDialog.cs @@ -23,7 +23,7 @@ public OptionsDialog(Panel? parent) : base(parent, "OptionsDialog") { #if GMOD_DLL // gmod's page order is different OptionsSubMultiplayer = new OptionsSubMultiplayer(this, "OptionsSubMultiplayer"); - AddPage(OptionsSubMultiplayer, "#GameUI_Multiplayer"); + AddPage(OptionsSubMultiplayer, "#GameUI_Game"); AddPage(new OptionsSubKeyboard(this, null), "#GameUI_Keyboard"); AddPage(new OptionsSubMouse(this, null), "#GameUI_Mouse"); OptionsSubAudio = new(this, null); diff --git a/Game.UI/OptionsSubKeyboard.cs b/Game.UI/OptionsSubKeyboard.cs index d236fac2..3f5f9f3f 100644 --- a/Game.UI/OptionsSubKeyboard.cs +++ b/Game.UI/OptionsSubKeyboard.cs @@ -13,7 +13,7 @@ public class OptionsSubKeyboard : PropertyPage { struct KeyBinding { - public char[]? Binding; + public string? Binding; } OptionsSubKeyboardAdvancedDlg? OptionsSubKeyboardAdvancedDlg; @@ -21,9 +21,8 @@ struct KeyBinding Button SetBindingButton; Button ClearBindingButton; - KeyBinding[] KeyBindings = new KeyBinding[(int)ButtonCode.Last]; - - List KeysToUnbind = new(); + readonly KeyBinding[] KeyBindings = new KeyBinding[(int)ButtonCode.Last]; + readonly List KeysToUnbind = []; public OptionsSubKeyboard(Panel? parent, ReadOnlySpan name) : base(parent, name) { for (int i = 0; i < KeyBindings.Length; i++) @@ -92,8 +91,8 @@ public override void OnCommand(ReadOnlySpan command) { readonly IEngineClient engine = Singleton(); public void ParseActionDescriptions() { - Span binding = stackalloc char[512];//256 - Span description = stackalloc char[512];//256 + Span binding = stackalloc char[256]; + Span description = stackalloc char[256]; long size = fileSystem.Size("scripts/kb_act.lst"); if (size <= 0) return; @@ -112,13 +111,13 @@ public void ParseActionDescriptions() { if (strlen(token) == 0) break; - token.CopyTo(binding); + strcpy(binding, token); data = engine.ParseFile(data, token); if (strlen(token) == 0) break; - token.CopyTo(description); + strcpy(description, token); if (description[0] != '=') { if (streq(binding, "blank")) { @@ -216,7 +215,7 @@ public void SaveCurrentBindings() { if (!binding.IsEmpty) continue; - KeyBindings[i].Binding = binding.ToArray(); + KeyBindings[i].Binding = new(binding); } } @@ -245,7 +244,7 @@ public void Finish(ButtonCode code) { int r = KeyBindList.GetItemOfInterest(); KeyBindList.EndCaptureMode(CursorCode.Arrow); - KeyValues item = KeyBindList.GetItemData(r); + KeyValues? item = KeyBindList.GetItemData(r); if (item != null) { if (code != ButtonCode.None && code != ButtonCode.KeyEscape && code != ButtonCode.Invalid) { // AddBinding(item, inputSystem.ButtonCodeToString(code));