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 );
	}
}