Terrain/Terraforming/TerraformMode.cs
using System;
using HC3.Terrain;
using Sandbox.Utility;

namespace HC3.Terraforming;

#nullable enable

public abstract class TerraformMode
{
	private bool _isActive;

	public virtual string Title => GetType().Name.ToTitleCase();
	public virtual string Icon => "height";
	public virtual string Description => "No description.";
	public virtual int Order => 0;

	public ITerraformBrush? Brush { get; protected set; }
	public int Margin { get; protected set; }

	protected Vector2 CursorGridPos { get; private set; }

	public bool IsActive
	{
		get => _isActive;
		set
		{
			if ( _isActive == value ) return;

			if ( _isActive ) OnDeactivate();

			_isActive = value;

			if ( _isActive ) OnActivate();
		}
	}

	public static IEnumerable<TerraformMode> GetAll()
	{
		return TypeLibrary.GetTypes<TerraformMode>()
			.Where( x => !x.IsAbstract )
			.Select( x =>
			{
				try
				{
					return x.Create<TerraformMode>();
				}
				catch
				{
					return null;
				}
			} )
			.OfType<TerraformMode>()
			.OrderBy( x => x.Order );
	}

	protected virtual void OnActivate()
	{

	}

	protected virtual void OnDeactivate()
	{

	}

	public void Update( Vector2 cursorGridPos )
	{
		CursorGridPos = cursorGridPos;

		OnUpdate();
	}

	protected virtual void OnUpdate()
	{

	}

	public void Apply( TileArraySlice original, TileArraySlice modified, TerraformContext context ) => OnApply( original, modified, context );

	protected abstract void OnApply( TileArraySlice original, TileArraySlice modified, TerraformContext context );
}

public sealed record TerraformContext(
	ParkTerrain Terrain,
	RectInt TileRange,
	Vector3 CursorGridPosition,
	int Delta );

public interface ITerraformBrush
{
	Vector2Int Size { get; }
	float GetWeight( Vector2Int tileIndex );
}

public sealed class BoxBrush : ITerraformBrush
{
	public Vector2Int Size { get; }

	public BoxBrush( Vector2Int size )
	{
		Size = size;
	}

	public float GetWeight( Vector2Int tileIndex ) => 1f;
}

public sealed class RadialBrush : ITerraformBrush
{
	public float Radius { get; }
	public Vector2Int Size { get; }

	public RadialBrush( float radius )
	{
		Radius = radius;
		Size = (int)MathF.Ceiling( Radius * 2 );
	}

	public float GetWeight( Vector2Int tileIndex ) =>
		1f - Easing.EaseIn( Math.Clamp( (tileIndex - Size / 2).Length / Radius, 0f, 1f ) );
}