tool remove gfx in combineaction SO data

This commit is contained in:
vuong dinh hoang
2026-05-29 10:02:16 +07:00
parent 6dfbdb7658
commit 1f86beee24
9 changed files with 3115 additions and 52 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,213 @@
// UTF-8 with BOM — required for Chinese character paths
using System;
using System.Collections.Generic;
using BrewMonster;
using BrewMonster.Scripts.ECModel;
using ModelViewer.Common;
using UnityEditor;
using UnityEngine;
public enum CombinedActionGfxPhase
{
All,
Cast,
Attack,
Other,
}
public readonly struct CombinedActionGfxEntry
{
public CombinedActionGfxEntry(
A3DCombinedAction action,
FX_BASE_INFO fxEvent,
string path,
string actionName,
string gfxBasename,
CombinedActionGfxPhase phase)
{
Action = action;
FxEvent = fxEvent;
Path = path;
ActionName = actionName;
GfxBasename = gfxBasename;
Phase = phase;
}
public A3DCombinedAction Action { get; }
public FX_BASE_INFO FxEvent { get; }
public string Path { get; }
public string ActionName { get; }
public string GfxBasename { get; }
public CombinedActionGfxPhase Phase { get; }
}
public static class CombinedActionGfxQuery
{
const string CastToken = "_\u541f\u5531_";
const string AttackRiseToken = "_\u65bd\u653e\u8d77_";
const string AttackFallToken = "_\u65bd\u653e\u843d_";
public static CombinedActionGfxPhase GetPhase(string actionName)
{
if (string.IsNullOrEmpty(actionName))
return CombinedActionGfxPhase.Other;
if (actionName.Contains(CastToken, StringComparison.Ordinal))
return CombinedActionGfxPhase.Cast;
if (actionName.Contains(AttackRiseToken, StringComparison.Ordinal) ||
actionName.Contains(AttackFallToken, StringComparison.Ordinal))
return CombinedActionGfxPhase.Attack;
return CombinedActionGfxPhase.Other;
}
public static string GetPhaseLabel(CombinedActionGfxPhase phase)
{
return phase switch
{
CombinedActionGfxPhase.Cast => "Cast",
CombinedActionGfxPhase.Attack => "Attack",
CombinedActionGfxPhase.Other => "Other",
_ => "All",
};
}
public static IEnumerable<CombinedActionGfxEntry> Scan(CombinedActionSO so)
{
if (so?.CombinedActionData == null)
yield break;
foreach (A3DCombinedAction action in so.CombinedActionData)
{
if (action?.m_EventInfoLst == null)
continue;
string actionName = action.m_strName ?? string.Empty;
CombinedActionGfxPhase phase = GetPhase(actionName);
foreach (EVENT_INFO eventInfo in action.m_EventInfoLst)
{
if (eventInfo is not FX_BASE_INFO fx || fx.m_strFilePaths == null)
continue;
foreach (string path in fx.m_strFilePaths)
{
if (string.IsNullOrWhiteSpace(path) || !path.Contains("gfx", StringComparison.OrdinalIgnoreCase))
continue;
string basename = LogPanelAnimeScene.GfxBasename(path);
if (string.IsNullOrEmpty(basename))
continue;
yield return new CombinedActionGfxEntry(action, fx, path, actionName, basename, phase);
}
}
}
}
public static bool MatchesFilters(
CombinedActionGfxEntry entry,
CombinedActionGfxPhase phaseFilter,
string actionFilter,
string basenameFilter)
{
if (phaseFilter != CombinedActionGfxPhase.All && entry.Phase != phaseFilter)
return false;
if (!string.IsNullOrWhiteSpace(actionFilter) &&
entry.ActionName.IndexOf(actionFilter, StringComparison.OrdinalIgnoreCase) < 0)
return false;
if (!string.IsNullOrWhiteSpace(basenameFilter) &&
entry.GfxBasename.IndexOf(basenameFilter, StringComparison.OrdinalIgnoreCase) < 0)
return false;
return true;
}
public static bool RemoveEntry(CombinedActionSO so, CombinedActionGfxEntry entry)
{
return RemovePath(so, entry.Action, entry.FxEvent, entry.Path);
}
public static bool RemovePath(CombinedActionSO so, A3DCombinedAction action, FX_BASE_INFO fxEvent, string path)
{
if (so == null || action == null || fxEvent == null || string.IsNullOrWhiteSpace(path))
return false;
Undo.RecordObject(so, "Remove GFX Event");
if (!ApplyRemoval(action, fxEvent, path))
return false;
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
return true;
}
public static int RemoveMatchingBasenameFromPhase(
CombinedActionSO so,
string basename,
CombinedActionGfxPhase phase)
{
if (so == null || string.IsNullOrWhiteSpace(basename) || phase == CombinedActionGfxPhase.All)
return 0;
Undo.RecordObject(so, "Remove GFX Events");
int removed = 0;
var toRemove = new List<(A3DCombinedAction action, FX_BASE_INFO fx, string path)>();
foreach (CombinedActionGfxEntry entry in Scan(so))
{
if (entry.Phase != phase)
continue;
if (!string.Equals(entry.GfxBasename, basename, StringComparison.OrdinalIgnoreCase))
continue;
toRemove.Add((entry.Action, entry.FxEvent, entry.Path));
}
foreach ((A3DCombinedAction action, FX_BASE_INFO fx, string path) item in toRemove)
{
if (ApplyRemoval(item.action, item.fx, item.path))
removed++;
}
if (removed > 0)
{
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
}
return removed;
}
static bool ApplyRemoval(A3DCombinedAction action, FX_BASE_INFO fxEvent, string path)
{
if (action == null || fxEvent == null || string.IsNullOrWhiteSpace(path))
return false;
if (fxEvent.m_strFilePaths != null && fxEvent.m_strFilePaths.Count > 1)
{
if (!fxEvent.m_strFilePaths.Remove(path))
return false;
if (fxEvent.m_strFilePaths.Count == 0 && action.m_EventInfoLst != null)
action.m_EventInfoLst.Remove(fxEvent);
}
else if (action.m_EventInfoLst != null)
{
if (!action.m_EventInfoLst.Remove(fxEvent))
return false;
}
else
{
return false;
}
return true;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5c3e5692ee8ed8043958fe88d788e1ed
@@ -0,0 +1,165 @@
// UTF-8 with BOM — required for Chinese character paths
using System.Collections.Generic;
using System.Linq;
using BrewMonster.Scripts.ECModel;
using UnityEditor;
using UnityEngine;
public class CombinedActionSOGfxRemover : EditorWindow
{
CombinedActionSO _combinedActionSo;
CombinedActionGfxPhase _phaseFilter = CombinedActionGfxPhase.All;
string _actionFilter = string.Empty;
string _basenameFilter = string.Empty;
Vector2 _scrollPos;
readonly List<CombinedActionGfxEntry> _entries = new();
GUIStyle _headerStyle;
GUIStyle HeaderStyle => _headerStyle ??= new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 };
[MenuItem("Tools/Brew Monster/Combined Action GFX Event Remover")]
public static void ShowWindow()
{
var win = GetWindow<CombinedActionSOGfxRemover>(false, "GFX Event Remover", true);
win.minSize = new Vector2(760, 420);
}
void OnEnable()
{
RefreshEntries();
}
void OnGUI()
{
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Combined Action GFX Event Remover", HeaderStyle);
EditorGUILayout.LabelField(
"Browse and remove FX/GFX events from CombinedActionSO assets.",
EditorStyles.miniLabel);
EditorGUILayout.Space(4);
EditorGUI.BeginChangeCheck();
_combinedActionSo = (CombinedActionSO)EditorGUILayout.ObjectField(
"Combined Action SO",
_combinedActionSo,
typeof(CombinedActionSO),
false);
if (EditorGUI.EndChangeCheck())
RefreshEntries();
using (new EditorGUILayout.HorizontalScope())
{
_phaseFilter = (CombinedActionGfxPhase)EditorGUILayout.EnumPopup("Phase", _phaseFilter);
if (GUILayout.Button("Refresh", GUILayout.Width(80)))
RefreshEntries();
}
_actionFilter = EditorGUILayout.TextField("Filter action name", _actionFilter);
_basenameFilter = EditorGUILayout.TextField("Filter gfx basename", _basenameFilter);
DrawBulkRemoveRow();
DrawSeparator();
DrawEntryTable();
}
void DrawBulkRemoveRow()
{
using (new EditorGUI.DisabledScope(_combinedActionSo == null || string.IsNullOrWhiteSpace(_basenameFilter)))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Bulk remove", GUILayout.Width(100));
if (GUILayout.Button("Remove basename from Attack actions", GUILayout.Height(22)))
{
int removed = CombinedActionGfxQuery.RemoveMatchingBasenameFromPhase(
_combinedActionSo,
_basenameFilter.Trim(),
CombinedActionGfxPhase.Attack);
Debug.Log($"[GFX Event Remover] Removed {removed} Attack GFX row(s) matching basename '{_basenameFilter.Trim()}'.");
RefreshEntries();
}
if (GUILayout.Button("Remove basename from Cast actions", GUILayout.Height(22)))
{
int removed = CombinedActionGfxQuery.RemoveMatchingBasenameFromPhase(
_combinedActionSo,
_basenameFilter.Trim(),
CombinedActionGfxPhase.Cast);
Debug.Log($"[GFX Event Remover] Removed {removed} Cast GFX row(s) matching basename '{_basenameFilter.Trim()}'.");
RefreshEntries();
}
EditorGUILayout.EndHorizontal();
}
}
void DrawEntryTable()
{
IEnumerable<CombinedActionGfxEntry> visible = _entries.Where(entry =>
CombinedActionGfxQuery.MatchesFilters(entry, _phaseFilter, _actionFilter, _basenameFilter));
int visibleCount = visible.Count();
EditorGUILayout.LabelField($"GFX rows: {visibleCount} / {_entries.Count}", EditorStyles.boldLabel);
if (_combinedActionSo == null)
{
EditorGUILayout.HelpBox("Assign a CombinedActionSO asset to begin.", MessageType.Info);
return;
}
if (visibleCount == 0)
{
EditorGUILayout.HelpBox("No GFX rows match the current filters.", MessageType.Warning);
return;
}
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
foreach (CombinedActionGfxEntry entry in visible)
DrawEntryRow(entry);
EditorGUILayout.EndScrollView();
}
void DrawEntryRow(CombinedActionGfxEntry entry)
{
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField(CombinedActionGfxQuery.GetPhaseLabel(entry.Phase), GUILayout.Width(56));
EditorGUILayout.LabelField(entry.GfxBasename, EditorStyles.boldLabel, GUILayout.Width(180));
EditorGUILayout.SelectableLabel(entry.Path, EditorStyles.miniLabel, GUILayout.Height(16));
if (GUILayout.Button("Remove", GUILayout.Width(72)))
{
if (EditorUtility.DisplayDialog(
"Remove GFX event",
$"Remove '{entry.GfxBasename}' from action '{entry.ActionName}'?",
"Remove",
"Cancel") &&
CombinedActionGfxQuery.RemoveEntry(_combinedActionSo, entry))
{
Debug.Log(
$"[GFX Event Remover] Removed '{entry.GfxBasename}' from action '{entry.ActionName}'.");
RefreshEntries();
}
}
}
EditorGUILayout.LabelField(entry.ActionName, EditorStyles.miniLabel);
}
}
void RefreshEntries()
{
_entries.Clear();
if (_combinedActionSo == null)
return;
_entries.AddRange(CombinedActionGfxQuery.Scan(_combinedActionSo));
Repaint();
}
static void DrawSeparator()
{
var rect = EditorGUILayout.GetControlRect(false, 1);
EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.35f));
EditorGUILayout.Space(4);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 550035299b5b67d489ea68adb0609e5a
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ea74bc9aef8c1251035c339a2a463e2d026cc0af379759abf687dcf13c9bda99
size 146707
oid sha256:9327b85e3d879a676a6928066162e7270af3c40f439dac47630f1325d3f925d5
size 150615
@@ -6,19 +6,42 @@ public class CopyTextButton : MonoBehaviour
{
[SerializeField] private TMP_Text textToCopy;
[SerializeField] private TMP_Text labelText;
[SerializeField] private TMP_Text actionNameText;
[SerializeField] Button button;
public void Awake()
{
button.onClick.AddListener(CopyText);
if (actionNameText != null)
{
var actionButton = actionNameText.GetComponent<Button>();
if (actionButton == null)
{
actionButton = actionNameText.gameObject.AddComponent<Button>();
actionButton.transition = Selectable.Transition.None;
actionButton.targetGraphic = actionNameText;
}
actionButton.onClick.AddListener(CopyActionName);
}
}
public void CopyText()
{
GUIUtility.systemCopyBuffer = textToCopy.text;
}
public void SetText(string labeltext, string text, bool? inAddressables = null)
public void CopyActionName()
{
var text = actionNameText.text;
if (string.IsNullOrEmpty(text) || text == "—")
return;
GUIUtility.systemCopyBuffer = text;
}
public void SetText(string labeltext, string text, bool? inAddressables = null, string actionName = null)
{
labelText.text = labeltext;
textToCopy.text = text;
if (actionNameText != null)
actionNameText.text = actionName ?? "—";
if (inAddressables == null)
labelText.color = Color.white;
else
@@ -36,19 +36,19 @@ namespace BrewMonster
}
}
public void AddCopyTextButton(string labeltext, string text, bool? inAddressables = null)
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);
copyTextButton.SetText(labeltext, text, inAddressables, actionName);
copyTextButtons.Add(copyTextButton);
}
else
{
copyTextButtons[currentIndex].gameObject.SetActive(true);
copyTextButtons[currentIndex].SetText(labeltext, text, inAddressables);
copyTextButtons[currentIndex].SetText(labeltext, text, inAddressables, actionName);
}
currentIndex++;
@@ -122,7 +122,7 @@ namespace BrewMonster
if (string.IsNullOrEmpty(displayText))
return;
AddCopyTextButton(label, displayText, GfxAddressableExists(rawPath));
AddCopyTextButton(label, displayText, GfxAddressableExists(rawPath), "—");
}
public void LogSkillCastGfx(CECHostPlayer player, int skillId)
@@ -176,7 +176,7 @@ namespace BrewMonster
if (string.IsNullOrEmpty(displayText) || !seen.Add(displayText))
continue;
AddCopyTextButton(label, displayText, GfxAddressableExists(path));
AddCopyTextButton(label, displayText, GfxAddressableExists(path), comAct.m_strName);
}
}
}
@@ -836,7 +836,6 @@ namespace BrewMonster
if (nCastTargetID == tar.idTarget)
bCastInTargets = true;
BMLogger.LogError("HoangDev: nCastTargetID:"+nCastTargetID);
AddOneTarget(nCastTargetID, nHostID, szFly, szHit, tar, i == 0, bIsGoblinSkill);
}
}