Add formal enemies, remaining formal partners and synthesized UI SFX
- UI SFX v3: procedurally synthesized one-shots (Tools/synth_ui_sfx.py) wired to buttons, rewards, errors and red dots via UiSfx, gated by the SFX setting. - Five formal enemies (Hornlet, Dashlet, Shellhorn, HornCaptain, GreatHorn): Hunyuan 50k -> 2000-tri XPbr, DGXA TokenRig, Idle/Walk/Attack, nested per type in PrototypeArena; PrototypeBattle.Enemy gains presentation-only Moving/Strikes. - Formal partners for all ten pets in the arena and portraits, including new VineSprite, ThunderLeopard and TideWhale. - Pipeline fixes: reject local TokenRig fallback, drop loose mesh islands, configurable Unity run_script timeout, companion static stage. - Also includes concurrent work from another tool in this working tree (FlameLion, enemy combat FX, challenge mode, home explorer, server updates). - Resources registered in Doc/ResDesc_*.md; verified on isolated :18081. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HotUpdate.Gameplay
|
||||
{
|
||||
/// <summary>Order matches the clips serialized on the UiSfx prefab (Ui_Click … Ui_Notify).</summary>
|
||||
public enum UiSound { Click, Confirm, Back, Open, Close, Tab, Claim, Deny, Toggle, Notify }
|
||||
|
||||
/// <summary>One-shot UI feedback from the authored UiSfx prefab; obeys the SFX setting, independent of music.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Button names from the UI JSON decide the feedback; anything unlisted is a plain click.</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user