OneMoreRoundWeapon.ModelPresentation.cs
using Sandbox;

/// <summary>
/// Presentation-only bridge between generic weapon events and optional model
/// adapters living on spawned view/world model prefabs.
///
/// BaseWeaponModel still owns stock attack/reload/muzzle/brass/tracer behavior.
/// This layer only handles OMR capabilities that BaseWeaponModel does not know
/// about, currently generic manual cycling.
/// </summary>
public partial class OneMoreRoundWeapon
{
	private GameObject _adapterViewModel;
	private GameObject _adapterWorldModel;
	private OMRWeaponModelPresentationAdapter _viewModelPresentationAdapter;
	private OMRWeaponModelPresentationAdapter _worldModelPresentationAdapter;

	private void BindWeaponModelPresentationEvents()
	{
		PresentationEvent -= HandleWeaponModelPresentationEvent;
		PresentationEvent += HandleWeaponModelPresentationEvent;
	}

	private void HandleWeaponModelPresentationEvent(
		WeaponPresentationEvent presentationEvent
	)
	{
		ResolveWeaponModelPresentationAdapters();

		switch ( presentationEvent.Kind )
		{
			case WeaponPresentationEventKind.CycleStarted:
				BeginCycleOnPresentationAdapters();
				break;

			case WeaponPresentationEventKind.CycleCompleted:
				CompleteCycleOnPresentationAdapters();
				break;

			case WeaponPresentationEventKind.Holstered:
				ResetWeaponModelPresentationState();
				break;
		}
	}

	private void UpdateWeaponModelPresentation()
	{
		ResolveWeaponModelPresentationAdapters();

		if ( HasOpticProfile )
		{
			_viewModelPresentationAdapter?.ApplyOpticPresentation(
				AimFraction,
				IsScopePresentationActive
			);

			_worldModelPresentationAdapter?.ApplyOpticPresentation(
				AimFraction,
				IsScopePresentationActive
			);
		}
		else
		{
			_viewModelPresentationAdapter?.ResetOpticPresentation();
			_worldModelPresentationAdapter?.ResetOpticPresentation();
		}

		if ( !IsCyclePresentationActive )
			return;

		float progress = CyclePresentationProgress;

		_viewModelPresentationAdapter?.UpdateCyclePresentation( progress );
		_worldModelPresentationAdapter?.UpdateCyclePresentation( progress );
	}

	private void BeginCycleOnPresentationAdapters()
	{
		if ( !HasCycleProfile )
			return;

		float duration = CycleProfile.Duration;
		OMRWeaponCycleKind kind = CycleProfile.Kind;

		_viewModelPresentationAdapter?.BeginCyclePresentation(
			kind,
			duration
		);

		_worldModelPresentationAdapter?.BeginCyclePresentation(
			kind,
			duration
		);
	}

	private void CompleteCycleOnPresentationAdapters()
	{
		_viewModelPresentationAdapter?.CompleteCyclePresentation();
		_worldModelPresentationAdapter?.CompleteCyclePresentation();
	}

	private void ResolveWeaponModelPresentationAdapters()
	{
		GameObject currentViewModel = ViewModel;
		GameObject currentWorldModel = WorldModel;

		if ( currentViewModel != _adapterViewModel )
		{
			_adapterViewModel = currentViewModel;
			_viewModelPresentationAdapter = FindPresentationAdapter(
				currentViewModel
			);
		}

		if ( currentWorldModel != _adapterWorldModel )
		{
			_adapterWorldModel = currentWorldModel;
			_worldModelPresentationAdapter = FindPresentationAdapter(
				currentWorldModel
			);
		}
	}

	private static OMRWeaponModelPresentationAdapter FindPresentationAdapter(
		GameObject model
	)
	{
		if ( model is null || !model.IsValid )
			return null;

		return model.Components.Get<OMRWeaponModelPresentationAdapter>(
			FindMode.InSelf |
			FindMode.InDescendants
		);
	}

	private void ResetWeaponModelPresentationState()
	{
		_viewModelPresentationAdapter?.ResetCyclePresentation();
		_worldModelPresentationAdapter?.ResetCyclePresentation();
		_viewModelPresentationAdapter?.ResetOpticPresentation();
		_worldModelPresentationAdapter?.ResetOpticPresentation();

		_adapterViewModel = null;
		_adapterWorldModel = null;
		_viewModelPresentationAdapter = null;
		_worldModelPresentationAdapter = null;
	}
}