- QuestSO: Add ValidateBranchCycles() DFS detection for branches[].nextQuest loop - QuestSO: Mark three legacy prerequisite fields with v2.0 removal warning in Tooltip - IQuestManager: Add QuestLockReason enum + QuestLockInfo struct (strongly-typed lock info) - IQuestManager: Add GetQuestLockInfo() method to interface; GetQuestLockReason() now delegates to it - IQuestEventSource: Add OnQuestStateChanged(questId, oldState, newState) unified event - QuestManager: Implement GetQuestLockInfo(); fire OnQuestStateChanged on all state transitions - DialogueManager: Add one-frame yield in HandleChoices before ShowChoices (skip-debounce fix) - DialogueManager: Increment _playbackId in ForceEnd() to invalidate residual choice callbacks - DialogueSequenceSO: Add UNITY_EDITOR debug log in TryGetActiveVariant on variant match - WorldStateRegistry: Add OnBatchStateChanged event + BatchMark() batch-write API - DialogueModule: List badge shows warning indicator for unconditional-shadowing variants - DialogueModule: BuildVariantsCard shows logic mode (AND/OR) alongside flag conditions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
213 lines
8.8 KiB
C#
213 lines
8.8 KiB
C#
using System;
|
||
using UnityEditor;
|
||
using UnityEditor.UIElements;
|
||
using UnityEngine;
|
||
using UnityEngine.UIElements;
|
||
using BaseGames.Equipment;
|
||
|
||
namespace BaseGames.Editor.Modules
|
||
{
|
||
/// <summary>
|
||
/// DataHub 护符模块 —— Tab 切换管理 CharmCatalogSO(目录)和 CharmSO(护符)资产。
|
||
/// </summary>
|
||
public class CharmModule : IDataModule, IDataModuleOrdered
|
||
{
|
||
private const string CharmFolder = "Assets/_Game/Data/Progression/Charms";
|
||
private const string CatalogPrefix = "CHM_Catalog";
|
||
private const string CharmPrefix = "CHM_";
|
||
|
||
public string ModuleId => "charm";
|
||
public string DisplayName => "护符";
|
||
public string IconName => null;
|
||
public int DisplayOrder => 60;
|
||
|
||
private int _activeTab = 0; // 0 = 目录, 1 = 护符
|
||
|
||
private SoListPane<CharmCatalogSO> _catalogPane;
|
||
private SoListPane<CharmSO> _charmPane;
|
||
private Action<UnityEngine.Object> _onSelected;
|
||
|
||
private DetailHeader _header;
|
||
private CharmCatalogSO _selectedCatalog;
|
||
private CharmSO _selectedCharm;
|
||
|
||
public void Initialize()
|
||
{
|
||
_catalogPane = new SoListPane<CharmCatalogSO>(CharmFolder, CatalogPrefix);
|
||
_catalogPane.SelectionChanged = s => { _selectedCatalog = s; _onSelected?.Invoke(s); };
|
||
|
||
_charmPane = new SoListPane<CharmSO>(CharmFolder, CharmPrefix,
|
||
c => $"{c.notchCost}格");
|
||
_charmPane.SelectionChanged = s => { _selectedCharm = s; _onSelected?.Invoke(s); };
|
||
}
|
||
|
||
public void BuildListPane(VisualElement container, Action<UnityEngine.Object> 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 btnCatalog = BuildTabBtn("目录", 0, tabBar);
|
||
var btnCharms = BuildTabBtn("护符", 1, tabBar);
|
||
|
||
var listArea = new VisualElement();
|
||
listArea.style.flexGrow = 1;
|
||
container.Add(listArea);
|
||
|
||
var tabs = new[] { btnCatalog, btnCharms };
|
||
ShowTab(0, listArea, tabs);
|
||
btnCatalog.clicked += () => ShowTab(0, listArea, tabs);
|
||
btnCharms.clicked += () => ShowTab(1, listArea, tabs);
|
||
|
||
_catalogPane.Refresh();
|
||
_charmPane.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 CharmCatalogSO catalog)
|
||
{
|
||
container.Add(BuildCatalogActionBar(catalog));
|
||
container.Add(SkillModule.MakeDivider());
|
||
container.Add(new InspectorElement(catalog));
|
||
}
|
||
else if (selected is CharmSO charm)
|
||
{
|
||
container.Add(BuildCharmCard(charm));
|
||
container.Add(BuildCharmActionBar(charm));
|
||
container.Add(SkillModule.MakeDivider());
|
||
container.Add(new InspectorElement(charm));
|
||
}
|
||
}
|
||
|
||
public void OnActivated()
|
||
{
|
||
_catalogPane?.Refresh();
|
||
_charmPane?.Refresh();
|
||
}
|
||
|
||
// ── Tab UI ────────────────────────────────────────────────────────────
|
||
|
||
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) { _catalogPane.style.flexGrow = 1; area.Add(_catalogPane); }
|
||
else { _charmPane.style.flexGrow = 1; area.Add(_charmPane); }
|
||
}
|
||
|
||
// ── 重命名 ────────────────────────────────────────────────────────────
|
||
|
||
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) _catalogPane.Invalidate();
|
||
else _charmPane.Invalidate();
|
||
}
|
||
}
|
||
|
||
// ── CharmCatalogSO 详情 ───────────────────────────────────────────────
|
||
|
||
private VisualElement BuildCatalogActionBar(CharmCatalogSO catalog)
|
||
{
|
||
var bar = SkillModule.MakeActionBar();
|
||
new Button(() => { EditorGUIUtility.PingObject(catalog); Selection.activeObject = catalog; })
|
||
{ text = "定位" }.AlsoAddTo(bar);
|
||
return bar;
|
||
}
|
||
|
||
// ── CharmSO 详情 ──────────────────────────────────────────────────────
|
||
|
||
private static VisualElement BuildCharmCard(CharmSO c)
|
||
{
|
||
var card = SkillModule.MakeCard();
|
||
SkillModule.AddChip(card, "ID", string.IsNullOrEmpty(c.charmId) ? "-" : c.charmId);
|
||
SkillModule.AddChip(card, "格数", $"{c.notchCost}");
|
||
SkillModule.AddChip(card, "效果数", $"{c.effects?.Count ?? 0}");
|
||
if (c.isUnique)
|
||
SkillModule.AddChip(card, "限定", "唯一");
|
||
return card;
|
||
}
|
||
|
||
private VisualElement BuildCharmActionBar(CharmSO charm)
|
||
{
|
||
var bar = SkillModule.MakeActionBar();
|
||
new Button(() => { EditorGUIUtility.PingObject(charm); Selection.activeObject = charm; })
|
||
{ text = "定位" }.AlsoAddTo(bar);
|
||
new Button(() => { var c = AssetOperations.Clone(charm, CharmFolder); if (c != null) _charmPane.Refresh(c); })
|
||
{ text = "克隆..." }.AlsoAddTo(bar);
|
||
var del = new Button(() => { if (AssetOperations.Delete(charm)) _charmPane.Refresh(null); }) { text = "删除" };
|
||
ApplyDeleteStyle(del);
|
||
del.AlsoAddTo(bar);
|
||
return bar;
|
||
}
|
||
|
||
// ── 共用 ─────────────────────────────────────────────────────────────
|
||
|
||
private static void ApplyDeleteStyle(Button btn)
|
||
{
|
||
var c = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f));
|
||
btn.style.borderLeftColor = c;
|
||
btn.style.borderRightColor = c;
|
||
btn.style.borderTopColor = c;
|
||
btn.style.borderBottomColor = c;
|
||
btn.style.borderLeftWidth = 1;
|
||
btn.style.borderRightWidth = 1;
|
||
btn.style.borderTopWidth = 1;
|
||
btn.style.borderBottomWidth = 1;
|
||
btn.style.marginLeft = 8;
|
||
}
|
||
}
|
||
}
|