Utility/EnvironmentShake.cs
[Alias( "EnvShake" )]
public sealed class EnvironmentShake : Component, Component.ITemporaryEffect, ICameraSetup
{
[Property] public float Rate { get; set; } = 10.0f;
[Property] public Angles Scale { get; set; } = new Angles( 100, 100, 0 );
[Property] public float MaxDistance { get; set; } = 1024;
[Property] public Curve DistanceCurve { get; set; } = new Curve( new( 0, 1 ), new( 1, 0 ) );
[FeatureEnabled( "Timed" ), Property] public bool TimeLimited { get; set; } = false;
[Feature( "Timed" ), Property] public float TimeLength { get; set; } = 5;
[Feature( "Timed" ), Property] public Curve TimeCurve { get; set; } = new Curve( new( 0, 1 ), new( 1, 0 ) );
[Sync] public TimeUntil EndTime { get; set; }
public bool IsActive => !TimeLimited || EndTime > 0;
protected override void OnEnabled()
{
EndTime = TimeLength;
}
void ICameraSetup.PostSetup( CameraComponent camera )
{
var distance = camera.WorldPosition.Distance( WorldPosition );
var distanceDelta = (distance / MaxDistance).Clamp( 0, 1 );
var amount = DistanceCurve.EvaluateDelta( distanceDelta ) * GamePreferences.Screenshake;
var noisex = Sandbox.Utility.Noise.Perlin( Time.Now * Rate, 0 ).Remap( 0, 1, -1, 1 );
var noisey = Sandbox.Utility.Noise.Perlin( Time.Now * Rate + 830, 0 ).Remap( 0, 1, -1, 1 );
var noisez = Sandbox.Utility.Noise.Perlin( Time.Now * Rate + 340, 0 ).Remap( 0, 1, -1, 1 );
if ( TimeLimited )
{
var timeDelta = (EndTime.Relative / TimeLength).Remap( 1, 0 );
if ( timeDelta >= 1 )
return;
amount *= TimeCurve.Evaluate( timeDelta );
}
camera.LocalRotation *= Rotation.From( noisex * Scale.pitch, noisey * Scale.yaw, noisez * Scale.roll ) * amount;
}
}