test/Enemy.cs
using Sandbox.Services;
public sealed class Enemy : Component, Component.ICollisionListener, Component.IDamageable
{
[RequireComponent] public PlayerController Controller { get; set; }
[Property] GameObject BloodPrefab { get; set; }
[Property] public float Health { get; set; } = 5f;
[Property] public float Speed { get; set; } = 150f;
[Property] public float Damage { get; set; } = 10f;
TimeSince timeSinceLastCheck = 0f;
List<Vector3> currentPath = new();
protected override void OnAwake()
{
timeSinceLastCheck = 0f;
}
protected override void OnFixedUpdate()
{
var player = Player.FindLocalPlayer();
if ( timeSinceLastCheck > 1.5f )
{
timeSinceLastCheck = 0f;
if ( player.IsValid() )
{
var path = Scene.NavMesh.CalculatePath( new Sandbox.Navigation.CalculatePathRequest()
{
Start = WorldPosition,
Target = player.WorldPosition
} );
//Log.Info( $"{GameObject.Name} got path with {path.Points.Count} points" );
if ( path.IsValid && path.Points.Count > 0 )
{
currentPath.Clear();
for ( int i = 0; i < path.Points.Count; i++ )
{
currentPath.Add( path.Points[i].Position );
}
}
}
}
if ( currentPath.Count > 0 )
{
var target = currentPath[0];
var dir = (target - WorldPosition).Normal.WithZ( 0 );
Controller.WishVelocity = dir * Speed;
Controller.EyeAngles = Rotation.LookAt( dir );
if ( WorldPosition.Distance( target ) < 32f )
{
currentPath.RemoveAt( 0 );
}
}
else if ( player.IsValid() )
{
var dir = (player.WorldPosition - WorldPosition).Normal.WithZ( 0 );
Controller.WishVelocity = dir * Speed;
Controller.EyeAngles = Rotation.LookAt( dir );
}
else
{
Controller.WishVelocity = 0f;
}
}
void IDamageable.OnDamage( in DamageInfo damage )
{
Health -= damage.Damage;
if ( BloodPrefab.IsValid() )
{
var bloodAm = Random.Shared.Int( 6, 15 ) / ((Health <= 0f) ? 1 : 5);
for ( int i = 0; i < bloodAm; i++ )
{
var blood = BloodPrefab.Clone( new CloneConfig()
{
StartEnabled = true,
Transform = new Transform()
{
Position = WorldPosition + Vector3.Up * (35f + Random.Shared.Float( 0f, 32f )),
Rotation = WorldRotation
}
} );
}
}
if ( Health <= 0f )
{
// Spawn sonar
using ( Scene.Push() )
{
var go = new GameObject( "Sonar" );
go.WorldPosition = WorldPosition;
var volume = go.GetOrAddComponent<GaussianSplatVolume>();
volume.Mode = SplatVolumeMode.Add;
volume.Shape = SplatVolumeShape.Sphere;
volume.Radius = 0f;
var sonar = go.GetOrAddComponent<SonarComponent>();
sonar.Volume = volume;
sonar.TargetSize = 256f;
sonar.Intensity = 0.05f;
}
// Award paint
var player = Player.FindLocalPlayer();
if ( player.IsValid() && player.Paint.IsValid() )
{
player.Paint.Paint += 5f;
}
// Award kill
if ( GameManager.Instance.IsValid() )
{
GameManager.Instance.Kills++;
Stats.Increment( "enemy_kills", 1 );
}
GameObject.Destroy();
}
}
void ICollisionListener.OnCollisionStart( Collision collision )
{
//Log.Info( collision.Other.GameObject.Root );
if ( collision.Other.GameObject.Root.Components.TryGet<Player>( out var player ) )
{
var info = new DamageInfo( Damage, GameObject, GameObject );
player.OnDamage( info );
}
}
}