using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Ichni.Story
{
///
/// Block 在故事树中的状态:已锁定、当前可交互、已完成。
///
public enum StoryBlockState
{
Locked,
Current,
Completed
}
///
/// Block 的类型,决定点击后触发的行为。
///
public enum StoryBlockType
{
Text,
Song,
Tutorial
}
///
/// 变量条件的比较方式。
///
public enum VariableComparison
{
Equal,
NotEqual,
Greater,
GreaterOrEqual,
Less,
LessOrEqual
}
///
/// 故事树中单个 block 的完整数据定义,由 持有。
///
[InlineProperty]
[Serializable]
public class StoryBlockDefinition
{
[FoldoutGroup("$blockId", false)]
[LabelText("Block ID")]
public string blockId;
[FoldoutGroup("$blockId")]
[LabelText("Type")]
public StoryBlockType blockType;
[FoldoutGroup("$blockId")]
[LabelText("Grid Column")]
[Tooltip("故事树网格中的列:向右为正,(0,0) 位于容器左侧中部。可为负数与小数(用于微调)。")]
public float gridColumn;
[FoldoutGroup("$blockId")]
[LabelText("Grid Row")]
[Tooltip("故事树网格中的行:向下为正、向上为负,0 对应垂直中线。可为小数(用于微调)。")]
public float gridRow;
[FoldoutGroup("$blockId")]
[LabelText("Next Block IDs")]
public List nextBlockIds = new List();
[FoldoutGroup("$blockId")]
[LabelText("Unlock Condition")]
public UnlockCondition unlockCondition = new UnlockCondition();
// ── Text Block ──────────────────────────────────────────────────────────
[FoldoutGroup("$blockId")]
[ShowIf("blockType", StoryBlockType.Text)]
[LabelText("Yarn Node Name")]
public string yarnNodeName;
[FoldoutGroup("$blockId")]
[ShowIf("blockType", StoryBlockType.Text)]
[LabelText("Title Key (Localization)")]
public string titleKey;
// ── Song Block ──────────────────────────────────────────────────────────
[FoldoutGroup("$blockId")]
[ShowIf("blockType", StoryBlockType.Song)]
[LabelText("Song Name")]
public string songName;
// ── Tutorial Block ──────────────────────────────────────────────────────
[FoldoutGroup("$blockId")]
[ShowIf("blockType", StoryBlockType.Tutorial)]
[LabelText("Tutorial Name")]
public string tutorialName;
///
/// 预览 / 调试用的显示标题:按类型取对应字段,缺省时回退到 blockId。
///
public string GetDisplayTitle()
{
string title = blockType switch
{
StoryBlockType.Text => titleKey,
StoryBlockType.Song => songName,
StoryBlockType.Tutorial => tutorialName,
_ => null
};
return string.IsNullOrEmpty(title) ? blockId : title;
}
}
///
/// Block 的解锁条件。以一棵可组合的条件树()表达,
/// 支持"与 / 或 / 非 + 叶子条件"的任意嵌套。根节点为空时视为无条件解锁(始终满足)。
///
[InlineProperty]
[Serializable]
public class UnlockCondition
{
[HideLabel]
[SerializeReference]
[InfoBox("留空表示无条件解锁(章节起始即可用)。可选择 All Of(AND) / Any Of(OR) / Not 复合节点进行任意嵌套。")]
public StoryConditionNode root;
///
/// 检查解锁条件是否满足。根节点为空视为满足。
///
/// 按变量名返回其整型值的委托。
/// 按 blockId 判断该 block 是否已完成的委托。
public bool IsSatisfied(Func getVariable, Func isBlockCompleted)
{
return root == null || root.Evaluate(getVariable, isBlockCompleted);
}
}
}