Files
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

59 lines
2.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using HotUpdate.UI;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>Confirms the camp painting has a walking placeholder and speech only after a tap.</summary>
public static class VerifyHomeExplorer
{
static async Task Until(Func<bool> condition, int seconds = 20)
{
var end = DateTime.UtcNow.AddSeconds(seconds);
while (!condition())
{
if (DateTime.UtcNow >= end) throw new Exception("Home explorer timed out");
await Task.Delay(100);
}
}
public static async Task<string> Play()
{
if (!EditorApplication.isPlaying) throw new Exception("Enter Play Mode first");
HomeExplorerPresenter presenter = null;
await Until(() => (presenter = UnityEngine.Object.FindObjectOfType<HomeExplorerPresenter>()) != null && presenter.Ready);
if (presenter.Talking) throw new Exception("Speech was visible before a tap: " + presenter.Line);
var greeting = presenter.transform.parent.Find("Layout_Sides/Panel_Greeting");
if (greeting != null && greeting.gameObject.activeSelf) throw new Exception("Static greeting is still visible");
float x = presenter.transform.parent.Find("Btn_Explorer").position.x;
await Task.Delay(900);
float moved = presenter.transform.parent.Find("Btn_Explorer").position.x;
if (Mathf.Abs(moved - x) < 1f) throw new Exception("Explorer did not move: " + x + " -> " + moved);
await Capture("walk");
var button = GameObject.Find("Btn_Explorer").GetComponent<Button>();
button.onClick.Invoke();
await Task.Delay(200);
if (!presenter.Talking) throw new Exception("Tap did not open speech");
string line = presenter.Line;
if (string.IsNullOrEmpty(line)) throw new Exception("Speech was empty");
await Capture("talk");
button.onClick.Invoke();
await Task.Delay(100);
if (presenter.Line == line) throw new Exception("Second tap repeated the same line");
return "PASS explorer walks and speaks only after a tap: " + line;
}
static async Task Capture(string name)
{
await Task.Delay(150);
var type = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType("Unity.Pipeline.Editor.Commands.Capture.CaptureCommands")).First(t => t != null);
type.GetMethod("CaptureGameView", BindingFlags.Public | BindingFlags.Static).Invoke(null, new object[] { 1080, 1920, null, "Screenshots/home_explorer_" + name + ".png", false, 0, "screen" });
}
}