Npcs/Roller/RollerMorphs.cs
using Sandbox.Utility;

namespace Sandbox.Npcs.Roller;

/// <summary>
/// Drives morph targets and material glow on the rollermine mesh based on hunting state.
/// Same mesh as hoverball — uses Coils_Deployed and Pins_Deployed morphs.
/// </summary>
public sealed class RollerMorphs : Component
{
	private RollerNpc _rollermine;
	private SkinnedModelRenderer _renderer;
	private Material _glowMaterialCopy;

	private float _coils;
	private float _pins;
	private float _brightnessTarget;
	private float _brightnessCurrent;
	private float _brightnessTimer;

	private float _coilsFrom;
	private float _coilsTo;
	private float _coilsTime;
	private float _pinsFrom;
	private float _pinsTo;
	private float _pinsTime;

	[Property] public float Speed { get; set; } = 15f;
	public float TransitionDuration => 0.3f;
	[Property] public Material GlowMaterial { get; set; }

	public Color IllumTint => Color.FromBytes( 20, 165, 200 );
	public float IllumBrightness => 8f;

	protected override void OnStart()
	{
		_rollermine = GetComponent<RollerNpc>();
		_renderer = GetComponentInChildren<SkinnedModelRenderer>();

		if ( GlowMaterial is not null && _renderer.IsValid() )
		{
			_glowMaterialCopy = GlowMaterial.CreateCopy();
			_renderer.MaterialOverride = _glowMaterialCopy;
			_renderer.SceneModel.Batchable = false;
		}
	}

	protected override void OnUpdate()
	{
		if ( !_rollermine.IsValid() || !_renderer.IsValid() ) return;

		var hunting = _rollermine.IsHunting;

		var targetCoils = hunting ? 1f : 0f;
		var targetPins = hunting ? 1f : 0f;

		if ( targetCoils != _coilsTo )
		{
			_coilsFrom = _coils;
			_coilsTo = targetCoils;
			_coilsTime = 0f;
		}

		if ( targetPins != _pinsTo )
		{
			_pinsFrom = _pins;
			_pinsTo = targetPins;
			_pinsTime = 0f;
		}

		_coilsTime = Math.Min( _coilsTime + Time.Delta / TransitionDuration, 1f );
		_pinsTime = Math.Min( _pinsTime + Time.Delta / TransitionDuration, 1f );

		_coils = MathX.Lerp( _coilsFrom, _coilsTo, Easing.BounceOut( _coilsTime ) );
		_pins = MathX.Lerp( _pinsFrom, _pinsTo, Easing.BounceOut( _pinsTime ) );

		_renderer.SceneModel?.Morphs.Set( "Coils_Deployed", _coils );
		_renderer.SceneModel?.Morphs.Set( "Pins_Deployed", _pins );

		UpdateGlowMaterial();
	}

	void UpdateGlowMaterial()
	{
		if ( _glowMaterialCopy is null ) return;

		var hunting = _rollermine.IsHunting;
		var brightness = hunting ? IllumBrightness : 0f;

		if ( hunting )
		{
			_brightnessTimer -= Time.Delta;
			if ( _brightnessTimer <= 0f )
			{
				_brightnessTarget = Random.Shared.Float( 6f, 8f );
				_brightnessTimer = Random.Shared.Float( 0.1f, 0.4f );
			}
			_brightnessCurrent = MathX.Approach( _brightnessCurrent, _brightnessTarget, Time.Delta * 7f );
			brightness = _brightnessCurrent;
		}

		_glowMaterialCopy.Set( "g_vSelfIllumTint", hunting ? IllumTint : Color.Black );
		_glowMaterialCopy.Set( "g_flSelfIllumBrightness", brightness * _coils );
	}
}