using System; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; using BaseGames.Boss; namespace BaseGames.Editor.Modules { /// /// DataHub Boss技能模块 —— Tab 切换管理 BossSkillSO 和 SkillSequenceSO。 /// public class BossSkillModule : IDataModule, IDataModuleOrdered { private const string SkillFolder = "Assets/_Game/Data/Boss/Skills"; private const string SeqFolder = "Assets/_Game/Data/Boss/Sequences"; public string ModuleId => "boss"; public string DisplayName => "Boss技能"; public string IconName => null; public int DisplayOrder => 50; private int _activeTab = 0; private SoListPane _skillPane; private SoListPane _seqPane; private Action _onSelected; private DetailHeader _header; private BossSkillSO _selectedSkill; private SkillSequenceSO _selectedSeq; public void Initialize() { _skillPane = new SoListPane( SkillFolder, "ABL_Boss_", s => s.category.ToString()); _skillPane.SelectionChanged = s => { _selectedSkill = s; _onSelected?.Invoke(s); }; _seqPane = new SoListPane(SeqFolder, "ABL_Seq_"); _seqPane.SelectionChanged = s => { _selectedSeq = s; _onSelected?.Invoke(s); }; } public void BuildListPane(VisualElement container, Action onSelected) { _onSelected = onSelected; container.style.flexDirection = FlexDirection.Column; // Tab bar var tabBar = new VisualElement(); tabBar.style.flexDirection = FlexDirection.Row; tabBar.style.borderBottomWidth = 1; tabBar.style.borderBottomColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.3f)); container.Add(tabBar); var btnSkill = BuildTabBtn("技能 (Skill)", 0, tabBar); var btnSeq = BuildTabBtn("序列 (Seq)", 1, tabBar); var listArea = new VisualElement(); listArea.style.flexGrow = 1; container.Add(listArea); ShowTab(0, listArea, new[] { btnSkill, btnSeq }); btnSkill.clicked += () => ShowTab(0, listArea, new[] { btnSkill, btnSeq }); btnSeq.clicked += () => ShowTab(1, listArea, new[] { btnSkill, btnSeq }); _skillPane.Refresh(); _seqPane.Refresh(); } public void BuildDetailPane(VisualElement container, UnityEngine.Object selected) { _header = new DetailHeader(); _header.SetAsset(selected); _header.RenameRequested += name => OnRenameRequested(selected, name); container.Add(_header); if (selected == null) return; if (selected is BossSkillSO skill) { container.Add(BuildSkillCard(skill)); container.Add(BuildActionBar(skill, SkillFolder, _skillPane)); container.Add(SkillModule.MakeDivider()); var insp = new InspectorElement(skill); container.Add(insp); } else if (selected is SkillSequenceSO seq) { container.Add(BuildSeqCard(seq)); container.Add(BuildActionBar(seq, SeqFolder, _seqPane)); container.Add(SkillModule.MakeDivider()); var insp = new InspectorElement(seq); container.Add(insp); } } public void OnActivated() { _skillPane?.Refresh(); _seqPane?.Refresh(); } // ── 内部 ───────────────────────────────────────────────────────────── private Button BuildTabBtn(string text, int tabIdx, VisualElement bar) { var btn = new Button { text = text }; btn.style.flexGrow = 1; btn.style.paddingTop = 5; btn.style.paddingBottom = 5; btn.style.borderTopLeftRadius = 0; btn.style.borderTopRightRadius = 0; btn.style.borderBottomLeftRadius = 0; btn.style.borderBottomRightRadius = 0; btn.style.borderLeftWidth = 0; btn.style.borderRightWidth = 0; btn.style.borderTopWidth = 0; btn.style.borderBottomWidth = 0; btn.style.backgroundColor = new StyleColor(Color.clear); btn.userData = tabIdx; bar.Add(btn); return btn; } private void ShowTab(int tab, VisualElement area, Button[] tabBtns) { _activeTab = tab; area.Clear(); for (int i = 0; i < tabBtns.Length; i++) { if (i == tab) { tabBtns[i].style.borderBottomWidth = 2; tabBtns[i].style.borderBottomColor = new StyleColor(new Color(0.4f, 0.65f, 1f, 1f)); tabBtns[i].style.opacity = 1f; } else { tabBtns[i].style.borderBottomWidth = 0; tabBtns[i].style.opacity = 0.65f; } } if (tab == 0) { _skillPane.style.flexGrow = 1; area.Add(_skillPane); } else { _seqPane.style.flexGrow = 1; area.Add(_seqPane); } } private void OnRenameRequested(UnityEngine.Object asset, string newName) { var (ok, err) = AssetOperations.Rename(asset, newName); if (!ok) EditorUtility.DisplayDialog("重命名失败", err, "确定"); else { _header.SetAsset(asset); if (_activeTab == 0) _skillPane.Invalidate(); else _seqPane.Invalidate(); } } private static VisualElement BuildSkillCard(BossSkillSO s) { var card = SkillModule.MakeCard(); SkillModule.AddChip(card, "分类", s.category.ToString()); SkillModule.AddChip(card, "类型", s.skillType.ToString()); SkillModule.AddChip(card, "模式数", (s.attackPatterns?.Length ?? 0).ToString()); SkillModule.AddChip(card, "弱点窗口", (s.vulnerabilityWindows?.Length ?? 0).ToString()); if (!string.IsNullOrEmpty(s.skillId)) SkillModule.AddChip(card, "ID", s.skillId); return card; } private static VisualElement BuildSeqCard(SkillSequenceSO s) { var card = SkillModule.MakeCard(); SkillModule.AddChip(card, "步骤数", (s.steps?.Length ?? 0).ToString()); SkillModule.AddChip(card, "循环", s.RepeatIfPlayerInRange ? "是" : "否"); SkillModule.AddChip(card, "最大循环次数", s.MaxRepeatCount.ToString()); return card; } private VisualElement BuildActionBar(T asset, string folder, SoListPane pane) where T : ScriptableObject { var bar = SkillModule.MakeActionBar(); new Button(() => { EditorGUIUtility.PingObject(asset); Selection.activeObject = asset; }) { text = "定位" }.AlsoAddTo(bar); new Button(() => { var c = AssetOperations.Clone(asset, folder); if (c != null) pane.Refresh(c); }) { text = "克隆..." }.AlsoAddTo(bar); var del = new Button(() => { if (AssetOperations.Delete(asset)) pane.Refresh(null); }) { text = "删除" }; del.style.borderLeftColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); del.style.borderRightColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); del.style.borderTopColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); del.style.borderBottomColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); del.style.borderLeftWidth = 1; del.style.borderRightWidth = 1; del.style.borderTopWidth = 1; del.style.borderBottomWidth = 1; del.style.marginLeft = 8; del.AlsoAddTo(bar); return bar; } } }