Terrain/Terraforming/RaiseLowerMode.cs
using HC3.Terrain;
using System;
namespace HC3.Terraforming;
#nullable enable
public sealed class RaiseLowerMode : TerraformMode
{
public override string Title => "Raise / Lower";
public override string Description => "Increase or decrease the height of a square.";
public override string Icon => "height";
public const int MinSize = 0;
public const int MaxSize = 8;
private int _size = 1;
private TileEdge? _selectedEdge;
private TileCorner? _selectedCorner;
[Property, Range( min: MinSize, max: MaxSize ), Step( 1f )]
public int Size
{
get => _size;
set
{
value = Math.Clamp( value, MinSize, MaxSize );
if ( _size == value && Brush is not null ) return;
_size = value;
_selectedEdge = null;
_selectedCorner = null;
Brush = new BoxBrush( Math.Max( 1, Size ) );
}
}
protected override void OnActivate()
{
Size = 1;
}
protected override void OnUpdate()
{
if ( Input.Down( "Run" ) )
{
Size += Math.Sign( Input.MouseWheel.y );
}
if ( Size == 0 )
{
var gridIndex = new Vector2Int( (int)MathF.Floor( CursorGridPos.x ), (int)MathF.Floor( CursorGridPos.y ) );
var localPos = CursorGridPos - gridIndex;
var (edge, corner) = GetSelectedFeature( localPos );
if ( (edge, corner) != (_selectedEdge, _selectedCorner) )
{
(_selectedEdge, _selectedCorner) = (edge, corner);
Brush = edge.HasValue ? new EdgeBrush( edge.Value )
: corner.HasValue ? new CornerBrush( corner.Value )
: new BoxBrush( 1 );
}
}
}
private (TileEdge? Edge, TileCorner? Corner) GetSelectedFeature( Vector2 localPos )
{
var featurePos = new Vector2Int(
(int)MathF.Floor( localPos.x * 3 - 1f ),
(int)MathF.Floor( localPos.y * 3 - 1f ) );
if ( featurePos == 0 )
{
return (null, null);
}
if ( featurePos.x == 0 )
{
return (featurePos.y < 0 ? TileEdge.YMin : TileEdge.YMax, null);
}
if ( featurePos.y == 0 )
{
return (featurePos.x < 0 ? TileEdge.XMin : TileEdge.XMax, null);
}
return featurePos.x < 0
? (null, featurePos.y < 0 ? TileCorner.XMinYMin : TileCorner.XMinYMax)
: (null, featurePos.y < 0 ? TileCorner.XMaxYMin : TileCorner.XMaxYMax);
}
protected override void OnApply( TileArraySlice original, TileArraySlice modified, TerraformContext context )
{
var (min, max) = original.GetHeightRange();
var delta = context.Delta;
var tileset = context.Terrain.Tileset;
Span<int> corners = stackalloc int[4];
var materialIndex = Terraformer.Instance?.MaterialIndex;
if ( Size == 0 )
{
var tile = original[0];
var paint = tile.Paint;
tile.GetCornerHeights( corners );
if ( _selectedCorner is { } corner )
{
corners[(int)corner] += delta;
if ( materialIndex is { } matIndex )
{
paint = paint.WithMaterialIndex( corner, matIndex );
}
}
else if ( _selectedEdge is { } edge )
{
var (c0, c1) = edge.GetCorners();
corners[(int)c0] += delta;
corners[(int)c1] += delta;
if ( materialIndex is { } matIndex )
{
paint = paint.WithMaterialIndex( c0, matIndex );
paint = paint.WithMaterialIndex( c1, matIndex );
}
}
else
{
for ( var i = 0; i < 4; ++i )
{
corners[i] += delta;
}
if ( materialIndex is { } matIndex )
{
paint = TilePaint.FromMaterialIndex( matIndex );
}
}
modified[0] = tileset.FromCornerHeights( corners, paint );
}
else
{
TilePaint? paint = null;
if ( materialIndex is { } matIndex )
{
paint = TilePaint.FromMaterialIndex( matIndex );
}
foreach ( var (index, tile) in original )
{
tile.GetCornerHeights( corners );
for ( var i = 0; i < 4; ++i )
{
corners[i] = delta > 0
? Math.Max( corners[i], min + delta )
: Math.Min( corners[i], max + delta );
}
modified[index] = tileset.FromCornerHeights( corners, paint ?? tile.Paint );
}
}
}
}
public sealed class CornerBrush : ITerraformBrush
{
public Vector2Int Size => 1;
public TileCorner Corner { get; }
public CornerBrush( TileCorner corner )
{
Corner = corner;
}
public float GetWeight( Vector2Int tileIndex ) => Corner.GetHorizontalOffset() == tileIndex
? 1f
: 0.25f;
}
public sealed class EdgeBrush : ITerraformBrush
{
public Vector2Int Size => 1;
public TileEdge Edge { get; }
public EdgeBrush( TileEdge edge )
{
Edge = edge;
}
public float GetWeight( Vector2Int tileIndex )
{
var (min, max) = Edge.GetCorners();
return min.GetHorizontalOffset() == tileIndex || max.GetHorizontalOffset() == tileIndex
? 1f
: 0.25f;
}
}