perks/PerkLandmine.cs

A Perk implementation that gives the player landmines on dash. It spawns a landmine prefab at the player's position when a dash starts, tracks active mines, enforces a max count by exploding the oldest, updates HUD display and modifies dash cooldown stat.

NetworkingFile Access
using System;
using Sandbox;

[Perk( Rarity.Rare, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.Aoe, PerkCategory.Explosion, PerkCategory.Landmine })]
public class PerkLandmine : Perk
{
	private enum Mod { ExplosionDamage, DashCooldown, NumMaxMines, };

	public int CurrNumLandmines { get; private set; }


	private List<Landmine> _landmines = new();

	static PerkLandmine()
	{
		Register<PerkLandmine>(
			name: "Landmines",
			imagePath: "textures/icons/vector/landmine.png",
			description: level => $"Drop a {GetValue( level, Mod.ExplosionDamage )} dmg landmine on dash (max: {GetValue( level, Mod.NumMaxMines )})\n-{GetValue( level, Mod.DashCooldown, true )}% dash recharge speed",
			upgradeDescription: level => $"Drop a {GetValue( level - 1, Mod.ExplosionDamage )}→{GetValue( level, Mod.ExplosionDamage )} dmg landmine on dash\n(max: {GetValue( level - 1, Mod.NumMaxMines )}→{GetValue( level, Mod.NumMaxMines )})\n-{GetValue( level - 1, Mod.DashCooldown, true )}%→-{GetValue( level, Mod.DashCooldown, true )}% dash recharge speed"
		);
	}

	// todo: do we even need a max count on this? just limit it to 50 or so and explode the oldest one if you place more

	public override void Start()
	{
		base.Start();

		DisplayCooldownColor = new Color( 1f, 0.3f, 0.3f, 3f );

		HighlightColor = new Color( 1f, 1f, 1f );
		HighlightDuration = 0.1f;
		HighlightOpacity = 0.25f;
	}

	public override void Refresh()
	{
		base.Refresh();

		Player.Modify( this, PlayerStat.DashCooldown, GetValue( Level, Mod.DashCooldown ), ModifierType.Mult );

		DisplayText = $"{CurrNumLandmines}/{(int)GetValue( Level, Mod.NumMaxMines )}";
	}

	public override void OnDashStartedEarly( Vector2 dir )
	{
		base.OnDashStartedEarly( dir );

		if ( CurrNumLandmines >= (int)GetValue( Level, Mod.NumMaxMines ) )
		{
			if( _landmines.Count > 0 )
			{
				if( _landmines[0].IsValid() )
					_landmines[0].Explode();

				_landmines.RemoveAt( 0 );
			}
		}

		var pos = Player.Position2D;
		var damage = GetValue( Level, Mod.ExplosionDamage );

		var landmineGo = GameObject.Clone( "prefabs/landmine.prefab", new CloneConfig { StartEnabled = true, Transform = new Transform( new Vector3( pos.x, pos.y, 0f ) ) } );
		var landmine = landmineGo.Components.Get<Landmine>( true );

		landmine.Damage = damage;
		landmine.Shooter = Player;

		landmineGo.NetworkSpawn( Player.Network.Owner );

		CurrNumLandmines++;
		//PlaySfx("burn", mine.Position2D, pitch: Game.Random.Float(0.95f, 1.05f));

		_landmines.Add( landmine );

		DisplayCooldown = Utils.Map( CurrNumLandmines, 0, (int)GetValue( Level, Mod.NumMaxMines ), 0f, 1f );
		Highlight();

		DisplayText = $"{CurrNumLandmines}/{(int)GetValue( Level, Mod.NumMaxMines )}";
	}

	public void LandmineDestroyed()
	{
		CurrNumLandmines = Math.Max( CurrNumLandmines - 1, 0 );
		DisplayCooldown = Utils.Map( CurrNumLandmines, 0, (int)GetValue( Level, Mod.NumMaxMines ), 0f, 1f );
		Highlight();

		DisplayText = $"{CurrNumLandmines}/{(int)GetValue( Level, Mod.NumMaxMines )}";
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.ExplosionDamage:
			default:
				return 5f + 5f * level;
			case Mod.NumMaxMines:
				return 5 * level;
			case Mod.DashCooldown:
				return isPercent
					? 8f + 2f * level
					: 1f + (0.08f + 0.02f * level);
		}
	}
}