feat: Round 49 narrative systems improvements
QuestManager: extract CheckQuestDepsAndFlags shared method, simplify GetQuestLockInfo/CanAccept/MeetsPrerequisites; add GetQuestsInState+FilterQuests implementations; fix extra brace compile bug; add _pauseTimestamps logging; use actualDelta in ApplyAffinity event. QuestSO: add depth>32 guard to HasPrerequisiteCycle and HasBranchCycle to prevent editor freeze on deep chains. EventChainModule: replace FindObjectOfType with ServiceLocator.GetOrDefault in ForceExecute; add self-trigger flag detection (check 6) in ValidateAllChains using reflection. DialogueVariantPreviewWindow: add matrix analysis section enumerating all 2^N flag combinations (N<=10) with table showing winning variant per combination. WorldStateRegistry: LoadFromSave null guard on data.World sub-collections (P0 fix). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -23,6 +23,7 @@ namespace BaseGames.Editor.Dialogue
|
||||
private ObjectField _targetField;
|
||||
private VisualElement _flagContainer;
|
||||
private VisualElement _resultContainer;
|
||||
private VisualElement _matrixContainer;
|
||||
|
||||
private static readonly Color ColWin = new(0.20f, 0.75f, 0.35f, 1f);
|
||||
private static readonly Color ColFail = new(0.55f, 0.55f, 0.55f, 1f);
|
||||
@@ -106,6 +107,20 @@ namespace BaseGames.Editor.Dialogue
|
||||
_resultContainer = new VisualElement();
|
||||
scrollView.Add(_resultContainer);
|
||||
|
||||
rootVisualElement.Add(MakeDivider());
|
||||
|
||||
// ── 矩阵分析区 ──
|
||||
var matrixFoldout = new Foldout { text = "矩阵分析(所有标志组合 → 胜出变体)", value = false };
|
||||
matrixFoldout.style.marginTop = 4;
|
||||
rootVisualElement.Add(matrixFoldout);
|
||||
|
||||
_matrixContainer = new VisualElement();
|
||||
matrixFoldout.Add(_matrixContainer);
|
||||
|
||||
var matrixBtn = new Button(() => RebuildMatrix()) { text = "矩阵分析" };
|
||||
matrixBtn.style.marginBottom = 4;
|
||||
matrixFoldout.Add(matrixBtn);
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
@@ -364,6 +379,119 @@ namespace BaseGames.Editor.Dialogue
|
||||
public bool HasFlag(string key) => _flags.Contains(key);
|
||||
}
|
||||
|
||||
// ── 矩阵分析 ─────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 枚举全部 2^N 标志组合(N ≤ 10),以表格形式展示每种组合下胜出的变体索引。
|
||||
/// N > 10 时显示提示,建议手动筛选标志后分析。
|
||||
/// </summary>
|
||||
private void RebuildMatrix()
|
||||
{
|
||||
if (_matrixContainer == null) return;
|
||||
_matrixContainer.Clear();
|
||||
|
||||
if (_target == null || _target.variants == null || _target.variants.Length == 0)
|
||||
{
|
||||
_matrixContainer.Add(new Label("(无可分析的变体)") { style = { opacity = 0.5f, fontSize = 11 } });
|
||||
return;
|
||||
}
|
||||
|
||||
var matrixFlags = _allFlags.Count > 0 ? _allFlags : new List<string>();
|
||||
if (matrixFlags.Count == 0)
|
||||
{
|
||||
_matrixContainer.Add(new Label("(变体不使用任何 requiredFlags,无需矩阵分析)") { style = { opacity = 0.5f, fontSize = 11 } });
|
||||
return;
|
||||
}
|
||||
|
||||
const int MaxFlags = 10;
|
||||
if (matrixFlags.Count > MaxFlags)
|
||||
{
|
||||
var warn = new Label($"⚠ 标志数量 ({matrixFlags.Count}) 超过 {MaxFlags},枚举 2^N 组合代价过高。请在上方取消勾选不关心的标志后重新点击「矩阵分析」。");
|
||||
warn.style.fontSize = 11;
|
||||
warn.style.color = new StyleColor(new Color(0.9f, 0.7f, 0.2f));
|
||||
warn.style.whiteSpace = WhiteSpace.Normal;
|
||||
_matrixContainer.Add(warn);
|
||||
return;
|
||||
}
|
||||
|
||||
int n = matrixFlags.Count;
|
||||
int combos = 1 << n; // 2^n
|
||||
|
||||
// ── 表头 ──
|
||||
var headerRow = MakeMatrixRow(isHeader: true);
|
||||
for (int ci = 0; ci < n; ci++)
|
||||
{
|
||||
var cell = MakeMatrixCell(matrixFlags[ci], isHeader: true);
|
||||
cell.style.minWidth = 90;
|
||||
headerRow.Add(cell);
|
||||
}
|
||||
headerRow.Add(MakeMatrixCell("胜出变体", isHeader: true));
|
||||
_matrixContainer.Add(headerRow);
|
||||
|
||||
// ── 数据行 ──
|
||||
for (int mask = 0; mask < combos; mask++)
|
||||
{
|
||||
var combo = new HashSet<string>(System.StringComparer.Ordinal);
|
||||
for (int bit = 0; bit < n; bit++)
|
||||
if ((mask & (1 << bit)) != 0) combo.Add(matrixFlags[bit]);
|
||||
|
||||
// 求胜出变体
|
||||
var mockReader = new MockFlagReader(combo);
|
||||
int winner = -1;
|
||||
for (int vi = 0; vi < _target.variants.Length; vi++)
|
||||
if (_target.CheckVariant(_target.variants[vi], mockReader)) { winner = vi; break; }
|
||||
|
||||
string winnerText = winner >= 0
|
||||
? $"变体 {winner}" +
|
||||
(_target.variants[winner].sequence != null
|
||||
? $"\n({_target.variants[winner].sequence.name})"
|
||||
: "(无序列)")
|
||||
: "默认台词";
|
||||
|
||||
var dataRow = MakeMatrixRow(isHeader: false);
|
||||
// 标志列
|
||||
for (int ci = 0; ci < n; ci++)
|
||||
{
|
||||
bool on = (mask & (1 << ci)) != 0;
|
||||
var cell = MakeMatrixCell(on ? "✓" : "–", isHeader: false);
|
||||
cell.style.color = new StyleColor(on ? ColWin : ColFail);
|
||||
cell.style.minWidth = 90;
|
||||
dataRow.Add(cell);
|
||||
}
|
||||
// 胜出列
|
||||
var winCell = MakeMatrixCell(winnerText, isHeader: false);
|
||||
winCell.style.color = new StyleColor(winner >= 0 ? ColWin : new Color(0.5f, 0.5f, 0.5f));
|
||||
dataRow.Add(winCell);
|
||||
|
||||
_matrixContainer.Add(dataRow);
|
||||
}
|
||||
}
|
||||
|
||||
private static VisualElement MakeMatrixRow(bool isHeader)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.borderBottomWidth = 1;
|
||||
row.style.borderBottomColor = new StyleColor(new Color(0.3f, 0.3f, 0.3f, 0.5f));
|
||||
if (isHeader)
|
||||
row.style.backgroundColor = new StyleColor(new Color(0.22f, 0.22f, 0.28f, 1f));
|
||||
return row;
|
||||
}
|
||||
|
||||
private static Label MakeMatrixCell(string text, bool isHeader)
|
||||
{
|
||||
var lbl = new Label(text);
|
||||
lbl.style.fontSize = isHeader ? 10 : 10;
|
||||
lbl.style.unityFontStyleAndWeight = isHeader ? FontStyle.Bold : FontStyle.Normal;
|
||||
lbl.style.paddingLeft = 4;
|
||||
lbl.style.paddingRight = 4;
|
||||
lbl.style.paddingTop = 3;
|
||||
lbl.style.paddingBottom = 3;
|
||||
lbl.style.whiteSpace = WhiteSpace.Normal;
|
||||
lbl.style.width = 80;
|
||||
return lbl;
|
||||
}
|
||||
|
||||
// ── 辅助 ─────────────────────────────────────────────────────────────
|
||||
|
||||
private static VisualElement MakeDivider()
|
||||
|
||||
Reference in New Issue
Block a user