UI/SettingsPanel.razor
@using System;
@using Sandbox;
@using Sandbox.UI;
@inherits Panel
<root class="settings-panel">
<div class="settings-tabs">
<div class=@ControlsTabClass onclick=@ShowControls>CONTROLS</div>
<div class=@PerformanceTabClass onclick=@ShowPerformance>PERFORMANCE</div>
</div>
<div class="settings-list">
@if ( !_performanceTab )
{
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">MOUSE SENSITIVITY</div>
<div class="setting-description">
One More Round look multiplier applied after your normal s&box sensitivity.
ADS zoom matching still scales from this value.
</div>
</div>
<div class="setting-control">
<div class="setting-step" onclick=@SensitivityDown>-</div>
<div class="setting-value">@SensitivityText</div>
<div class="setting-step" onclick=@SensitivityUp>+</div>
</div>
</div>
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">FIELD OF VIEW</div>
<div class="setting-description">
Horizontal gameplay FOV. Higher values expose more peripheral space,
but the official viewmodels are designed around more moderate FOVs.
</div>
</div>
<div class="setting-control">
<div class="setting-step" onclick=@FovDown>-</div>
<div class="setting-value">@FovText</div>
<div class="setting-step" onclick=@FovUp>+</div>
</div>
</div>
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">CROUCH INPUT</div>
<div class="setting-description">
Choose whether crouch toggles or remains active only while held.
</div>
</div>
<div class="setting-cycle" onclick=@CycleCrouch>
@CrouchText
</div>
</div>
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">SPRINT INPUT</div>
<div class="setting-description">
Hybrid keeps the current tap-to-toggle and hold-to-sprint behavior.
</div>
</div>
<div class="setting-cycle" onclick=@CycleSprint>
@SprintText
</div>
</div>
}
else
{
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">FIRST PERSON RESOLUTION</div>
<div class="setting-description">Render budget for your arms and weapon.</div>
</div>
<div class="setting-cycle" onclick=@CycleViewModelQuality>@OMRClientPerformance.ViewModelQualityName</div>
</div>
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">SCOPE HIPFIRE PREVIEW</div>
<div class="setting-description">ADS always refreshes every frame.</div>
</div>
<div class="setting-cycle" onclick=@CycleScopePreview>@ScopePreviewText</div>
</div>
<div class="setting-row">
<div class="setting-copy">
<div class="setting-name">BOT DIAGNOSTICS</div>
<div class="setting-description">Detailed Sandbox AI readout.</div>
</div>
<div class="setting-cycle" onclick=@ToggleBotDebug>@(OMRClientPerformance.BotDebugOverlay ? "ON" : "OFF")</div>
</div>
}
</div>
<div class="settings-panel-actions">
<div class="settings-reset" onclick=@ResetDefaults>
RESET DEFAULTS
</div>
</div>
</root>
@code
{
private bool _performanceTab;
private string ControlsTabClass => !_performanceTab ? "settings-tab selected" : "settings-tab";
private string PerformanceTabClass => _performanceTab ? "settings-tab selected" : "settings-tab";
private void ShowControls() => _performanceTab = false;
private void ShowPerformance() => _performanceTab = true;
private string ScopePreviewText => OMRClientPerformance.ScopePreviewFrameRate <= 0 ? "FULL RATE" : $"{OMRClientPerformance.ScopePreviewFrameRate} FPS";
private void CycleViewModelQuality() => OMRClientPerformance.ViewModelQuality = ( Math.Clamp( OMRClientPerformance.ViewModelQuality, 0, 2 ) + 1 ) % 3;
private void CycleScopePreview() => OMRClientPerformance.ScopePreviewFrameRate = OMRClientPerformance.ScopePreviewFrameRate <= 0 ? 30 : 0;
private void ToggleBotDebug() => OMRClientPerformance.BotDebugOverlay = !OMRClientPerformance.BotDebugOverlay;
private string SensitivityText =>
OMRPlayerSettings
.Sensitivity
.ToString(
"0.00"
);
private string FovText =>
$"{MathF.Round( OMRPlayerSettings.FieldOfView )}°";
private string CrouchText =>
OMRPlayerSettings
.CrouchBehavior
.ToString()
.ToUpperInvariant();
private string SprintText =>
OMRPlayerSettings
.SprintBehavior
.ToString()
.ToUpperInvariant();
private void SensitivityDown()
{
OMRPlayerSettings.Sensitivity -=
0.10f;
}
private void SensitivityUp()
{
OMRPlayerSettings.Sensitivity +=
0.10f;
}
private void FovDown()
{
OMRPlayerSettings.FieldOfView -=
5f;
}
private void FovUp()
{
OMRPlayerSettings.FieldOfView +=
5f;
}
private void CycleCrouch()
{
OMRPlayerSettings.CrouchBehavior =
OMRPlayerSettings.CrouchBehavior ==
CrouchBehavior.Toggle
? CrouchBehavior.Hold
: CrouchBehavior.Toggle;
}
private void CycleSprint()
{
OMRPlayerSettings.SprintBehavior =
OMRPlayerSettings.SprintBehavior switch
{
SprintInputBehavior.Hold =>
SprintInputBehavior.Toggle,
SprintInputBehavior.Toggle =>
SprintInputBehavior.Hybrid,
_ =>
SprintInputBehavior.Hold
};
}
private void ResetDefaults()
{
OMRPlayerSettings.ResetDefaults();
OMRClientPerformance.ViewModelQuality = 0;
OMRClientPerformance.ScopePreviewFrameRate = 30;
OMRClientPerformance.BotDebugOverlay = false;
}
protected override int BuildHash()
{
HashCode hash =
new();
hash.Add(
MathF.Round(
OMRPlayerSettings.Sensitivity *
100f
)
);
hash.Add(
MathF.Round(
OMRPlayerSettings.FieldOfView
)
);
hash.Add(
OMRPlayerSettings.CrouchBehavior
);
hash.Add(
OMRPlayerSettings.SprintBehavior
);
hash.Add( _performanceTab );
hash.Add( OMRClientPerformance.ViewModelQuality );
hash.Add( OMRClientPerformance.ScopePreviewFrameRate );
hash.Add( OMRClientPerformance.BotDebugOverlay );
return hash.ToHashCode();
}
}