PushAbility.cs
using Sandbox;
using System;
using System.Threading.Tasks;
public sealed class PushAbility : Component
{
[Property] public float PushForce { get; set; } = 20000f;
[Property] public float PushRadius { get; set; } = 300f;
[Property] public float Cooldown { get; set; } = 0.5f;
// Эффекты
[Property] public GameObject PushParticle { get; set; }
[Property] public SoundEvent PushSound { get; set; }
private TimeSince _lastPush;
public float CooldownProgress => MathF.Min( _lastPush / Cooldown, 1f );
public bool IsReady => _lastPush >= Cooldown;
protected override void OnUpdate()
{
if ( Input.Pressed( "attack1" ) && _lastPush > Cooldown )
{
_lastPush = 0;
ExecutePush();
}
}
private void ExecutePush()
{
Log.Info( "Толчок сработал!" );
if ( PushParticle.IsValid() )
{
var p = PushParticle.Clone( WorldPosition );
_ = DestroyAfterDelay( p, 2000 );
}
if ( PushSound != null )
{
Sound.Play( PushSound, WorldPosition );
}
var startPos = WorldPosition;
var endPos = WorldPosition + Vector3.Up * 1f;
var hits = Scene.Trace.Sphere( PushRadius, startPos, endPos )
.IgnoreGameObject( GameObject )
.RunAll();
// Получаем множитель силы от выбранного в магазине предмета (по умолчанию 1.0)
float mult = ShopManager.Instance?.GetCurrentForceMultiplier() ?? 1.0f;
float currentPushForce = PushForce * mult;
foreach ( var hit in hits )
{
if ( !hit.GameObject.IsValid() ) continue;
var rb = hit.GameObject.Components.Get<Rigidbody>();
if ( rb.IsValid() )
{
var direction = (hit.GameObject.WorldPosition - WorldPosition).Normal;
direction.z = 0.4f;
// Применяем итоговую силу с учетом множителя
rb.ApplyImpulse( direction * currentPushForce );
Log.Info( $"Толкнули объект: {hit.GameObject.Name} с силой {currentPushForce}" );
}
}
}
private async Task DestroyAfterDelay( GameObject obj, int delayMs )
{
await Task.Delay( delayMs );
if ( obj.IsValid() )
{
obj.Destroy();
}
}
}