swb_base/bullets/PhysicalBullet.Mover.cs
using SWB.Shared;
using System;
using System.Linq;
namespace SWB.Base;
[Group( "SWB" )]
[Title( "Physical Bullet Mover" )]
public class PhysicalBulletMover : Component
{
public IPlayerBase Owner { get; set; }
public string ClassName { get; set; }
public Vector3 BulletVelocity { get; set; }
public Weapon Weapon { get; set; }
public ShootInfo ShootInfo { get; set; }
public PhysicalBulletInfo BulletInfo { get; set; }
public float BulletGravity => BulletInfo.Gravity;
public float BulletDrag => BulletInfo.Drag;
public bool HasImpacted { get; private set; } = false;
public void Initialize( PhysicalBulletInfo bulletInfo, Weapon weapon, ShootInfo shootInfo, Vector3 bulletVelocity )
{
BulletInfo = bulletInfo;
Owner = weapon.Owner;
ClassName = weapon.ClassName;
Weapon = weapon;
ShootInfo = shootInfo;
BulletVelocity = bulletVelocity;
// If a bullet is flying this long it probably got bugged
GetOrAddComponent<TemporaryEffect>().DestroyAfterSeconds = 20f;
}
protected override void OnFixedUpdate()
{
if ( IsProxy || HasImpacted || Owner is null ) return;
// Apply drag (before gravity so we aren't immediately dragging on gravity)
BulletVelocity *= 1 - BulletDrag;
// Apply gravity
BulletVelocity += Vector3.Down * BulletGravity * Time.Delta;
var bulletMovement = BulletVelocity * Time.Delta;
// Trace along path to see if we hit anything
var bulletTrace = Weapon.TraceBullet( Owner.GameObject, WorldPosition, WorldPosition + bulletMovement );
if ( bulletTrace.Hit )
{
HandleImpact( bulletTrace );
WorldPosition = bulletTrace.HitPosition;
// Allows for graceful ending of effects
HasImpacted = true;
GetOrAddComponent<TemporaryEffect>().DestroyAfterSeconds = 0.5f;
ITemporaryEffect.DisableLoopingEffects( GameObject );
return;
}
// If we didn't hit anything, we can move the bullet
WorldPosition += bulletMovement;
}
protected void HandleImpact( SceneTraceResult traceResult )
{
var hitObject = traceResult.GameObject;
if ( SurfaceUtil.IsSkybox( traceResult.Surface ) || traceResult.HitPosition == Vector3.Zero ) return;
// Impact
var decal = Weapon.CreateBulletImpact( traceResult );
decal?.NetworkSpawn();
// Damage
if ( hitObject is not null )
{
var target = hitObject.Components.GetInAncestorsOrSelf<IDamageable>();
var hitTags = Array.Empty<string>();
if ( traceResult.Hitbox is not null )
hitTags = traceResult.Hitbox.Tags.TryGetAll().ToArray();
var forward = BulletVelocity.Normal;
// Assume force from shoot info is the force at firing
var force = BulletVelocity.Length.Remap( 0, BulletInfo.Velocity, 0, ShootInfo.Force );
var dmgInfo = Shared.DamageInfo.FromBullet(
Owner.GameObject,
Weapon.GameObject,
traceResult.Hitbox,
traceResult.EndPosition,
traceResult.Shape,
ClassName,
ShootInfo.Damage,
traceResult.HitPosition,
forward * 100 * force,
ShootInfo.HitFlinch,
Weapon.GetMovementImpactFromForce( ShootInfo.Force ),
hitTags );
target?.OnDamage( dmgInfo );
}
}
}