OMRIncrementalWeaponModel.cs
using Sandbox;
/// <summary>
/// BaseWeaponModel variant for weapons that reload one round at a time.
///
/// Facepunch's Spaghelli M4 exposes an advanced reload stance through
/// b_reloading + b_reloading_shell instead of relying on the ordinary b_reload
/// animation alone. BaseCombatWeapon already owns the authoritative incremental
/// reload timing and calls these model hooks on every peer; this component only
/// translates those hooks into the documented AnimGraph parameters.
/// </summary>
public sealed class OMRIncrementalWeaponModel : BaseWeaponModel
{
[Property, Group( "Incremental Reload" ), Title( "Reload Stance Parameter" )]
public string ReloadStanceParameter { get; set; } = "b_reloading";
[Property, Group( "Incremental Reload" ), Title( "Shell Insert Parameter" )]
public string ShellInsertParameter { get; set; } = "b_reloading_shell";
[Property, Group( "Incremental Reload" ), Title( "First Shell Parameter" ), Description( "Optional special first-shell trigger. Disabled by default because whether a carrier-first animation is appropriate depends on authored weapon state." )]
public string FirstShellInsertParameter { get; set; } = "b_reloading_first_shell";
[Property, Group( "Incremental Reload" ), Title( "Use First Shell Trigger" )]
public bool UseFirstShellInsertAnimation { get; set; } = false;
[Property, Group( "Incremental Reload" ), Title( "Simple Reload Parameter" ), Description( "The stock b_reload parameter. OMR keeps it off while the advanced incremental stance is active so the two animation paths do not fight each other." )]
public string SimpleReloadParameter { get; set; } = "b_reload";
private bool _firstInsertPending;
public override void OnReloadStart()
{
ResolveRenderer();
_firstInsertPending = true;
if ( Renderer is null || !Renderer.IsValid )
return;
SetBool( SimpleReloadParameter, false );
SetBool( ReloadStanceParameter, true );
}
public override void OnIncrementalReload()
{
ResolveRenderer();
if ( Renderer is null || !Renderer.IsValid )
return;
if ( _firstInsertPending && UseFirstShellInsertAnimation )
{
SetBool( FirstShellInsertParameter, true );
}
else
{
SetBool( ShellInsertParameter, true );
}
_firstInsertPending = false;
}
public override void OnReloadFinish()
{
ResolveRenderer();
ClearReloadState();
base.OnReloadFinish();
}
public override void OnReloadCancel()
{
ResolveRenderer();
ClearReloadState();
base.OnReloadCancel();
}
private void ClearReloadState()
{
_firstInsertPending = false;
if ( Renderer is null || !Renderer.IsValid )
return;
SetBool( ReloadStanceParameter, false );
SetBool( SimpleReloadParameter, false );
}
private void ResolveRenderer()
{
if ( Renderer is not null && Renderer.IsValid )
return;
Renderer = GameObject.Components.Get<SkinnedModelRenderer>(
FindMode.InSelf |
FindMode.InDescendants
);
}
private void SetBool( string parameter, bool value )
{
if ( Renderer is null || string.IsNullOrWhiteSpace( parameter ) )
return;
Renderer.Set( parameter, value );
}
}