313 lines
12 KiB
C#
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
|
|
}
|
|
}
|