tool remove gfx in combineaction SO data
This commit is contained in:
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user