An Obstacle entity derived from Thing. It sets physics properties, ignores proxy instances, handles collisions with EnemyProjectile by playing effects, deflecting or removing projectiles based on difficulty, and plays material-specific hit sound via an RPC.
using System;
using System.Net.WebSockets;
using Sandbox;
public enum ObstacleMaterial { Stone, Wood, }
public class Obstacle : Thing
{
[Property] public ObstacleMaterial Material { get; set; }
private List<Thing> _hitThings;
protected override void OnStart()
{
base.OnStart();
PushStrength = 20000f;
Weight = 8f;
if ( IsProxy )
return;
CollideWithTags.Add( "enemy_projectile" );
_hitThings = new();
}
protected override void OnUpdate()
{
base.OnUpdate();
if ( IsProxy )
return;
for(int i = _hitThings.Count - 1; i >= 0; i-- )
{
var thing = _hitThings[i];
if( !thing.IsValid() )
_hitThings.RemoveAt( i );
}
}
public override void Colliding( Thing other, float percent, float dt )
{
base.Colliding( other, percent, dt );
if( _hitThings.Contains( other ) )
return;
if ( other is EnemyProjectile projectile )
{
// todo: better sfx
Manager.Instance.PlaySfxNearbyRpc( "bullet.impact", projectile.Position2D, pitch: Game.Random.Float( 0.9f, 1f ), volume: 1.15f, maxDist: 300f );
var normal = (projectile.Position2D - Position2D).Normal;
Manager.Instance.SpawnBulletImpactParticlesRpc( projectile.WorldPosition, (Vector3)normal, Color.White );
//projectile.RemoveRpc( shouldSpawnEffects: false );
if( Manager.Instance.Difficulty == 0 )
{
projectile.RemoveRpc( shouldSpawnEffects: true );
}
else
{
projectile.Position2D = Position2D + normal * (Radius + projectile.Radius + 5f);
projectile.SetDirection( normal );
PlayHitSfxRpc( projectile.WorldPosition );
_hitThings.Add( other );
}
}
}
[Rpc.Broadcast]
public void PlayHitSfxRpc( Vector3 pos )
{
var sfxName = Material switch
{
ObstacleMaterial.Stone => "hit_concrete",
ObstacleMaterial.Wood => "hit_wood_1",
_ => "hit_concrete"
};
Manager.Instance.PlaySfxNearby( sfxName, pos, pitch: 1f, volume: 1f, maxDist: 400f );
}
}