Terrain/TerrainScenery.cs
using HC3.Persistence;
using System;
namespace HC3.Terrain;
#nullable enable
public class SceneryGenerationOptions
{
[Range( 0f, 1f )]
public float ClusterDensity { get; set; } = 0f;
[Range( 0f, 1f )]
public float ClusterScale { get; set; }
public RangedFloat AltitudeRange { get; set; } = new( 10f, 20f );
public Curve AltitudeCurve { get; set; } = new Curve( new Curve.Frame( 0f, 1f ), new Curve.Frame( 1f, 1f ) );
[Property]
public int MaxGradient { get; set; }
}
public sealed class TerrainScenery : Component, ISceneMetadata, IPrefabSourceComponent<Vector4>
{
[RequireComponent] public GridObject? GridObject { get; private set; }
[Property] public Vector2Int Size { get; set; } = 1;
[Property] public int DestructionCost { get; set; } = 2_500;
[Property]
public SceneryGenerationOptions GenerationOptions { get; set; } = new();
/// <summary>
/// Hack so we don't forget which prefab this came from when it spawns.
/// </summary>
public string? PrefabSource { get; private set; }
Dictionary<string, string> ISceneMetadata.GetMetadata()
{
return new() {
{ "Type", "Scenery" },
{ nameof(GenerationOptions), Json.Serialize( GenerationOptions ) }
};
}
protected override void OnValidate()
{
GridObject ??= GetOrAddComponent<GridObject>();
GridObject.LocalBounds = new RectInt( -Size / 2, Size );
GridObject.GridFlags = GridObjectFlags.BlocksConstruction;
}
protected override void OnAwake()
{
if ( GameObject.IsPrefabInstanceRoot )
{
PrefabSource = GameObject.PrefabInstanceSource;
}
}
protected override void DrawGizmos()
{
if ( !Gizmo.IsSelected ) return;
var min = -Size / 2f;
var max = min + Size;
Gizmo.Draw.Color = Color.Red;
Gizmo.Draw.LineBBox( new BBox( min * GridManager.GridSize, max * GridManager.GridSize ) );
Gizmo.Draw.Color = Color.Red.WithAlpha( 0.25f );
Gizmo.Draw.SolidBox( new BBox( min * GridManager.GridSize, max * GridManager.GridSize ) );
}
public static IEnumerable<(GameObject Prefab, SceneryGenerationOptions Options)> AllPrefabs
{
get
{
return ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Type", "" ).Equals( "Scenery" ) )
.ToList()
.Select( x => (GameObject.GetPrefab( x.ResourcePath ),
Json.Deserialize<SceneryGenerationOptions>( x.GetMetadata( nameof( GenerationOptions ), "{}" ) )) )
.Where( x => x.Item1 is not null );
}
}
Vector4 IPrefabSourceComponent<Vector4>.GetSaveData() =>
new( GridManager.Instance.Terrain.WorldToGrid( WorldPosition ), WorldRotation.Yaw() );
void IPrefabSourceComponent<Vector4>.Spawn( Vector4 saveData )
{
var gridManager = GridManager.Instance;
WorldPosition = gridManager.Terrain.GridToWorld( saveData );
WorldRotation = Rotation.FromYaw( saveData.w );
if ( !Scene.IsEditor )
{
GameObject.NetworkSpawn();
}
else
{
GameObject.Flags |= GameObjectFlags.NotSaved;
}
}
}
public sealed class RandomizeTransform : Component, Component.ExecuteInEditor
{
[Property]
public Curve Scale { get; set; } = 1f;
protected override void OnEnabled()
{
GameObject.WorldRotation = Rotation.FromYaw( Random.Shared.NextSingle() * 360f );
GameObject.WorldScale = Scale.Evaluate( Random.Shared.NextSingle() );
}
}
file sealed class ScenerySaveData : SpawnedPrefabSaveData<TerrainScenery, Vector4>
{
public override string PropertyName => "Scenery";
}