diff --git a/layout/pages/settings/video.xml b/layout/pages/settings/video.xml index 06eb998f..8567cc2f 100644 --- a/layout/pages/settings/video.xml +++ b/layout/pages/settings/video.xml @@ -5,6 +5,8 @@ + + @@ -31,7 +33,21 @@ - + + + + + + diff --git a/scripts/pages/settings/fov.js b/scripts/pages/settings/fov.js new file mode 100644 index 00000000..21684434 --- /dev/null +++ b/scripts/pages/settings/fov.js @@ -0,0 +1,62 @@ +class Fov { + static panels = { + /** @type {SettingsSlider} @static */ + fov: $('#FOV'), + /** @type {TextEntry} @static */ + horizontalFov: $('#FOV_Horizontal'), + aspectRatio: $('#FOV_Horizontal_AspectRatioEnum') + }; + + static loadSettings() { + this.panels.aspectRatio.SetSelected('aspectratio1'); + this.updateFOV(); + } + + static aspectRatio() { + const id = this.panels.aspectRatio.GetSelected().id; + switch (id) { + case 'aspectratio0': + return 4 / 3; + case 'aspectratio1': + return 16 / 9; + case 'aspectratio2': + return 16 / 10; + } + return Number.NaN; + } + + // based on https://casualhacks.net/Source-FOV-calculator.html + static fovToHorizontal(fov) { + const ratioRatio = this.aspectRatio() / (4 / 3); + return 2 * rad2deg(Math.atan(Math.tan(deg2rad(fov) / 2) * ratioRatio)); + } + + static horizontalToFov(horizontalFov) { + const ratioRatio = this.aspectRatio() / (4 / 3); + return 2 * rad2deg(Math.atan(Math.tan(deg2rad(horizontalFov) / 2) / ratioRatio)); + } + + static updateFOV() { + if (!this.panels.fov || !this.panels.horizontalFov) return; + + let fov = GameInterfaceAPI.GetSettingFloat('fov_desired'); + fov = Math.round(this.fovToHorizontal(fov)); + + if (!Number.isNaN(fov)) { + this.panels.horizontalFov.text = fov; + } + } + + static updateHorizontalFov() { + if (!this.panels.fov || !this.panels.horizontalFov) return; + + let fov = Number.parseFloat(this.panels.horizontalFov.text); + fov = Math.round(this.horizontalToFov(fov)); + + if (!Number.isNaN(fov)) { + const fovText = this.panels.fov.FindChildTraverse('Value'); + fovText.text = fov; + fovText.Submit(); + } + } +} diff --git a/scripts/pages/settings/settings.js b/scripts/pages/settings/settings.js index 29768eb5..f7038ccf 100644 --- a/scripts/pages/settings/settings.js +++ b/scripts/pages/settings/settings.js @@ -244,9 +244,7 @@ class MainMenuSettings { static initPanelsRecursive(panel) { // Initialise info panel event handlers - if (this.isSettingsPanel(panel) || this.isSpeedometerPanel(panel)) { - this.setPanelInfoEvents(panel); - } + this.setPanelInfoEvents(panel); // Initialise all the settings using persistent storage // Only Enum and EnumDropDown are currently supported, others can be added when/if needed @@ -312,15 +310,19 @@ class MainMenuSettings { static setPanelInfoEvents(panel) { const message = panel.GetAttributeString('infomessage', ''); + const title = panel.GetAttributeString('infotitle', ''); + + // Don't set events if there's no info to show + if (!this.isSettingsPanel(panel) && message === '' && title === '' && !panel.convar && !panel.bind) return; + // Default to true if not set const hasDocs = !(panel.GetAttributeString('hasdocspage', '') === 'false'); + panel.SetPanelEvent('onmouseover', () => { // Set onmouseover events for all settings panels this.showInfo( // If a panel has a specific title use that, if not use the panel's name. Child ID names vary between panel types, blame Valve - panel.GetAttributeString('infotitle', '') || - panel.FindChildTraverse('Title')?.text || - panel.FindChildTraverse('title')?.text, + title || panel.FindChildTraverse('Title')?.text || panel.FindChildTraverse('title')?.text, message, panel.convar ?? panel.bind, hasDocs, @@ -434,8 +436,4 @@ class MainMenuSettings { 'ConVarColorDisplay' ].includes(panel.paneltype); } - - static isSpeedometerPanel(panel) { - return ['SpeedometersContainer', 'RangeColorProfilesContainer'].includes(panel.id); - } } diff --git a/scripts/util/math.ts b/scripts/util/math.ts index f9096b09..306f3906 100644 --- a/scripts/util/math.ts +++ b/scripts/util/math.ts @@ -121,3 +121,11 @@ function mapAngleToScreenDist(angle: number, fov: number, length: number, scale: return Math.round((1 + Math.tan(angle * 0.5) / Math.tan(fov * 0.5)) * screenDist * 0.5); } } + +function deg2rad(x: number): number { + return (x / 180) * Math.PI; +} + +function rad2deg(x: number): number { + return (x * 180) / Math.PI; +}