using UnityEngine; namespace HotUpdate.Gameplay { /// Order matches the clips serialized on the UiSfx prefab (Ui_Click … Ui_Notify). public enum UiSound { Click, Confirm, Back, Open, Close, Tab, Claim, Deny, Toggle, Notify } /// One-shot UI feedback from the authored UiSfx prefab; obeys the SFX setting, independent of music. public sealed class UiSfx : MonoBehaviour { public const string PrefabName = "UiSfx"; public const string PrefabFolder = "Assets/Bundles/Sound/Prefab"; // Ignore the same sound re-requested within this window (e.g. a refresh firing twice in one frame). const float RepeatGuard = .06f; [SerializeField] AudioSource _source; [SerializeField] AudioClip[] _clips; static UiSfx _instance; UiSound _last; float _lastAt = -1; public AudioSource Source => _source; public AudioClip Clip(UiSound sound) => _clips != null && (int)sound < _clips.Length ? _clips[(int)sound] : null; void OnEnable() { _instance = this; } void OnDisable() { if (_instance == this) _instance = null; } public static void Play(UiSound sound) { var player = _instance; if (player == null || player._source == null || !PrototypeClientPrefs.Sfx) return; var clip = player.Clip(sound); if (clip == null) return; float now = Time.unscaledTime; if (sound == player._last && now - player._lastAt < RepeatGuard) return; player._last = sound; player._lastAt = now; player._source.PlayOneShot(clip); } /// Button names from the UI JSON decide the feedback; anything unlisted is a plain click. public static UiSound ForButton(string name) { if (name == "Back" || name == "Home" || name == "Close") return UiSound.Close; if (name == "CancelPreview" || name == "RemovePet" || name == "Abandon") return UiSound.Back; if (name.Contains("Tab") || name == "MailPrev" || name == "MailNext") return UiSound.Tab; if (name.StartsWith("Toggle") || name == "CycleQuality") return UiSound.Toggle; return UiSound.Click; } } }