Files
test/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs
T
2026-05-29 11:05:10 +07:00

313 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Skills;
using BrewMonster.Config;
using ModelViewer.Common;
using UnityEngine;
namespace BrewMonster
{
public class LogPanelAnimeScene : MonoBehaviour
{
[SerializeField] private CopyTextButton copyTextButtonPrefab;
[SerializeField] private List<CopyTextButton> copyTextButtons;
[SerializeField] private Transform copyTextButtonsParent;
private int currentIndex = 0;
public void Awake()
{
copyTextButtons = new List<CopyTextButton>();
currentIndex = 0;
Reset();
}
public void Reset()
{
currentIndex = 0;
foreach (var copyText in copyTextButtons)
{
copyText.gameObject.SetActive(false);
}
}
public void AddCopyTextButton(string labeltext, string text, bool? inAddressables = null, string actionName = null)
{
if (currentIndex >= copyTextButtons.Count)
{
var copyTextButton = Instantiate(copyTextButtonPrefab, copyTextButtonsParent);
copyTextButton.gameObject.SetActive(true);
copyTextButton.SetText(labeltext, text, inAddressables, actionName);
copyTextButtons.Add(copyTextButton);
}
else
{
copyTextButtons[currentIndex].gameObject.SetActive(true);
copyTextButtons[currentIndex].SetText(labeltext, text, inAddressables, actionName);
}
currentIndex++;
}
/// <summary>
/// Display name: last path segment only, no gfx/ prefix, no .gfx suffix.
/// 显示名:仅最后一段,无 gfx/ 前缀、无 .gfx 后缀。
/// </summary>
public static string GfxBasename(string path)
{
if (string.IsNullOrWhiteSpace(path))
return null;
path = AFile.NormalizePath(path, true);
if (path.StartsWith("gfx/", StringComparison.OrdinalIgnoreCase))
path = path.Substring(4);
int slash = path.LastIndexOf('/');
string file = slash >= 0 ? path.Substring(slash + 1) : path;
if (file.EndsWith(".gfx", StringComparison.OrdinalIgnoreCase))
file = file.Substring(0, file.Length - 4);
return string.IsNullOrEmpty(file) ? null : file;
}
/// <summary>
/// Candidate Addressables keys (runtime CECModel path + catalog .gfx tail).
/// </summary>
static IEnumerable<string> GfxAddressableKeyCandidates(string rawPath)
{
if (string.IsNullOrWhiteSpace(rawPath))
yield break;
string path = AFile.NormalizePath(rawPath, true);
string runtimeKey = path.StartsWith("gfx/", StringComparison.OrdinalIgnoreCase)
? path
: "gfx/" + path.TrimStart('/');
yield return runtimeKey;
if (!runtimeKey.EndsWith(".gfx", StringComparison.OrdinalIgnoreCase))
yield return runtimeKey + ".gfx";
}
public static bool GfxAddressableExists(string rawPath)
{
var mgr = AddressableManager.Instance;
if (mgr == null || !mgr.IsInitialized())
return false;
foreach (string key in GfxAddressableKeyCandidates(rawPath))
{
if (AddressableManager.KeyExists(key, typeof(GameObject)))
return true;
}
return false;
}
/// <summary>
/// Has path → show row. Green if Addressables has key, red if not. No path → skip.
/// 有路径则显示:Addressables 有则绿,无则红;无路径不显示。
/// </summary>
void AddGfxPathRow(string label, string rawPath)
{
if (string.IsNullOrWhiteSpace(rawPath))
return;
string displayText = GfxBasename(rawPath);
if (string.IsNullOrEmpty(displayText))
return;
AddCopyTextButton(label, displayText, GfxAddressableExists(rawPath), "—");
}
public void LogSkillCastGfx(CECHostPlayer player, int skillId)
{
AddGfxEventsFromComAct(player?.GetSkillCastComAct(skillId), "Cast");
}
public void LogPlayAttackEffectGfx(CECHostPlayer player, int skillId, int section = 0)
{
if (player == null)
return;
AddGfxEventsFromComAct(player.GetSkillAttackComActRise(skillId, section), "Attack");
AddGfxEventsFromComAct(player.GetSkillAttackComActFall(skillId, section), "Attack");
LogSkillStubFlyHitGfx(skillId, section);
}
public void LogAttackEventGfx(CECAttackEvent ev)
{
if (ev == null || ev.m_idSkill == 0)
return;
LogSkillStubFlyHitGfx(ev.m_idSkill, ev.m_nSkillSection);
}
/// <summary>
/// Logs visible-state aura GFX and state-reaction ComAct GFX (skill_state_action + name fallback for local anim-test).
/// </summary>
public void LogSkillStateGfx(CECHostPlayer player, int skillId)
{
List<int> stateIds = SkillVisibleStateResolver.ResolveVisibleStateIds(skillId);
bool hasConfigRows = SkillVisibleStateResolver.TryGetConfigRows(skillId, out IReadOnlyList<SkillStateActionRow> rows);
// #region agent log
AgentDebugLog("LogSkillStateGfx:entry", "resolved visible states", "H3",
$"{{\"skillId\":{skillId},\"stateIds\":\"{string.Join(",", stateIds)}\",\"hasConfigRows\":{hasConfigRows.ToString().ToLower()},\"configRowCount\":{(hasConfigRows ? rows.Count : 0)}}}");
// #endregion
if (stateIds.Count == 0 && !hasConfigRows)
return;
const string stateGfxBasePath = "gfx/策划联入/状态效果/";
var seenStateGfx = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var seenBeHitActions = new HashSet<string>(StringComparer.Ordinal);
var seenStayActions = new HashSet<string>(StringComparer.Ordinal);
CECModel playerModel = player?.GetPlayerCECModel();
int profession = player != null ? player.GetProfession() : 0;
foreach (int stateId in stateIds)
{
VisibleState visibleState = VisibleState.Query(profession, stateId);
string effect = visibleState?.GetEffect();
// #region agent log
AgentDebugLog("LogSkillStateGfx:query", "VisibleState resolved", "H2",
$"{{\"skillId\":{skillId},\"stateId\":{stateId},\"profession\":{profession},\"effect\":\"{effect ?? ""}\",\"name\":\"{visibleState?.GetName() ?? ""}\"}}");
// #endregion
if (string.IsNullOrWhiteSpace(effect))
continue;
string rawPath = stateGfxBasePath + effect;
string displayText = GfxBasename(rawPath);
if (string.IsNullOrEmpty(displayText) || !seenStateGfx.Add(displayText))
continue;
string stateLabel = visibleState.GetName();
if (string.IsNullOrEmpty(stateLabel))
stateLabel = $"state {stateId}";
else
stateLabel += $" (id {stateId})";
AddCopyTextButton("State", displayText, GfxAddressableExists(rawPath), stateLabel);
}
if (!hasConfigRows)
return;
foreach (SkillStateActionRow row in rows)
{
if (playerModel != null && !string.IsNullOrWhiteSpace(row.beHitAction) &&
seenBeHitActions.Add(row.beHitAction))
{
AddGfxEventsFromComAct(playerModel.GetComActByName(row.beHitAction), "StateBeHit");
}
if (playerModel != null && !string.IsNullOrWhiteSpace(row.stayDownAction) &&
seenStayActions.Add(row.stayDownAction))
{
AddGfxEventsFromComAct(playerModel.GetComActByName(row.stayDownAction), "StateStay");
}
}
}
public void AddGfxEventsFromComAct(A3DCombinedAction comAct, string label)
{
if (string.IsNullOrEmpty(label) || comAct?.m_EventInfoLst == null)
{
return;
}
var seen = new HashSet<string>();
foreach (var ev in comAct.m_EventInfoLst)
{
if (ev is not FX_BASE_INFO gfx || gfx.m_strFilePaths == null)
{
continue;
}
foreach (string path in gfx.m_strFilePaths)
{
if(!path.Contains("gfx"))
{
continue;
}
if (string.IsNullOrWhiteSpace(path))
continue;
string displayText = GfxBasename(path);
if (string.IsNullOrEmpty(displayText) || !seen.Add(displayText))
continue;
AddCopyTextButton(label, displayText, GfxAddressableExists(path), comAct.m_strName);
}
}
}
void LogSkillStubFlyHitGfx(int skillId, int section)
{
string fly = null;
string hitGrnd = null;
string hit = null;
var atkMan = CECAttacksMan.Instance;
if (section > 0 && atkMan?.m_pMultiSkillGfxComposerMan != null)
{
var composer = atkMan.m_pMultiSkillGfxComposerMan.GetSkillGfxComposer(skillId, section);
if (composer != null)
{
fly = composer.flyGfxName;
hit = composer.hitGfxName;
hitGrnd = composer.hitGrdGfxName;
}
}
else
{
var composer = atkMan?.GetSkillGfxComposerMan()?.GetSkillGfxComposer(skillId);
if (composer != null)
{
fly = composer.flyGfxName;
hit = composer.hitGfxName;
hitGrnd = composer.hitGrdGfxName;
}
}
if (string.IsNullOrEmpty(fly) && string.IsNullOrEmpty(hit) && string.IsNullOrEmpty(hitGrnd))
{
var paths = ElementSkill.GetAllGFX((uint)skillId);
fly = paths.Item1;
hitGrnd = paths.Item2;
hit = paths.Item3;
}
AddGfxPathRow("Fly", fly);
AddGfxPathRow("Hit", hit);
AddGfxPathRow("HitGrnd", hitGrnd);
}
// #region agent log
public static void AgentDebugLog(string location, string message, string hypothesisId, string dataJson)
{
try
{
string path = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "..", "debug-1197c4.log"));
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string line = "{\"sessionId\":\"1197c4\",\"location\":\"" + location + "\",\"message\":\"" + message + "\",\"hypothesisId\":\"" + hypothesisId + "\",\"timestamp\":" + ts + ",\"data\":" + dataJson + "}";
File.AppendAllText(path, line + Environment.NewLine);
}
catch
{
}
}
// #endregion
}
}