OneMoreRoundWeapon.ViewModelFirePresentation.cs
using Sandbox;
using System;
/// <summary>
/// Keeps the authored ADS fire-animation scale stable for the lifetime of the
/// shot animation. This matters for manual-cycle weapons: gameplay can leave
/// ADS immediately after firing while the AnimGraph is still evaluating the
/// same b_attack pulse. Letting ironsights_fire_scale jump from the scoped
/// value back to 1 during that window can make the scope lurch into the camera.
/// </summary>
public partial class OneMoreRoundWeapon
{
private bool _viewModelFireScaleLockActive;
private float _viewModelFireScaleLockUntil = -1f;
private float _viewModelFireScaleLockValue = 1f;
public bool IsViewModelFireScaleLocked =>
_viewModelFireScaleLockActive &&
RealTime.Now < _viewModelFireScaleLockUntil;
public float ViewModelFireScaleLockRemaining =>
IsViewModelFireScaleLocked
? MathF.Max( _viewModelFireScaleLockUntil - RealTime.Now, 0f )
: 0f;
public float EffectiveViewModelFireScale =>
GetEffectiveViewModelFireScale();
private void LatchViewModelFireScaleForPrimaryShot()
{
if ( !IsAiming )
return;
float holdTime = GetViewModelFireScaleHoldTime();
if ( holdTime <= 0f )
return;
_viewModelFireScaleLockActive = true;
_viewModelFireScaleLockValue = MathX.Clamp( IronSightsFireScale, 0f, 1f );
_viewModelFireScaleLockUntil = RealTime.Now + holdTime;
ApplyViewModelFireScaleImmediately();
}
private float GetViewModelFireScaleHoldTime()
{
if ( !HasOpticProfile )
return 0f;
return MathF.Max( OpticProfile.ViewModelFireScaleHoldTime, 0f );
}
private float GetEffectiveViewModelFireScale()
{
if ( IsViewModelFireScaleLocked )
return _viewModelFireScaleLockValue;
return IsAiming
? MathX.Clamp( IronSightsFireScale, 0f, 1f )
: 1f;
}
private void UpdateViewModelFireScaleGuard( SkinnedModelRenderer renderer )
{
if ( renderer is null )
return;
if (
_viewModelFireScaleLockActive &&
RealTime.Now >= _viewModelFireScaleLockUntil
)
{
_viewModelFireScaleLockActive = false;
_viewModelFireScaleLockUntil = -1f;
}
renderer.Set(
"ironsights_fire_scale",
GetEffectiveViewModelFireScale()
);
}
private void ApplyViewModelFireScaleImmediately()
{
GameObject viewModel = ViewModel;
if ( viewModel is null || !viewModel.IsValid )
return;
SkinnedModelRenderer renderer = GetViewModelRenderer( viewModel );
if ( renderer is null )
return;
renderer.Set(
"ironsights_fire_scale",
GetEffectiveViewModelFireScale()
);
}
private void ResetViewModelFireScaleGuard()
{
_viewModelFireScaleLockActive = false;
_viewModelFireScaleLockUntil = -1f;
_viewModelFireScaleLockValue = 1f;
}
}