Combat/NpcGibSystem.cs

Two small components for NPC gib/dismemberment visuals. NpcDismembermentVisual finds its DemoNpc owner and calls ApplyDismembermentVisuals each update. GibLifetime fades a gib model over time and destroys the GameObject after a solid + fade duration.

Native Interop
using AdaptiveDirectorDemo.Director;
using Sandbox;
using System;

namespace AdaptiveDirectorDemo.Combat;

/// <summary>Body regions understood by the demo's localized damage adapter.</summary>
public enum NpcBodyRegion
{
	None,
	Head,
	Torso,
	LeftArm,
	RightArm,
	LeftHand,
	RightHand,
	LeftLeg,
	RightLeg,
	LeftFoot,
	RightFoot
}

/// <summary>
/// Applies persistent bone removal after the Citizen animation graph has updated.
/// Kept separate from DemoNpc so the override runs later in component order.
/// </summary>
[Title( "NPC Dismemberment Visual" )]
[Category( "Adaptive Director Demo" )]
public sealed class NpcDismembermentVisual : Component
{
	public DemoNpc Owner { get; set; }

	protected override void OnUpdate()
	{
		if ( Owner is null || !Owner.IsValid )
			Owner = Components.Get<DemoNpc>();

		Owner?.ApplyDismembermentVisuals();
	}
}

/// <summary>Fades physical gibs after a short readable lifetime.</summary>
[Title( "Gib Lifetime" )]
[Category( "Adaptive Director Demo" )]
public sealed class GibLifetime : Component
{
	[Property] public float SolidSeconds { get; set; } = 12.0f;
	[Property] public float FadeSeconds { get; set; } = 3.0f;

	private ModelRenderer _renderer;
	private Color _baseTint = Color.White;
	private TimeSince _age;

	protected override void OnStart()
	{
		_renderer = Components.Get<ModelRenderer>();
		if ( _renderer is not null )
			_baseTint = _renderer.Tint;
		_age = 0.0f;
	}

	protected override void OnUpdate()
	{
		if ( _age < SolidSeconds )
			return;

		var fade = Math.Clamp( (_age - SolidSeconds) / Math.Max( 0.1f, FadeSeconds ), 0.0f, 1.0f );
		if ( _renderer is not null && _renderer.IsValid )
			_renderer.Tint = _baseTint.WithAlpha( 1.0f - fade );

		if ( fade >= 1.0f )
			GameObject.Destroy();
	}
}