using UnityEngine; namespace AibisDream.MiniGame.HuoShan.EmotionWave { [CreateAssetMenu(fileName = "EmotionWaveConfig", menuName = "AibisDream/EmotionWave Config")] public class EmotionWaveConfig : ScriptableObject { [System.Serializable] public struct WaveParams { [Header("Trajectory Shape")] [Range(0f, 1f)] public float waveMode; [Range(0.1f, 1f)] public float timeSpread; public float omegaX; public float omegaY; public float phaseSpeed; [Min(1)] public int numCycles; [Header("Amplitude & Waveform")] public float amplitude; public float amplitudeY; [Range(0f, 2f)] public float waveformType; [Range(0f, 1f)] public float spikiness; [Range(1f, 12f)] public float waveshapeGain; public float harmonicStrength; public float harmonicFreq; [Header("Noise & Glitch")] public float noiseAmount; public float noiseScale; public float noiseSpeed; public float jitter; [Range(0f, 1f)] public float weierstrassAmount; [Header("Line")] public float lineWidth; [Range(0.5f, 0.98f)] public float persistence; [Range(0f, 1f)] public float fragmentation; [Header("Color (HSL)")] public float hue; [Range(0f, 100f)] public float saturation; [Range(5f, 80f)] public float lightness; [Range(0f, 1.2f)] public float glowIntensity; [Header("Dynamics")] public float breathRate; public float breathDepth; [Header("Review (injected at runtime)")] [HideInInspector] public float reviewIntensity; [HideInInspector] public float featureBoxRate; [HideInInspector] public float thresholdLevel; [HideInInspector] public float cursorSpeed; [Header("Spike")] [HideInInspector] public float spikeIntensity; [HideInInspector] public float spikeFreq; [HideInInspector] public float spikeSharpness; [HideInInspector] public float envelopeDecay; [HideInInspector] public float tipLayer; public static WaveParams Lerp(WaveParams a, WaveParams b, float t) { return new WaveParams { waveMode = Mathf.Lerp(a.waveMode, b.waveMode, t), timeSpread = Mathf.Lerp(a.timeSpread, b.timeSpread, t), omegaX = Mathf.Lerp(a.omegaX, b.omegaX, t), omegaY = Mathf.Lerp(a.omegaY, b.omegaY, t), phaseSpeed = Mathf.Lerp(a.phaseSpeed, b.phaseSpeed, t), numCycles = Mathf.RoundToInt(Mathf.Lerp(a.numCycles, b.numCycles, t)), amplitude = Mathf.Lerp(a.amplitude, b.amplitude, t), amplitudeY = Mathf.Lerp(a.amplitudeY, b.amplitudeY, t), waveformType = Mathf.Lerp(a.waveformType, b.waveformType, t), spikiness = Mathf.Lerp(a.spikiness, b.spikiness, t), waveshapeGain = Mathf.Lerp(a.waveshapeGain, b.waveshapeGain, t), harmonicStrength = Mathf.Lerp(a.harmonicStrength, b.harmonicStrength, t), harmonicFreq = Mathf.Lerp(a.harmonicFreq, b.harmonicFreq, t), noiseAmount = Mathf.Lerp(a.noiseAmount, b.noiseAmount, t), noiseScale = Mathf.Lerp(a.noiseScale, b.noiseScale, t), noiseSpeed = Mathf.Lerp(a.noiseSpeed, b.noiseSpeed, t), jitter = Mathf.Lerp(a.jitter, b.jitter, t), weierstrassAmount = Mathf.Lerp(a.weierstrassAmount, b.weierstrassAmount, t), lineWidth = Mathf.Lerp(a.lineWidth, b.lineWidth, t), persistence = Mathf.Lerp(a.persistence, b.persistence, t), fragmentation = Mathf.Lerp(a.fragmentation, b.fragmentation, t), hue = Mathf.Lerp(a.hue, b.hue, t), saturation = Mathf.Lerp(a.saturation, b.saturation, t), lightness = Mathf.Lerp(a.lightness, b.lightness, t), glowIntensity = Mathf.Lerp(a.glowIntensity, b.glowIntensity, t), breathRate = Mathf.Lerp(a.breathRate, b.breathRate, t), breathDepth = Mathf.Lerp(a.breathDepth, b.breathDepth, t), }; } public void Clamp() { numCycles = Mathf.Max(2, numCycles); amplitude = Mathf.Max(0.05f, amplitude); amplitudeY = Mathf.Max(0.05f, amplitudeY); lineWidth = Mathf.Max(0.3f, lineWidth); persistence = Mathf.Clamp(persistence, 0.5f, 0.98f); glowIntensity = Mathf.Clamp(glowIntensity, 0f, 1.2f); noiseAmount = Mathf.Max(0f, noiseAmount); fragmentation = Mathf.Clamp01(fragmentation); hue = ((hue % 360f) + 360f) % 360f; saturation = Mathf.Clamp(saturation, 0f, 100f); lightness = Mathf.Clamp(lightness, 5f, 80f); waveMode = Mathf.Clamp01(waveMode); spikiness = Mathf.Clamp01(spikiness); waveformType = Mathf.Clamp(waveformType, 0f, 2f); timeSpread = Mathf.Clamp(timeSpread, 0.1f, 1f); waveshapeGain = Mathf.Clamp(waveshapeGain, 1f, 12f); weierstrassAmount = Mathf.Clamp01(weierstrassAmount); } } [System.Serializable] public struct StageAnchor { public float position; public string name; public string stage; public WaveParams waveParams; } public StageAnchor[] anchors = new StageAnchor[9]; static float Smoothstep(float t) { t = Mathf.Clamp01(t); return t * t * (3f - 2f * t); } public WaveParams Interpolate(float value) { value = Mathf.Clamp01(value); int n = anchors.Length; if (n == 0) return default; if (n == 1) return anchors[0].waveParams; int idx = 0; for (int i = 0; i < n - 1; i++) { if (value <= anchors[i + 1].position) { idx = i; break; } if (i == n - 2) idx = n - 2; } int i1 = idx; int i2 = Mathf.Min(n - 1, idx + 1); float segStart = anchors[i1].position; float segEnd = anchors[i2].position; float segLen = segEnd - segStart; float tLinear = segLen > 0f ? (value - segStart) / segLen : 0f; float t = Smoothstep(tLinear); var result = WaveParams.Lerp(anchors[i1].waveParams, anchors[i2].waveParams, t); result.Clamp(); return result; } public string GetStageName(float value) { value = Mathf.Clamp01(value); if (anchors == null || anchors.Length == 0) return ""; StageAnchor closest = anchors[0]; float minDist = float.MaxValue; foreach (var a in anchors) { float d = Mathf.Abs(value - a.position); if (d < minDist) { minDist = d; closest = a; } } if (minDist < 0.03f) return closest.name; StageAnchor lower = anchors[0]; StageAnchor upper = anchors[anchors.Length - 1]; for (int i = 0; i < anchors.Length - 1; i++) { if (value >= anchors[i].position && value <= anchors[i + 1].position) { lower = anchors[i]; upper = anchors[i + 1]; break; } } float blend = (upper.position - lower.position) > 0.001f ? (value - lower.position) / (upper.position - lower.position) : 0f; if (blend < 0.35f) return lower.name; if (blend > 0.65f) return upper.name; return $"{lower.name} -> {upper.name}"; } public string GetStagePhase(float value) { if (value < 0.35f) return "BUILD"; if (value < 0.65f) return "REVIEW"; return "ADJUST"; } } }