things/Landmine.cs

A Thing subclass representing a landmine entity. It initializes collision tags, detects collisions with enemies or players after a short spawn delay, and triggers an explosion that applies damage, repel effects, optional player-safe behavior, perk callbacks, and spawns bullets based on shooter stats.

NetworkingFile Access
using System;
using Sandbox;
using Sandbox.Diagnostics;
using Sandbox.UI;

public class Landmine : Thing
{
	[Property] public ModelRenderer ModelRenderer { get; set; }

	public Player Shooter { get; set; }
	public float Damage { get; set; }

	private const float EXPLOSION_RADIUS = 80f;

	protected override void OnStart()
	{
		base.OnStart();

		if ( IsProxy )
			return;

		CollideWithTags.Add( "enemy" );
		CollideWithTags.Add( "player" );
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

	}

	public override void Colliding( Thing other, float percent, float dt )
	{
		base.Colliding( other, percent, dt );

		if ( TimeSinceSpawn < 0.5f )
			return;

		if ( other is Enemy enemy && !enemy.IsInTheAir )
		{
			Explode();
		}
		else if ( other is Player player && !player.IsDead && !player.IsInTheAir )
		{
			Explode();
		}
	}

	public void Explode()
	{
		Assert.True( !IsProxy );

		float radius = EXPLOSION_RADIUS * (Shooter.IsValid() ? Shooter.Stats[PlayerStat.RadiusMultiplier] * Shooter.Stats[PlayerStat.ExplosionSizeMultiplier] : 1f);
		float damage = Damage * (Shooter.IsValid() ? Shooter.Stats[PlayerStat.ExplosionDamageMultiplier] : 1f);

		var dontHurtPlayers = Shooter.IsValid() && Shooter.GetSyncStat( PlayerStat.LandmineRepelPlayerAmount ) > 0f;

		RepelOptions options = RepelOptions.RepelPlayers | RepelOptions.DamagePlayers | RepelOptions.DamageEnemies | RepelOptions.RepelEnemies | RepelOptions.RepelItems;

		if( dontHurtPlayers )
			options &= ~RepelOptions.DamagePlayers;

		var color = dontHurtPlayers ? new Color( 0.7f, 0f, 1f ) : Color.Red;

		var force = damage * 20f;

		Manager.Instance.CreateExplosionRpc( Position2D, radius, damage, repelRadius: radius * 1.15f, repelForce: force, playerSource: Shooter, enemySource: null, enemyType: EnemyType.None, color, options );

		if( dontHurtPlayers )
		{
			var repelPlayerAmount = force * Shooter.GetSyncStat( PlayerStat.LandmineRepelPlayerAmount );
			Manager.Instance.AffectInRadiusHost( Position2D, radius, damage: 0f, repelRadius: radius * 1.15f, repelForce: repelPlayerAmount, playerSource: Shooter, enemySource: null, enemyType: EnemyType.None, DamageType.Aoe, options: RepelOptions.RepelPlayers );
		}

		if ( Shooter.IsValid() )
		{
			var perkType = TypeLibrary.GetType( typeof( PerkLandmine ) );
			if ( Shooter.HasPerk( perkType ) )
			{
				var landminePerk = Shooter.GetPerk( perkType ) as PerkLandmine;
				if ( landminePerk != null )
					landminePerk.LandmineDestroyed();
			}

			if ( Shooter.Stats[PlayerStat.NumLandmineBulletsPerAmmo] > 0f )
			{
				int numBullets = (int)Shooter.Stats[PlayerStat.NumLandmineBulletsPerAmmo] * (int)Shooter.Stats[PlayerStat.MaxAmmoCount];
				var bullets = Shooter.SpawnBulletRing( Position2D, numBullets, Utils.GetRandomVector(), isFromClip: false, spawnOffset: 10f );
				foreach ( var bullet in bullets )
				{
					bullet.SetupArc( arcHeight: Game.Random.Float( 150f, 200f ), Shooter.Stats[PlayerStat.ArcBulletBounces] );
					bullet.Velocity *= Game.Random.Float( 0.3f, 0.55f );
					bullet.Stats[BulletStat.Lifetime] *= Game.Random.Float( 1f, 1.4f );
				}
			}
		}

		GameObject.Destroy();
	}
}