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:
udx
2026-10-21 12:23:55 +08:00
co-authored by Claude Opus 5.5
parent 6c64210331
commit 89e0fa246f
780 changed files with 7117051 additions and 15906 deletions
@@ -16,7 +16,7 @@ namespace HotUpdate.Gameplay
ConfirmDialogView _dialog;
PrototypeFlow _prototype;
UiPrefabLoader _musicLoader;
GameObject _musicInstance;
GameObject _musicInstance, _sfxInstance;
bool _maintenanceLock;
string _maintenanceMessage;
string _reconnectMessage;
@@ -79,31 +79,41 @@ namespace HotUpdate.Gameplay
_prototype = gameObject.AddComponent<PrototypeFlow>();
_prototype.Bind(_ctx, mgr, _hud, _dialog, () => _net?.Session, () => _net?.RenewSession());
_prototype.SetLocked(_maintenanceLock);
StartCoroutine(EnsureMusic());
StartCoroutine(EnsureAudio());
if (_maintenanceLock) _dialog?.Show("维护公告", _maintenanceMessage);
else MaybeShowBootNotice();
}
System.Collections.IEnumerator EnsureMusic()
/// <summary>Music and UI SFX prefabs share the Sound/Prefab folder and one loader.</summary>
System.Collections.IEnumerator EnsureAudio()
{
if (_musicInstance != null || _musicLoader != null) yield break;
_musicLoader = new UiPrefabLoader(_ctx?.Package, MainMenuMusic.PrefabFolder);
GameObject prefab = null;
yield return _musicLoader.LoadAsync(MainMenuMusic.PrefabName, loaded => prefab = loaded);
if (prefab == null) Debug.LogWarning("[MainMenuMusic] Music prefab unavailable in the current Art package.");
else
{
_musicInstance = Instantiate(prefab, transform, false);
_musicInstance.name = MainMenuMusic.PrefabName;
_prototype?.BindMusic(_musicInstance.GetComponent<MainMenuMusic>());
}
prefab = null;
yield return _musicLoader.LoadAsync(UiSfx.PrefabName, loaded => prefab = loaded);
if (prefab == null)
{
Debug.LogWarning("[MainMenuMusic] Music prefab unavailable in the current Art package.");
Debug.LogWarning("[UiSfx] UI sound prefab unavailable in the current Art package.");
yield break;
}
_musicInstance = Instantiate(prefab, transform, false);
_musicInstance.name = MainMenuMusic.PrefabName;
_prototype?.BindMusic(_musicInstance.GetComponent<MainMenuMusic>());
_sfxInstance = Instantiate(prefab, transform, false);
_sfxInstance.name = UiSfx.PrefabName;
}
void OnDestroy()
{
StopAllCoroutines();
if (_musicInstance != null) Destroy(_musicInstance);
if (_sfxInstance != null) Destroy(_sfxInstance);
_musicLoader?.Dispose();
}