Player/PlayerDamageIndicators.cs
public sealed class PlayerDamageIndicators : Component, IPlayerEvent
{
[RequireComponent] Player Player { get; set; }
float RadialDistanceFromCenter => 128f;
float RadialIndicatorLifetime => 2f;
TimeSince timeSinceHurt = 10f;
Vignette damageVignette;
List<(Vector3 WorldPos, TimeSince Lifetime)> radialIndicators = new();
protected override void OnDisabled()
{
damageVignette?.Destroy();
}
protected override void OnDestroy()
{
damageVignette?.Destroy();
}
protected override void OnPreRender()
{
if ( !Player.IsLocalPlayer ) return;
if ( Scene.Camera is null ) return;
UpdateVignette();
UpdateRadialIndicators();
}
void UpdateVignette()
{
if ( damageVignette is null )
{
damageVignette = Scene.Camera.AddComponent<Vignette>();
damageVignette.Flags |= ComponentFlags.NotNetworked;
damageVignette.Color = Color.Red;
damageVignette.Intensity = 0;
}
if ( timeSinceHurt < 1f )
{
damageVignette.Intensity = (1f - timeSinceHurt) / 10f;
}
else
{
damageVignette.Intensity = 0;
}
}
Texture texture = Texture.Load( "ui/damage_radial.png" );
void UpdateRadialIndicators()
{
var hud = Scene.Camera.Hud;
var playerPos = Player.EyeTransform.Position;
var playerRot = Player.EyeTransform.Rotation;
var center = Screen.Size / 2f;
// rough approx of where the crosshair is in worldspace, makes close-up directions more easily parsable/accurate
var focalPoint = playerPos + playerRot.Forward * 16;
for ( int i = radialIndicators.Count - 1; i >= 0; i-- )
{
var entry = radialIndicators[i];
if ( entry.Lifetime >= RadialIndicatorLifetime )
{
radialIndicators.RemoveAt( i );
continue;
}
var dir = (entry.WorldPos - focalPoint).Normal;
var angle = -MathF.Atan2( dir.y, dir.x ) + playerRot.Angles().yaw.DegreeToRadian() - (MathF.PI / 2f);
Matrix matrix = Matrix.CreateRotation( Rotation.From( 0, angle.RadianToDegree(), 0 ) );
matrix *= Matrix.CreateTranslation( center );
hud.SetMatrix( matrix );
var size = new Vector2( 256, 512 ) * Hud.Scale;
var rect = new Rect( new Vector2( RadialDistanceFromCenter * Hud.Scale, -size.y / 2 ), size );
// scale alpha based on damage dealt or something?
hud.DrawTexture( texture, rect, Color.Red.WithAlpha( 1f - (entry.Lifetime / RadialIndicatorLifetime) ) );
}
hud.SetMatrix( Matrix.Identity );
}
void IPlayerEvent.OnDamage( IPlayerEvent.DamageParams args )
{
timeSinceHurt = 0f;
if ( args.Attacker.IsValid() )
{
radialIndicators.Add( (args.Attacker.WorldPosition, 0f) );
}
}
}