Runtime console commands for tuning a weapon scope in-game. Finds the active SWB.Base.Weapon in the active scene and exposes ConCmds nz_scope (set/report magnification) and nz_scope_sway (toggle/report scope overlay sway), printing various live scope and camera state to logs.
using Sandbox;
using System.Linq;
namespace NZombies;
/// <summary>
/// TUNE A 2D SCOPE WHILE LOOKING THROUGH IT. `nz_scope`, `nz_scope_sway`.
///
/// ⛔ SCOPE FEEL IS NOT DERIVABLE, AND GUESSING AT IT COSTS A ROUND TRIP EACH TIME. TFA states a
/// magnification (the 1000 Yard Stare's RTScopeFOV works out to 2.5x) but it renders that into a
/// SMALL CIRCLE while the rest of the screen stays at normal FOV. SWB zooms the whole screen, so
/// the same 2.5 reads as far less magnification than the original. There is no arithmetic that
/// converts one into the other -- it has to be looked at.
///
/// ⚠️ RUNTIME ONLY. ScopeInfo lives on the prefab, which is read-only in game, so these change the
/// live weapon and print the value to be written back into the port. Say the number and it gets
/// baked; do not expect it to survive a respawn.
/// </summary>
public static class ScopeTuning
{
static SWB.Base.Weapon Active =>
Game.ActiveScene?.GetAllComponents<SWB.Base.Weapon>().FirstOrDefault( x => x.IsValid() );
/// <summary>Set (or report) the scope magnification. `nz_scope 4`.</summary>
[ConCmd( "nz_scope" )]
public static void Magnification( float x = -1f )
{
var w = Active;
if ( w is null ) { Log.Info( "[scope] no weapon equipped" ); return; }
if ( !w.Scoping )
{
Log.Info( $"[scope] '{w.DisplayName}' is not a scoped weapon (Scoping = false)" );
return;
}
if ( x > 0 )
{
w.ScopeInfo.Magnification = x;
// ⚠️ Magnification only wins while it is ABOVE ZERO — ScopeInfo.FOV is the fallback
// branch in PlayerCameraHandler, so a leftover FOV would take over the moment this
// dropped to 0 and the scope would silently jump to a different zoom.
Log.Info( $"[scope] {w.DisplayName}: magnification -> {x:0.##}x "
+ $"(FOV {Preferences.FieldOfView / x:0.#} deg at your {Preferences.FieldOfView:0.#})" );
}
else
{
// ⛔ REPORT THE LIVE STATE, NOT THE PREFAB'S. "The zoom does not work" has four
// possible causes and reasoning cannot separate them: the weapon may not be scoping,
// the magnification may not have deserialised, the camera handler may be absent, or it
// may be running and something downstream is overwriting the FOV. Each is one line here.
var cam = Game.ActiveScene?.GetAllComponents<SWB.Base.PlayerCameraHandler>()
.FirstOrDefault( x => x.IsValid() );
Log.Info( $"[scope] {w.DisplayName}" );
Log.Info( $"[scope] Scoping={w.Scoping} IsScoping={w.IsScoping} IsAiming={w.IsAiming}" );
Log.Info( $"[scope] Magnification={w.ScopeInfo.Magnification:0.###} "
+ $"FOV={w.ScopeInfo.FOV:0.#} Sway={w.ScopeInfo.Sway}" );
Log.Info( $"[scope] AimInfo.PlayerFOVZoom={w.AimInfo.PlayerFOVZoom:0.###} "
+ $"ShouldTuck={w.ShouldTuckVar} IsReloading={w.IsReloading}" );
Log.Info( $"[scope] cameraHandler={(cam is null ? "MISSING" : (cam.Enabled ? "enabled" : "DISABLED"))}"
+ $" weaponOnHandler={(cam?.Weapon is null ? "null" : cam.Weapon.DisplayName)}" );
Log.Info( $"[scope] prefs FOV={Preferences.FieldOfView:0.#} "
+ $"expected scoped FOV={(w.ScopeInfo.Magnification > 0 ? Preferences.FieldOfView / w.ScopeInfo.Magnification : -1):0.#}" );
Log.Info( $"[scope] texture {w.ScopeInfo.ScopeTexture}" );
}
}
/// <summary>Does the overlay drift with look and sway. `nz_scope_sway 0`.</summary>
[ConCmd( "nz_scope_sway" )]
public static void Sway( int on = -1 )
{
var w = Active;
if ( w is null ) { Log.Info( "[scope] no weapon equipped" ); return; }
if ( on >= 0 )
w.ScopeInfo.Sway = on != 0;
Log.Info( $"[scope] {w.DisplayName}: sway {w.ScopeInfo.Sway}" );
}
}