TerrainSculptNet.cs
// Copyright (c) 2026 SubZero Studios LLC. All rights reserved.

using System;
using Sandbox;

namespace SubZero.SubTerrain;

/// <summary>
/// Optional host-authoritative play-sculpt net. Attach to a NetworkMode.Object
/// GameObject. Host writes height; peers apply the same op locally.
/// </summary>
[Title( "Terrain Sculpt Net" )]
[Category( "SubTerrain" )]
[Icon( "share" )]
public sealed class TerrainSculptNet : Component
{
	public bool Submit( TerrainEditRequest request )
	{
		if ( request.Seed == 0 )
			request.Seed = Game.Random.Int( 1, 1_000_000_000 );

		if ( !Networking.IsActive || Networking.IsHost )
		{
			ApplyLocal( request );
			if ( Networking.IsActive )
				BroadcastStamp( (int)request.Kind, request.WorldPos, request.Radius, request.Depth, request.Seed, request.HasTargetZ, request.TargetZ );
			return true;
		}

		RequestHost( (int)request.Kind, request.WorldPos, request.Radius, request.Depth, request.Seed, request.HasTargetZ, request.TargetZ );
		return true;
	}

	[Rpc.Host]
	public void RequestHost( int kind, Vector3 pos, float radius, float depth, int seed, bool hasTargetZ, float targetZ )
	{
		var request = Make( kind, pos, radius, depth, seed, hasTargetZ, targetZ );
		ApplyLocal( request );
		BroadcastStamp( kind, pos, radius, depth, seed, hasTargetZ, targetZ );
	}

	[Rpc.Broadcast]
	public void BroadcastStamp( int kind, Vector3 pos, float radius, float depth, int seed, bool hasTargetZ, float targetZ )
	{
		if ( Networking.IsHost )
			return;

		ApplyLocal( Make( kind, pos, radius, depth, seed, hasTargetZ, targetZ ) );
	}

	static TerrainEditRequest Make( int kind, Vector3 pos, float radius, float depth, int seed, bool hasTargetZ, float targetZ )
	{
		return new TerrainEditRequest
		{
			Kind = (TerrainEditKind)kind,
			WorldPos = pos,
			Radius = radius,
			Depth = depth,
			Seed = seed,
			HasTargetZ = hasTargetZ,
			TargetZ = targetZ
		};
	}

	void ApplyLocal( TerrainEditRequest request )
	{
		TerrainSculpt.TryApply( Scene, request );
	}
}