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 copyTextButtons; [SerializeField] private Transform copyTextButtonsParent; private int currentIndex = 0; public void Awake() { copyTextButtons = new List(); 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++; } /// /// Display name: last path segment only, no gfx/ prefix, no .gfx suffix. /// 显示名:仅最后一段,无 gfx/ 前缀、无 .gfx 后缀。 /// 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; } /// /// Candidate Addressables keys (runtime CECModel path + catalog .gfx tail). /// static IEnumerable 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; } /// /// Has path → show row. Green if Addressables has key, red if not. No path → skip. /// 有路径则显示:Addressables 有则绿,无则红;无路径不显示。 /// 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); } /// /// Logs visible-state aura GFX and state-reaction ComAct GFX (skill_state_action + name fallback for local anim-test). /// public void LogSkillStateGfx(CECHostPlayer player, int skillId) { List stateIds = SkillVisibleStateResolver.ResolveVisibleStateIds(skillId); bool hasConfigRows = SkillVisibleStateResolver.TryGetConfigRows(skillId, out IReadOnlyList 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(StringComparer.OrdinalIgnoreCase); var seenBeHitActions = new HashSet(StringComparer.Ordinal); var seenStayActions = new HashSet(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(); 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 } }