Weapons/HornetGun/HornetProjectile.cs
/// <summary>
/// An Hornet projectile. Will follow a target.
/// </summary>
public partial class HornetProjectile : Projectile, Component.ITriggerListener, Component.IDamageable
{
// Not sure why this exists?
public enum FireMode
{
Normal,
Turbo
}
[Property] public SoundEvent LoopingSound { get; set; }
[Property] public SoundEvent DieSound { get; set; }
[Property] public SoundEvent MunchSound { get; set; }
[Property] public float Speed { get; set; } = 512;
[Property] public float SpeedTurbo { get; set; } = 1024;
[RequireComponent] public Explosive Explosive { get; set; }
public GameObject Weapon { get; set; }
public FireMode Mode { get; set; }
Player TargetPlayer { get; set; }
TimeSince timeSinceThink;
SoundHandle LoopingSoundHandle;
protected override void OnEnabled()
{
base.OnEnabled();
if ( LoopingSound.IsValid() )
LoopingSoundHandle = Sound.Play( LoopingSound, WorldPosition );
if ( IsProxy )
return;
Rigidbody.Gravity = false;
timeSinceThink = 0;
}
protected override void OnDisabled()
{
LoopingSoundHandle?.Stop();
}
protected override void OnUpdate()
{
if ( LoopingSoundHandle is not null )
LoopingSoundHandle.Position = WorldPosition;
}
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
if ( !Networking.IsHost ) return;
if ( Mode == FireMode.Turbo ) return;
if ( TargetPlayer.IsValid() )
{
UpdateWithTarget( TargetPlayer.EyeTransform.Position, 256f );
return;
}
if ( timeSinceThink > 0.3f )
{
float yaw = Random.Shared.Float( -15f, 15f );
float pitch = Random.Shared.Float( -2f, 2f );
WorldRotation *= Rotation.From( pitch, yaw, 0 );
Rigidbody.Velocity = WorldRotation.Forward * Rigidbody.Velocity.Length;
timeSinceThink = 0;
}
}
protected override void OnHit( Collision collision = default )
{
var player = collision.Other.GameObject.GetComponentInParent<Player>();
if ( player.IsValid() )
{
if ( Instigator.IsValid() && player.PlayerData == Instigator )
return;
NotifyHit( player, collision.Contact.Point );
var dmg = new DamageInfo( 10, Instigator?.Player?.GameObject, GameObject );
var snd = GameObject.PlaySound( MunchSound );
snd.Pitch = Random.Shared.Float( 0.8f, 1.2f );
player.OnDamage( dmg );
GameObject.Destroy();
return;
}
if ( Mode == FireMode.Turbo )
{
GameObject.PlaySound( DieSound );
GameObject.Destroy();
return;
}
var reflect = Vector3.Reflect( -collision.Contact.Speed, collision.Contact.Normal ).Normal;
UpdateDirection( reflect, GetSpeed() );
}
/// <summary>
/// This is meant to be called continuously, updates the target, rotates slowly to it and moves at a set speed.
/// </summary>
/// <param name="target"></param>
/// <param name="speed"></param>
internal void UpdateWithTarget( Vector3 target, float speed )
{
var direction = (target - WorldPosition).Normal;
var targetRotation = Rotation.LookAt( direction, Vector3.Up );
float alignDelta = Vector3.Dot( WorldTransform.Forward, direction );
if ( alignDelta <= 0 )
{
alignDelta = 0.25f;
}
Rigidbody.Velocity = (WorldTransform.Forward + direction).Normal * speed * alignDelta;
WorldRotation = Rotation.LookAt( Rigidbody.Velocity, Vector3.Up );
}
public float GetSpeed() => Mode switch
{
FireMode.Turbo => SpeedTurbo,
_ => Speed,
};
/// <summary>
/// Called when a gameobject enters the trigger.
/// </summary>
void ITriggerListener.OnTriggerEnter( GameObject other )
{
if ( !Networking.IsHost ) return;
if ( GameObject.IsDestroyed ) return;
var player = other.GetComponent<Player>();
if ( !player.IsValid() )
return;
if ( Instigator.IsValid() && player.PlayerData == Instigator ) return;
TargetPlayer = player;
// Reset the explosive timer
Explosive.TimeSinceActive = 0;
}
public void OnDamage( in DamageInfo damage )
{
GameObject.PlaySound( DieSound );
GameObject.Destroy();
}
}