using System; using System.Collections.Generic; using UltimateWater.Internal; using UnityEngine; using UnityEngine.Serialization; namespace UltimateWater { public class ComplexWavesEmitter : MonoBehaviour, IWavesParticleSystemPlugin { public enum WavesSource { CustomWaveFrequency = 0, WindWavesSpectrum = 1, Shoaling = 2, Vehicle = 3 } private class SpawnPoint { public readonly Vector2 Position; public Vector2 Direction; public readonly float Frequency; public readonly float Amplitude; public readonly float TimeInterval; public float TimeLeft; public SpawnPoint(Vector2 position, Vector2 direction, float frequency, float amplitude, float speed) { Position = position; Direction = direction; Frequency = frequency; Amplitude = amplitude; TimeInterval = (float)Math.PI * 2f / speed * UnityEngine.Random.Range(1f, 8f); TimeLeft = UnityEngine.Random.Range(0f, TimeInterval); } } [FormerlySerializedAs("wavesParticleSystem")] [SerializeField] private WaveParticleSystem _WavesParticleSystem; [FormerlySerializedAs("wavesSource")] [SerializeField] private WavesSource _WavesSource; [FormerlySerializedAs("wavelength")] [SerializeField] private float _Wavelength = 120f; [FormerlySerializedAs("amplitude")] [SerializeField] private float _Amplitude = 0.6f; [FormerlySerializedAs("emissionRate")] [SerializeField] private float _EmissionRate = 2f; [FormerlySerializedAs("width")] [SerializeField] private int _Width = 8; [FormerlySerializedAs("spectrumCoincidenceRange")] [SerializeField] [Range(0f, 180f)] private float _SpectrumCoincidenceRange = 20f; [FormerlySerializedAs("spectrumWavesCount")] [SerializeField] [Range(0f, 100f)] private int _SpectrumWavesCount = 30; [FormerlySerializedAs("span")] [SerializeField] [Tooltip("Affects both waves and emission area width.")] private float _Span = 1000f; [FormerlySerializedAs("waveShapeIrregularity")] [SerializeField] [Range(1f, 3.5f)] private float _WaveShapeIrregularity = 2f; [FormerlySerializedAs("lifetime")] [SerializeField] private float _Lifetime = 200f; [FormerlySerializedAs("shoreWaves")] [SerializeField] private bool _ShoreWaves = true; [FormerlySerializedAs("boundsSize")] [SerializeField] private Vector2 _BoundsSize = new Vector2(500f, 500f); [FormerlySerializedAs("spawnDepth")] [SerializeField] [Range(3f, 80f)] private float _SpawnDepth = 8f; [FormerlySerializedAs("emissionFrequencyScale")] [SerializeField] [Range(0.01f, 2f)] private float _EmissionFrequencyScale = 1f; [FormerlySerializedAs("spawnPointsDensity")] [SerializeField] private float _SpawnPointsDensity = 1f; private SpawnPoint[] _SpawnPoints; private WindWaves _WindWaves; private float _NextSpawnTime; private float _TimeStep; public void UpdateParticles(float time, float deltaTime) { if (!base.isActiveAndEnabled) { return; } switch (_WavesSource) { case WavesSource.CustomWaveFrequency: if (time > _NextSpawnTime) { Vector3 position = base.transform.position; Vector3 forward = base.transform.forward; WaveParticle waveParticle = WaveParticle.Create(new Vector2(position.x, position.z), new Vector2(forward.x, forward.z).normalized, (float)Math.PI * 2f / _Wavelength, _Amplitude, _Lifetime, _ShoreWaves); if (waveParticle != null) { _WavesParticleSystem.Spawn(waveParticle, _Width, _WaveShapeIrregularity); waveParticle.Destroy(); waveParticle.AddToCache(); } _NextSpawnTime += _TimeStep; } break; case WavesSource.WindWavesSpectrum: if (_SpawnPoints == null) { CreateSpectralWavesSpawnPoints(); } UpdateSpawnPoints(deltaTime); break; case WavesSource.Shoaling: if (_SpawnPoints == null) { CreateShoalingSpawnPoints(); } UpdateSpawnPoints(deltaTime); break; } } private void Awake() { _WindWaves = _WavesParticleSystem.GetComponent().WindWaves; OnValidate(); _WavesParticleSystem.RegisterPlugin(this); } private void OnEnable() { OnValidate(); _NextSpawnTime = Time.time + UnityEngine.Random.Range(0f, _TimeStep); } private void OnValidate() { _TimeStep = _Wavelength / _EmissionRate; } private void UpdateSpawnPoints(float deltaTime) { deltaTime *= _EmissionFrequencyScale; for (int i = 0; i < _SpawnPoints.Length; i++) { SpawnPoint spawnPoint = _SpawnPoints[i]; spawnPoint.TimeLeft -= deltaTime; if (spawnPoint.TimeLeft < 0f) { float num = (float)Math.PI * 2f / spawnPoint.Frequency; float num2 = _Span * 0.3f / num; int min = Mathf.Max(2, Mathf.RoundToInt(num2 * 0.7f)); int max = Mathf.Max(2, Mathf.RoundToInt(num2 * 1.429f)); spawnPoint.TimeLeft += spawnPoint.TimeInterval; Vector2 position = spawnPoint.Position + new Vector2(spawnPoint.Direction.y, 0f - spawnPoint.Direction.x) * UnityEngine.Random.Range((0f - _Span) * 0.35f, _Span * 0.35f); WaveParticle waveParticle = WaveParticle.Create(position, spawnPoint.Direction, spawnPoint.Frequency, spawnPoint.Amplitude, _Lifetime, _ShoreWaves); if (waveParticle != null) { _WavesParticleSystem.Spawn(waveParticle, UnityEngine.Random.Range(min, max), _WaveShapeIrregularity); waveParticle.Destroy(); waveParticle.AddToCache(); } } } } private void CreateShoalingSpawnPoints() { Bounds bounds = new Bounds(base.transform.position, new Vector3(_BoundsSize.x, 0f, _BoundsSize.y)); float x = bounds.min.x; float z = bounds.min.z; float x2 = bounds.max.x; float z2 = bounds.max.z; float num = Mathf.Sqrt(_SpawnPointsDensity); float num2 = Mathf.Max(35f, bounds.size.x / 256f) / num; float num3 = Mathf.Max(35f, bounds.size.z / 256f) / num; bool[,] array = new bool[32, 32]; List list = new List(); GerstnerWave[] array2 = _WindWaves.SpectrumResolver.SelectShorelineWaves(50, 0f, 360f); if (array2.Length == 0) { _SpawnPoints = new SpawnPoint[0]; return; } float num4 = _SpawnDepth * 0.85f; float num5 = _SpawnDepth * 1.18f; Vector2 vector = default(Vector2); for (float num6 = z; num6 < z2; num6 += num3) { for (float num7 = x; num7 < x2; num7 += num2) { int num8 = Mathf.FloorToInt(32f * (num7 - x) / (x2 - x)); int num9 = Mathf.FloorToInt(32f * (num6 - z) / (z2 - z)); if (array[num8, num9]) { continue; } float totalDepthAt = StaticWaterInteraction.GetTotalDepthAt(num7, num6); if (!(totalDepthAt > num4) || !(totalDepthAt < num5) || !(UnityEngine.Random.value < 0.06f)) { continue; } array[num8, num9] = true; vector.x = StaticWaterInteraction.GetTotalDepthAt(num7 - 3f, num6) - StaticWaterInteraction.GetTotalDepthAt(num7 + 3f, num6); vector.y = StaticWaterInteraction.GetTotalDepthAt(num7, num6 - 3f) - StaticWaterInteraction.GetTotalDepthAt(num7, num6 + 3f); vector.Normalize(); GerstnerWave gerstnerWave = array2[0]; float num10 = Vector2.Dot(vector, array2[0].Direction); for (int i = 1; i < array2.Length; i++) { float num11 = Vector2.Dot(vector, array2[i].Direction); if (num11 > num10) { num10 = num11; gerstnerWave = array2[i]; } } list.Add(new SpawnPoint(new Vector2(num7, num6), vector, gerstnerWave.Frequency, Mathf.Abs(gerstnerWave.Amplitude), gerstnerWave.Speed)); } } _SpawnPoints = list.ToArray(); } private void CreateSpectralWavesSpawnPoints() { Vector3 normalized = base.transform.forward.normalized; float num = Mathf.Atan2(normalized.x, normalized.z); GerstnerWave[] array = _WindWaves.SpectrumResolver.SelectShorelineWaves(_SpectrumWavesCount, num * 57.29578f, _SpectrumCoincidenceRange); _SpectrumWavesCount = array.Length; Vector3 vector = new Vector3(base.transform.position.x + _Span * 0.5f, 0f, base.transform.position.z + _Span * 0.5f); Vector2 vector2 = new Vector2(vector.x, vector.z); List list = new List(); for (int i = 0; i < _SpectrumWavesCount; i++) { GerstnerWave gerstnerWave = array[i]; if (gerstnerWave.Amplitude != 0f) { Vector2 position = vector2 - gerstnerWave.Direction * _Span * 0.5f; list.Add(new SpawnPoint(position, gerstnerWave.Direction, gerstnerWave.Frequency, Mathf.Abs(gerstnerWave.Amplitude), gerstnerWave.Speed)); } } _SpawnPoints = list.ToArray(); } } }