Component that gives an entity dash and parry abilities. It handles input for dash and parry, cooldowns and timing windows, plays sounds and spawns particle GameObjects, and exposes bot-callable methods to trigger dash/parry.
using Sandbox;
using System;
public sealed class ManeuverAbility : Component
{
[Property] public float DashForce { get; set; } = 900f;
[Property] public float DashCooldown { get; set; } = 1.2f;
[Property] public float ParryWindow { get; set; } = 0.4f;
[Property] public float ParryCooldown { get; set; } = 2.5f;
[Property] public SoundEvent DashSound { get; set; }
[Property] public SoundEvent ParrySound { get; set; }
[Property] public SoundEvent ReflectSound { get; set; }
[Property] public GameObject DashParticle { get; set; }
[Property] public GameObject ParryParticle { get; set; }
private TimeSince _lastDash;
private TimeSince _lastParry;
private TimeSince _parryTimer;
public bool IsParrying => _parryTimer < ParryWindow;
public bool IsDashReady => _lastDash >= DashCooldown;
public bool IsParryReady => _lastParry >= ParryCooldown;
protected override void OnUpdate()
{
if ( IsProxy ) return;
if ( Input.Pressed( "run" ) && IsDashReady )
{
ExecuteDash();
}
if ( Input.Pressed( "attack2" ) && IsParryReady )
{
ExecuteParry();
}
}
private void ExecuteDash()
{
var cc = Components.Get<CharacterController>();
if ( !cc.IsValid() ) return;
var moveInput = Input.AnalogMove;
var wishDir = (WorldTransform.Forward * moveInput.x + WorldTransform.Left * moveInput.y).Normal;
if ( wishDir.IsNearlyZero() )
{
wishDir = WorldTransform.Forward;
}
PerformDash( wishDir );
}
private void ExecuteParry()
{
PerformParry();
}
// --- МЕТОДЫ ДЛЯ БОТОВ И ВНЕШНИХ ВЫЗОВОВ ---
public void TryExecuteBotDash( Vector3 direction )
{
if ( !IsDashReady ) return;
PerformDash( direction );
}
public void TryExecuteBotParry()
{
if ( !IsParryReady ) return;
PerformParry();
}
private void PerformDash( Vector3 direction )
{
_lastDash = 0;
var cc = Components.Get<CharacterController>();
if ( cc.IsValid() )
{
cc.Velocity += direction.Normal * DashForce;
if ( DashSound != null ) Sound.Play( DashSound, WorldPosition );
if ( DashParticle.IsValid() ) DashParticle.Clone( WorldPosition );
}
}
private void PerformParry()
{
_lastParry = 0;
_parryTimer = 0;
if ( ParrySound != null ) Sound.Play( ParrySound, WorldPosition );
if ( ParryParticle.IsValid() ) ParryParticle.Clone( WorldPosition );
}
public void OnSuccessfulReflect( Vector3 reflectOrigin )
{
if ( ReflectSound != null )
{
Sound.Play( ReflectSound, WorldPosition );
}
}
}