things/Obstacle.cs

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.

Networking
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 );
	}
}