Files
zeling_v2/Assets/_Game/Scripts/Editor/Modules/CharmModule.cs
Joywayer 6eaa83dc71 feat: Round 48 narrative systems improvements
- 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>
2026-05-25 00:05:15 +08:00

213 lines
8.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}