Editor/TerrainShapes/Realistic.cs
using Editor;
using Sandbox;
using System;

namespace Sturnus.TerrainGenerationTool;
public static class Realistic
{
	public static float Default(
	int x,
	int y,
	int width,
	int height,
	long seed,
	float minHeight,
	bool domainWarping,
	float domainWarpingSize,
	float domainWarpingStrength)
	{
		// Normalize coordinates to [-1, 1]
		float nx = (x / (float)width) * 2 - 1;
		float ny = (y / (float)height) * 2 - 1;

		// Apply domain warping for natural distortion
		if ( domainWarping )
		{
			float warpX = OpenSimplex2S.Noise2( seed, nx * domainWarpingSize, ny * domainWarpingSize ) * domainWarpingStrength;
			float warpY = OpenSimplex2S.Noise2( seed + 1, nx * domainWarpingSize, ny * domainWarpingSize ) * domainWarpingStrength;
			nx += warpX;
			ny += warpY;
		}

		float baseTerrain = OpenSimplex2S.Noise2( seed, nx, ny );

		// Create hill/valley transitions with non-linear blending
		float hillFactor = MathF.Pow( baseTerrain, 6 ); // Emphasize hills
		float valleyFactor = 1.0f - MathF.Pow( 1.0f - baseTerrain, 1 ); // Emphasize valleys

		// Use smoothstep-like function for non-linear blending
		float smoothTransition = SmoothStep( 0.75f, 1.25f, baseTerrain );
		float terrainShape = MathX.Lerp( valleyFactor, hillFactor, smoothTransition );

		// Add finer details
		float fineNoise = OpenSimplex2S.Noise2( seed + 2, nx * 8.0f, ny * 8.0f ) * 0.1f;

		// Combine base terrain with fine details
		float heightValue = terrainShape + fineNoise;

		// Add a baseline value to ensure no flat zero areas
		float baseline = minHeight; // Minimum height
		heightValue = MathF.Max( heightValue, baseline );

		// Normalize height to [0, 1]
		return Math.Clamp( heightValue, 0.0f, 1.0f );
	}

	public static float Hills( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize = 0.1f, float warpStrength = 0.5f )
	{
		Random random = new Random( (int)(seed & 0xFFFFFFFF) );
		float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
		float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]

		float hillHeight = 0.6f;   // Maximum height of the hills
		float hillFrequency = 3f; // Frequency of the hills
		float noiseStrength = 0f; // Strength of noise for natural detail

		// Apply domain warping for irregularity
		if ( warp )
		{
			float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
			float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
			nx += warpX;
			ny += warpY;
		}

		// Base overlapping hills
		float hillBase1 = MathF.Sin( nx * hillFrequency * MathF.PI ) + MathF.Cos( ny * hillFrequency * MathF.PI );
		float hillBase2 = MathF.Sin( ny * (hillFrequency * 0.75f) * MathF.PI ) + MathF.Cos( nx * (hillFrequency * 0.75f) * MathF.PI );
		float combinedHills = (hillBase1 + hillBase2) / 5f; // Blend two hill patterns
		combinedHills = MathF.Abs( combinedHills ); // Ensure positive-only values

		// Add noise for natural detail
		float baseNoise = OpenSimplex2S.Noise2( seed, nx * 6.0f, ny * 6.0f ) * noiseStrength;
		float fineNoise = OpenSimplex2S.Noise2( seed + 1, nx * 12.0f, ny * 12.0f ) * (noiseStrength / 2);

		// Combine hills and noise
		float heightValue = (combinedHills * hillHeight) + minHeight + fineNoise;
		// Ensure minimum base height
	
		// Clamp the final height value
		return Math.Clamp( heightValue, 0, 1 );
	}

	public static float Plateau(
		int x,
		int y,
		int width,
		int height,
		long seed,
		float minHeight,
		bool warp,
		float warpSize,
		float warpStrength
	
	)
	{
		Random random = new Random( (int)(seed & 0xFFFFFFFF) );
		float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
		float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]

		float plateauHeight = 0.8f;   // Maximum height of the plateau
		float widthRatio = 0.75f;     // Width of the side that does not extend to the edge
		float slopeWidthRatio = 0.02f; // Width of the slope transition
		float slopeNoiseStrength = 0.1f; // Strength of additional noise on slopes
		float baseHeight = 0.1f;     // Minimum terrain height
		float noiseStrength = 0f;  // Strength of noise for natural variation
		float cutInOutStrength = 2f; // Strength of the cut-ins and jut-outs
		float topNoiseStrength = 0f; // Reduced noise strength for the plateau to

		// Apply domain warping for irregularity
		if ( warp )
		{
			float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
			float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
			nx += warpX;
			ny += warpY;
		}

		// Define the plateau boundaries
		float plateauStartX = -1f; // Left edge
		float plateauEndX = widthRatio * 2 - 1; // End of the one side
		float plateauStartY = -1f; // Bottom edge
		float plateauEndY = 1f; // Top edge (extends to the edge)

		// Check if the current point is within the flat plateau region
		bool isFlatPlateau = nx >= plateauStartX && nx <= plateauEndX && ny >= plateauStartY && ny <= plateauEndY;

		// Flat plateau height
		float heightValue = isFlatPlateau ? plateauHeight : 0f;

		// Add slopes with jut-outs and cut-ins for smooth, natural drop-offs
		if ( !isFlatPlateau )
		{
			// Add noise-based cut-ins and jut-outs
			float noiseCut = OpenSimplex2S.Noise2( seed + 20, nx * 10.0f, ny * 10.0f ) * cutInOutStrength;
			// Generate additional noise for slopes
			float slopeNoise = OpenSimplex2S.Noise2( seed + 30, nx * 15.0f, ny * 15.0f ) * slopeNoiseStrength;

			// Left slope
			if ( nx < plateauStartX )
			{
				float slope = Math.Clamp( 1f - MathF.Abs( (nx - plateauStartX) / slopeWidthRatio ) + noiseCut  + slopeNoise, 0f, 1f );
				heightValue = Math.Max( heightValue, slope * plateauHeight );
			}
			// Right slope (for the single side ending early)
			else if ( nx > plateauEndX )
			{
				float slope = Math.Clamp( 1f - MathF.Abs( (nx - plateauEndX) / slopeWidthRatio ) + noiseCut + slopeNoise, 0f, 1f );
				heightValue = Math.Max( heightValue, slope * plateauHeight );
			}
			// Bottom slope
			if ( ny < plateauStartY )
			{
				float slope = Math.Clamp( 1f - MathF.Abs( (ny - plateauStartY) / slopeWidthRatio ) + noiseCut + slopeNoise, 0f, 1f );
				heightValue = Math.Max( heightValue, slope * plateauHeight );
			}
			// Top slope
			if ( ny > plateauEndY )
			{
				float slope = Math.Clamp( 1f - MathF.Abs( (ny - plateauEndY) / slopeWidthRatio ) + noiseCut + slopeNoise, 0f, 1f );
				heightValue = Math.Max( heightValue, slope * plateauHeight );
			}
		}

		// Add noise for terrain variation
		float baseNoise = OpenSimplex2S.Noise2( seed + 100, nx * 8.0f, ny * 8.0f ) * topNoiseStrength;

		float fineNoise = OpenSimplex2S.Noise2( seed + 1, nx * 16.0f, ny * 16.0f ) * (noiseStrength / 2);

		// Add the noise and base height to the terrain
		heightValue += baseNoise + fineNoise + baseHeight;

		// Ensure the base terrain height does not fall below baseHeight
		heightValue = Math.Max( heightValue, baseHeight );

		var heightValueBase = Math.Max( heightValue, minHeight );

		// Clamp the final height
		return Math.Clamp( heightValueBase, 0, 1 );
	}

	private static float SmoothStep( float edge0, float edge1, float x )
	{
		x = Math.Clamp( (x - edge0) / (edge1 - edge0), 0.0f, 1.0f ); // Normalize to [0, 1]
		return x * x * (3 - 2 * x); // Smoothstep formula
	}

}