Files
AIGame/Tools/verify_enemy_sfx.py
T
udxandClaude Opus 5.5 89e0fa246f 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>
2026-10-21 12:23:55 +08:00

34 lines
1.9 KiB
Python

"""Validate delivered one-shots and their TangoFlux source/recipe provenance."""
import hashlib
import json
from pathlib import Path
import numpy as np
from scipy.io import wavfile
ROOT=Path(__file__).resolve().parents[1]
DESIGN=ROOT/'Doc/Design/Sound/EnemyCombat'
def main():
generation=json.loads((DESIGN/'generation.json').read_text(encoding='utf-8'))
rows=[]
for item in generation['items']:
path=ROOT/item['output']; source=ROOT/item['source']
assert hashlib.sha256(path.read_bytes()).hexdigest()==item['outputSha256']
assert hashlib.sha256(source.read_bytes()).hexdigest()==item['sourceSha256']
assert item['job']['status']=='succeeded'
assert item['requestHash']==hashlib.sha256(json.dumps(item['request'],sort_keys=True).encode()).hexdigest()
rate,samples=wavfile.read(path);x=samples.astype(float)/32768
assert rate==44100 and x.ndim==2 and x.shape[1]==2 and samples.dtype==np.int16
assert .1<len(x)/rate<1 and np.isfinite(x).all()
assert np.max(np.abs(x))<.95 and np.max(np.abs(x[:4410]))>.05
assert np.max(np.abs(x[-10:]))<.003
rms=float(20*np.log10(np.sqrt(np.mean(x*x))))
assert rms>-36
rows.append(dict(id=item['id'],seconds=len(x)/rate,peakDb=float(20*np.log10(np.max(np.abs(x)))),rmsDb=rms,clippedSamples=int(np.sum(np.abs(x)>=.999)),hashesMatch=True))
assert len(rows)==8
report=dict(status='pass',clips=rows,checks='source/output/request hashes; succeeded job; PCM16 44.1kHz stereo; short duration; prompt onset; fade tail; finite samples; zero clipping',subjectiveListening='not_asserted_by_automated_QA',previewOrder=[item['id'] for item in generation['items']])
(DESIGN/'audio_validation.json').write_text(json.dumps(report,indent=2),encoding='utf-8')
print('PASS eight TangoFlux one-shots: provenance, format, onset, level, fade and zero clipping')
if __name__=='__main__':main()