diff --git a/locale/schema_en.properties b/locale/schema_en.properties
new file mode 100644
index 0000000..b83dc49
--- /dev/null
+++ b/locale/schema_en.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = Line
+dialog/schema/builtin/text_desc = A dialog line; the locale key is optional
+dialog/schema/field/text = Text
+dialog/schema/field/locale_key = Locale key
+dialog/schema/builtin/ptext = Narration
+dialog/schema/builtin/ptext_desc = The narrator text, fills ${var}
+dialog/schema/builtin/flag = Story flag
+dialog/schema/builtin/flag_desc = A saved mark that conditions react to
+dialog/schema/field/flag = Flag
+dialog/schema/field/value = Value
+dialog/schema/builtin/switch_scene = Switch scene
+dialog/schema/builtin/switch_scene_desc = A novel scene or a map — a map is a scene too
+dialog/schema/field/scene = Scene
+dialog/schema/builtin/goto = Go to a label
+dialog/schema/builtin/goto_desc = An unconditional jump
+dialog/schema/field/label = Label
+dialog/schema/builtin/end = End the dialog
+dialog/schema/builtin/end_desc = Closes the dialog box
+dialog/schema/checks/when = When — jump
+dialog/schema/checks/when_desc = A conditional jump on the shared condition language: flags, quests, stats, gear
+dialog/schema/checks/arrow_true = When true, go to
+dialog/schema/field/condition = Condition
+dialog/schema/field/else = Else label
+dialog/schema/builtin/select = Choice
+dialog/schema/builtin/select_desc = The question before the answers; option lines follow
+dialog/schema/builtin/option = Answer
+dialog/schema/builtin/option_desc = An answer and the label it leads to; with a condition it is shown only while it holds
+dialog/schema/field/option_if = Show if
+dialog/schema/builtin/option_if_desc = For example archetype(rogue) or "stat(cha) >= 5"; empty — always
diff --git a/locale/schema_es.properties b/locale/schema_es.properties
new file mode 100644
index 0000000..019bb85
--- /dev/null
+++ b/locale/schema_es.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = Réplica
+dialog/schema/builtin/text_desc = Una línea de diálogo; la clave de traducción es opcional
+dialog/schema/field/text = Texto
+dialog/schema/field/locale_key = Clave de traducción
+dialog/schema/builtin/ptext = Narración
+dialog/schema/builtin/ptext_desc = El texto del narrador, rellena ${var}
+dialog/schema/builtin/flag = Marca de historia
+dialog/schema/builtin/flag_desc = Una marca guardada a la que reaccionan las condiciones
+dialog/schema/field/flag = Marca
+dialog/schema/field/value = Valor
+dialog/schema/builtin/switch_scene = Cambiar de escena
+dialog/schema/builtin/switch_scene_desc = Una escena de novela o un mapa: un mapa también es una escena
+dialog/schema/field/scene = Escena
+dialog/schema/builtin/goto = Ir a una etiqueta
+dialog/schema/builtin/goto_desc = Un salto incondicional
+dialog/schema/field/label = Etiqueta
+dialog/schema/builtin/end = Fin del diálogo
+dialog/schema/builtin/end_desc = Cierra el cuadro de diálogo
+dialog/schema/checks/when = Si — saltar
+dialog/schema/checks/when_desc = Un salto condicional según el lenguaje común de condiciones: marcas, misiones, estadísticas, equipo
+dialog/schema/checks/arrow_true = Si es cierto, ir a
+dialog/schema/field/condition = Condición
+dialog/schema/field/else = Etiqueta si no
+dialog/schema/builtin/select = Elección
+dialog/schema/builtin/select_desc = La pregunta antes de las respuestas; siguen las líneas option
+dialog/schema/builtin/option = Respuesta
+dialog/schema/builtin/option_desc = Una respuesta y la etiqueta a la que lleva; con condición solo se muestra mientras se cumple
+dialog/schema/field/option_if = Mostrar si
+dialog/schema/builtin/option_if_desc = Por ejemplo archetype(rogue) o "stat(cha) >= 5"; vacío: siempre
diff --git a/locale/schema_ja.properties b/locale/schema_ja.properties
new file mode 100644
index 0000000..9ad00da
--- /dev/null
+++ b/locale/schema_ja.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = セリフ
+dialog/schema/builtin/text_desc = 会話の一行。翻訳キーは任意
+dialog/schema/field/text = テキスト
+dialog/schema/field/locale_key = 翻訳キー
+dialog/schema/builtin/ptext = 地の文
+dialog/schema/builtin/ptext_desc = 語りの文。${var} を埋め込む
+dialog/schema/builtin/flag = ストーリーフラグ
+dialog/schema/builtin/flag_desc = 保存される印。条件がそれに反応する
+dialog/schema/field/flag = フラグ
+dialog/schema/field/value = 値
+dialog/schema/builtin/switch_scene = シーン切り替え
+dialog/schema/builtin/switch_scene_desc = ノベルのシーンかマップ——マップもシーン
+dialog/schema/field/scene = シーン
+dialog/schema/builtin/goto = ラベルへ移動
+dialog/schema/builtin/goto_desc = 無条件のジャンプ
+dialog/schema/field/label = ラベル
+dialog/schema/builtin/end = 会話の終わり
+dialog/schema/builtin/end_desc = 会話ウィンドウを閉じる
+dialog/schema/checks/when = もし——ジャンプ
+dialog/schema/checks/when_desc = 共通の条件言語による条件ジャンプ:フラグ、クエスト、ステータス、装備
+dialog/schema/checks/arrow_true = 真のときのラベル
+dialog/schema/field/condition = 条件
+dialog/schema/field/else = それ以外のラベル
+dialog/schema/builtin/select = 選択肢
+dialog/schema/builtin/select_desc = 選択肢の前の問い。続けて option 行
+dialog/schema/builtin/option = 選択肢
+dialog/schema/builtin/option_desc = 答えと行き先のラベル。条件付きなら成り立つ間だけ表示
+dialog/schema/field/option_if = 表示条件
+dialog/schema/builtin/option_if_desc = 例:archetype(rogue) や "stat(cha) >= 5"。空なら常に
diff --git a/locale/schema_ko.properties b/locale/schema_ko.properties
new file mode 100644
index 0000000..6691d3f
--- /dev/null
+++ b/locale/schema_ko.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = 대사
+dialog/schema/builtin/text_desc = 대화 한 줄. 번역 키는 선택
+dialog/schema/field/text = 텍스트
+dialog/schema/field/locale_key = 번역 키
+dialog/schema/builtin/ptext = 서술
+dialog/schema/builtin/ptext_desc = 서술자 텍스트. ${var}를 채움
+dialog/schema/builtin/flag = 스토리 플래그
+dialog/schema/builtin/flag_desc = 저장되는 표시. 조건이 반응합니다
+dialog/schema/field/flag = 플래그
+dialog/schema/field/value = 값
+dialog/schema/builtin/switch_scene = 장면 전환
+dialog/schema/builtin/switch_scene_desc = 노벨 장면이나 맵 — 맵도 장면입니다
+dialog/schema/field/scene = 장면
+dialog/schema/builtin/goto = 라벨로 이동
+dialog/schema/builtin/goto_desc = 무조건 이동
+dialog/schema/field/label = 라벨
+dialog/schema/builtin/end = 대화 끝
+dialog/schema/builtin/end_desc = 대화창을 닫음
+dialog/schema/checks/when = 조건 — 이동
+dialog/schema/checks/when_desc = 공통 조건 언어로 하는 조건 이동: 플래그, 퀘스트, 능력치, 장비
+dialog/schema/checks/arrow_true = 참이면 이동
+dialog/schema/field/condition = 조건
+dialog/schema/field/else = 아니면 라벨
+dialog/schema/builtin/select = 선택
+dialog/schema/builtin/select_desc = 답 앞의 질문. 뒤에 option 줄이 옵니다
+dialog/schema/builtin/option = 선택지
+dialog/schema/builtin/option_desc = 답과 이어지는 라벨. 조건이 있으면 참일 때만 보입니다
+dialog/schema/field/option_if = 표시 조건
+dialog/schema/builtin/option_if_desc = 예: archetype(rogue) 또는 "stat(cha) >= 5". 비우면 항상
diff --git a/locale/schema_ru.properties b/locale/schema_ru.properties
new file mode 100644
index 0000000..3e6eb41
--- /dev/null
+++ b/locale/schema_ru.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = Реплика
+dialog/schema/builtin/text_desc = Строка диалога; ключ перевода — по желанию
+dialog/schema/field/text = Текст
+dialog/schema/field/locale_key = Ключ перевода
+dialog/schema/builtin/ptext = Повествование
+dialog/schema/builtin/ptext_desc = Текст рассказчика, подставляет ${var}
+dialog/schema/builtin/flag = Флаг сюжета
+dialog/schema/builtin/flag_desc = Сохраняемая отметка, по ней срабатывают условия
+dialog/schema/field/flag = Флаг
+dialog/schema/field/value = Значение
+dialog/schema/builtin/switch_scene = Сменить сцену
+dialog/schema/builtin/switch_scene_desc = Сцена новеллы или карта — карта тоже сцена
+dialog/schema/field/scene = Сцена
+dialog/schema/builtin/goto = Перейти к метке
+dialog/schema/builtin/goto_desc = Безусловный переход
+dialog/schema/field/label = Метка
+dialog/schema/builtin/end = Конец диалога
+dialog/schema/builtin/end_desc = Закрывает окно диалога
+dialog/schema/checks/when = Если — переход
+dialog/schema/checks/when_desc = Условный переход по общему языку условий: флаги, квесты, статы, снаряжение
+dialog/schema/checks/arrow_true = Если верно — метка
+dialog/schema/field/condition = Условие
+dialog/schema/field/else = Иначе — метка
+dialog/schema/builtin/select = Выбор
+dialog/schema/builtin/select_desc = Вопрос перед вариантами ответа; дальше идут строки option
+dialog/schema/builtin/option = Вариант ответа
+dialog/schema/builtin/option_desc = Вариант выбора и метка, куда он ведёт; с условием — показывается, только пока оно верно
+dialog/schema/field/option_if = Показывать, если
+dialog/schema/builtin/option_if_desc = Например archetype(rogue) или "stat(cha) >= 5"; пусто — всегда
diff --git a/locale/schema_vi.properties b/locale/schema_vi.properties
new file mode 100644
index 0000000..8f934c8
--- /dev/null
+++ b/locale/schema_vi.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = Lời thoại
+dialog/schema/builtin/text_desc = Một dòng hội thoại; khóa dịch tùy chọn
+dialog/schema/field/text = Văn bản
+dialog/schema/field/locale_key = Khóa dịch
+dialog/schema/builtin/ptext = Lời dẫn
+dialog/schema/builtin/ptext_desc = Lời người kể, điền ${var}
+dialog/schema/builtin/flag = Cờ cốt truyện
+dialog/schema/builtin/flag_desc = Một dấu được lưu, điều kiện sẽ phản ứng theo
+dialog/schema/field/flag = Cờ
+dialog/schema/field/value = Giá trị
+dialog/schema/builtin/switch_scene = Chuyển cảnh
+dialog/schema/builtin/switch_scene_desc = Cảnh tiểu thuyết hoặc bản đồ — bản đồ cũng là cảnh
+dialog/schema/field/scene = Cảnh
+dialog/schema/builtin/goto = Đến nhãn
+dialog/schema/builtin/goto_desc = Bước nhảy vô điều kiện
+dialog/schema/field/label = Nhãn
+dialog/schema/builtin/end = Kết thúc hội thoại
+dialog/schema/builtin/end_desc = Đóng hộp hội thoại
+dialog/schema/checks/when = Nếu — nhảy
+dialog/schema/checks/when_desc = Bước nhảy có điều kiện theo ngôn ngữ điều kiện chung: cờ, nhiệm vụ, chỉ số, trang bị
+dialog/schema/checks/arrow_true = Nếu đúng, đến
+dialog/schema/field/condition = Điều kiện
+dialog/schema/field/else = Nhãn nếu không
+dialog/schema/builtin/select = Lựa chọn
+dialog/schema/builtin/select_desc = Câu hỏi trước các đáp án; sau đó là các dòng option
+dialog/schema/builtin/option = Đáp án
+dialog/schema/builtin/option_desc = Một đáp án và nhãn nó dẫn tới; có điều kiện thì chỉ hiện khi điều kiện đúng
+dialog/schema/field/option_if = Hiện nếu
+dialog/schema/builtin/option_if_desc = Ví dụ archetype(rogue) hoặc "stat(cha) >= 5"; để trống — luôn hiện
diff --git a/locale/schema_zh.properties b/locale/schema_zh.properties
new file mode 100644
index 0000000..8f1ca15
--- /dev/null
+++ b/locale/schema_zh.properties
@@ -0,0 +1,29 @@
+dialog/schema/builtin/text = 台词
+dialog/schema/builtin/text_desc = 一句对白;翻译键可选
+dialog/schema/field/text = 文本
+dialog/schema/field/locale_key = 翻译键
+dialog/schema/builtin/ptext = 旁白
+dialog/schema/builtin/ptext_desc = 旁白文本,会代入 ${var}
+dialog/schema/builtin/flag = 剧情标记
+dialog/schema/builtin/flag_desc = 会被保存的标记,条件据此触发
+dialog/schema/field/flag = 标记
+dialog/schema/field/value = 数值
+dialog/schema/builtin/switch_scene = 切换场景
+dialog/schema/builtin/switch_scene_desc = 小说场景或地图——地图也是场景
+dialog/schema/field/scene = 场景
+dialog/schema/builtin/goto = 跳到标签
+dialog/schema/builtin/goto_desc = 无条件跳转
+dialog/schema/field/label = 标签
+dialog/schema/builtin/end = 结束对话
+dialog/schema/builtin/end_desc = 关闭对话框
+dialog/schema/checks/when = 如果——跳转
+dialog/schema/checks/when_desc = 依据通用条件语言的条件跳转:标记、任务、属性、装备
+dialog/schema/checks/arrow_true = 为真时跳转
+dialog/schema/field/condition = 条件
+dialog/schema/field/else = 否则跳转
+dialog/schema/builtin/select = 选择
+dialog/schema/builtin/select_desc = 回答前的问题;后面跟着 option 行
+dialog/schema/builtin/option = 回答选项
+dialog/schema/builtin/option_desc = 一个回答及其跳转标签;带条件时仅在条件成立时显示
+dialog/schema/field/option_if = 显示条件
+dialog/schema/builtin/option_if_desc = 例如 archetype(rogue) 或 "stat(cha) >= 5";留空——总是显示
diff --git a/schemas/dialog_base.json b/schemas/dialog_base.json
new file mode 100644
index 0000000..3da982c
--- /dev/null
+++ b/schemas/dialog_base.json
@@ -0,0 +1,164 @@
+{
+ "Commands": [
+ {
+ "Id": "text",
+ "Name": "dialog/schema/builtin/text",
+ "Description": "dialog/schema/builtin/text_desc",
+ "Category": "dialog",
+ "Positional": [
+ {
+ "Key": "text",
+ "Type": "text",
+ "Required": true,
+ "Name": "dialog/schema/field/text"
+ }
+ ],
+ "Params": [
+ {
+ "Key": "key",
+ "Type": "text",
+ "Name": "dialog/schema/field/locale_key"
+ }
+ ]
+ },
+ {
+ "Id": "ptext",
+ "Name": "dialog/schema/builtin/ptext",
+ "Description": "dialog/schema/builtin/ptext_desc",
+ "Category": "dialog",
+ "Positional": [
+ {
+ "Key": "text",
+ "Type": "text",
+ "Required": true,
+ "Name": "dialog/schema/field/text"
+ }
+ ],
+ "Params": [
+ {
+ "Key": "key",
+ "Type": "text",
+ "Name": "dialog/schema/field/locale_key"
+ }
+ ]
+ },
+ {
+ "Id": "flag",
+ "Name": "dialog/schema/builtin/flag",
+ "Description": "dialog/schema/builtin/flag_desc",
+ "Category": "flow",
+ "Positional": [
+ {
+ "Key": "name",
+ "Type": "flag",
+ "Required": true,
+ "Name": "dialog/schema/field/flag"
+ },
+ {
+ "Key": "value",
+ "Type": "text",
+ "Required": true,
+ "Default": "1",
+ "Name": "dialog/schema/field/value"
+ }
+ ]
+ },
+ {
+ "Id": "switch_scene",
+ "Name": "dialog/schema/builtin/switch_scene",
+ "Description": "dialog/schema/builtin/switch_scene_desc",
+ "Category": "flow",
+ "Positional": [
+ {
+ "Key": "scene",
+ "Type": "scene",
+ "Required": true,
+ "Name": "dialog/schema/field/scene"
+ }
+ ]
+ },
+ {
+ "Id": "goto",
+ "Name": "dialog/schema/builtin/goto",
+ "Description": "dialog/schema/builtin/goto_desc",
+ "Category": "flow",
+ "Arrow": "dialog/schema/field/label"
+ },
+ {
+ "Id": "end",
+ "Name": "dialog/schema/builtin/end",
+ "Description": "dialog/schema/builtin/end_desc",
+ "Category": "flow"
+ },
+ {
+ "Id": "when",
+ "Name": "dialog/schema/checks/when",
+ "Description": "dialog/schema/checks/when_desc",
+ "Category": "flow",
+ "Arrow": "dialog/schema/checks/arrow_true",
+ "Positional": [
+ {
+ "Key": "condition",
+ "Type": "condition",
+ "Required": true,
+ "Name": "dialog/schema/field/condition"
+ }
+ ],
+ "Params": [
+ {
+ "Key": "else",
+ "Type": "label",
+ "Name": "dialog/schema/field/else"
+ }
+ ]
+ },
+ {
+ "Id": "select",
+ "Name": "dialog/schema/builtin/select",
+ "Description": "dialog/schema/builtin/select_desc",
+ "Category": "dialog",
+ "Positional": [
+ {
+ "Key": "text",
+ "Type": "text",
+ "Name": "dialog/schema/field/text"
+ }
+ ],
+ "Params": [
+ {
+ "Key": "key",
+ "Type": "text",
+ "Name": "dialog/schema/field/locale_key"
+ }
+ ]
+ },
+ {
+ "Id": "option",
+ "Name": "dialog/schema/builtin/option",
+ "Description": "dialog/schema/builtin/option_desc",
+ "Category": "dialog",
+ "Arrow": "dialog/schema/field/label",
+ "Positional": [
+ {
+ "Key": "text",
+ "Type": "text",
+ "Required": true,
+ "Name": "dialog/schema/field/text"
+ }
+ ],
+ "Params": [
+ {
+ "Key": "key",
+ "Type": "text",
+ "Name": "dialog/schema/field/locale_key"
+ },
+ {
+ "Key": "if",
+ "Type": "condition",
+ "Name": "dialog/schema/field/option_if",
+ "Description": "dialog/schema/builtin/option_if_desc"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/Scenarios/Impls/WhenCommandStrategy.cs b/src/Scenarios/Impls/WhenCommandStrategy.cs
new file mode 100644
index 0000000..7c2af52
--- /dev/null
+++ b/src/Scenarios/Impls/WhenCommandStrategy.cs
@@ -0,0 +1,43 @@
+namespace Cthangover.Mods.Dialog.Scenarios
+{
+ using System.Collections.Generic;
+ using Cthangover.Core.Localization;
+ using Cthangover.Core.Scenes;
+ using Cthangover.Mods.Dialog;
+
+ ///
+ /// A conditional jump in the middle of a dialog:
+ /// when "stat(cha) >= 5 && door == 'open'" -> :persuaded [else=refused]
+ /// evaluates the shared condition language (flags, quests, stats, equipment, GDScript
+ /// conditions — everything mods registered) at the moment the line runs, and jumps to
+ /// the arrow label when it holds, to else when it does not (or goes on).
+ ///
+ /// It is the RPG Maker "Conditional Branch" without a new syntax: the condition is the
+ /// same text a scenario header's condition: takes. Several words without quotes
+ /// are joined back with spaces, so short conditions need no quotes.
+ ///
+ public class WhenCommandStrategy : IScenarioCommandStrategy, IScenarioCommandSpec
+ {
+ ///
+ public string Command => "when";
+
+ ///
+ public int MinPositional => 1;
+
+ ///
+ public IReadOnlyList AllowedNamed => new[] { "else" };
+
+ ///
+ public string JumpTargetNamedKey => "else";
+
+ ///
+ public void Execute(DialogQueue dlg, List positional, Dictionary named, string arrowTarget, ILocalizationProvider locale)
+ {
+ var condition = string.Join(" ", positional);
+ named.TryGetValue("else", out var otherwise);
+ dlg.IfGoTo(() => ScenarioCondition.Evaluate(condition), Label(arrowTarget), Label(otherwise));
+ }
+
+ private static string Label(string target) => string.IsNullOrWhiteSpace(target) ? null : target.Trim().TrimStart(':');
+ }
+}
diff --git a/src/Scenarios/ScenarioParser.cs b/src/Scenarios/ScenarioParser.cs
index 4044666..0a419d7 100644
--- a/src/Scenarios/ScenarioParser.cs
+++ b/src/Scenarios/ScenarioParser.cs
@@ -103,7 +103,10 @@ namespace Cthangover.Mods.Dialog.Scenarios
if (pendingSelect.Variants == null)
pendingSelect.Variants = new List();
- pendingSelect.Variants.Add(SelectVariant.New(optText, goTo));
+ var variant = SelectVariant.New(optText, goTo);
+ if (named.TryGetValue("if", out var condition))
+ variant.Condition = condition;
+ pendingSelect.Variants.Add(variant);
continue;
}
diff --git a/src/UI/Dialog/Action/Impls/ActionSelect.cs b/src/UI/Dialog/Action/Impls/ActionSelect.cs
index a9b4c92..a669055 100644
--- a/src/UI/Dialog/Action/Impls/ActionSelect.cs
+++ b/src/UI/Dialog/Action/Impls/ActionSelect.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using System.Linq;
+using Cthangover.Core.Utils;
using Cthangover.Mods.Dialog.Action;
using Godot;
using Cthangover.Core.UI;
@@ -35,16 +37,29 @@ namespace Cthangover.Mods.Dialog.Action.Impls
public override WaitType WaitType { get; set; } = WaitType.WaitSelect;
/// Substitutes variables in the variants and context text, then shows the answer box; the runtime stays paused until a variant is picked.
+ ///
+ /// Offers the options whose conditions hold now. When none does, the choice is
+ /// skipped and the dialog goes on with the next line, so a choice of only gated
+ /// options never leaves the player stuck.
+ ///
public override void DoRun(DialogRuntime runtime)
{
var dialogBox = runtime.DialogBox;
if (dialogBox == null)
return;
- foreach (var variant in Variants)
+ var shown = Variants.Where(v => v.IsShown()).ToList();
+ WaitType = shown.Count == 0 ? WaitType.NoWait : WaitType.WaitSelect;
+ if (shown.Count == 0)
+ {
+ GameLogger.Log("DIALOG", "select: no option is shown (all conditions false), going on", LogLevel.Debug);
+ return;
+ }
+
+ foreach (var variant in shown)
variant.Text = runtime.ProcessText(variant.Text);
- dialogBox.SetVariants(Variants);
+ dialogBox.SetVariants(shown);
dialogBox.SetText(runtime.ProcessText(Text));
}
}
diff --git a/src/UI/Dialog/Action/Impls/SelectVariant.cs b/src/UI/Dialog/Action/Impls/SelectVariant.cs
index e8769e9..ec7cbcc 100644
--- a/src/UI/Dialog/Action/Impls/SelectVariant.cs
+++ b/src/UI/Dialog/Action/Impls/SelectVariant.cs
@@ -23,6 +23,16 @@ namespace Cthangover.Mods.Dialog.Action.Impls
/// Target action ID to jump to via when this variant is selected.
public string GoTo { get; set; }
+ ///
+ /// Condition of the shared condition language (option "…" if=… -> :label); the
+ /// option is shown only while it holds. Null = always shown. Evaluated each time the
+ /// choice appears, so a stat, a flag or an item gained a moment ago counts.
+ ///
+ public string Condition { get; set; }
+
+ /// Whether the option is offered now.
+ public bool IsShown() => string.IsNullOrWhiteSpace(Condition) || ScenarioCondition.Evaluate(Condition);
+
/// Generates a new unique action ID for use as a GoTo target (e.g. with ).
public static string Point => Guid.NewGuid().ToString();
diff --git a/tests/OptionConditionTests.cs b/tests/OptionConditionTests.cs
new file mode 100644
index 0000000..4a884a6
--- /dev/null
+++ b/tests/OptionConditionTests.cs
@@ -0,0 +1,59 @@
+namespace Cthangover.Mods.Dialog.Tests
+{
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Cthangover.Core.Mods.Testing;
+ using Cthangover.Mods.Dialog.Action.Impls;
+ using Cthangover.Mods.Dialog.Scenarios;
+
+ ///
+ /// option … if=…: an option is offered only while its condition holds; a choice whose
+ /// options are all hidden is skipped, so the dialog never waits for an answer nobody can give.
+ ///
+ public class OptionConditionTests
+ {
+ [ModTest("options/parser_keeps_the_condition")]
+ static void ParserKeepsTheCondition(ModTestContext ctx)
+ {
+ var dlg = ScenarioParser.Parse("select \"?\"\noption \"A\" -> :a\noption \"B\" \"if=door == 'open' && !false\" -> :b\n:a\nend\n:b\nend\n");
+ var select = dlg.Queue.OfType().Single();
+ ModAssert.Null(select.Variants[0].Condition, "a plain option has no condition");
+ ModAssert.Equal("door == 'open' && !false", select.Variants[1].Condition, "the quoted pair keeps spaces");
+ ModAssert.Equal("b", select.Variants[1].GoTo, "the jump target stays");
+ }
+
+ [ModTest("options/hidden_options_are_not_offered", TimeoutSeconds = 10)]
+ static async Task HiddenOptionsAreNotOffered(ModTestContext ctx)
+ {
+ var stage = await DialogStage.Create();
+ try
+ {
+ var dlg = ScenarioParser.Parse("select \"?\"\noption \"A\" if=false -> :a\noption \"B\" if=true -> :b\noption \"C\" -> :c\n:a\nend\n:b\nend\n:c\nend\n");
+ ModAssert.True(stage.Box.SetDialogQueueAndRun(dlg, null, 0, null), "dialog starts");
+ ModAssert.True(stage.Box.IsWaitingAnswer, "waits for an answer");
+ ModAssert.Equal(2, stage.Box.AnswerVariantCount, "the false option is hidden");
+ }
+ finally
+ {
+ stage.Free();
+ }
+ }
+
+ [ModTest("options/all_hidden_skips_the_choice", TimeoutSeconds = 10)]
+ static async Task AllHiddenSkipsTheChoice(ModTestContext ctx)
+ {
+ var stage = await DialogStage.Create();
+ try
+ {
+ var dlg = ScenarioParser.Parse("select \"?\"\noption \"A\" if=false -> :a\n\nset outcome skipped\nend\n:a\nset outcome a\nend\n");
+ stage.Box.SetDialogQueueAndRun(dlg, null, 0, null);
+ ModAssert.False(stage.Box.IsWaitingAnswer, "no answer is awaited");
+ ModAssert.Equal("skipped", stage.Box.Runtime.GetVariable("outcome"), "the dialog went on past the choice");
+ }
+ finally
+ {
+ stage.Free();
+ }
+ }
+ }
+}
diff --git a/tests/WhenCommandStrategyTests.cs b/tests/WhenCommandStrategyTests.cs
new file mode 100644
index 0000000..ce9384b
--- /dev/null
+++ b/tests/WhenCommandStrategyTests.cs
@@ -0,0 +1,42 @@
+namespace Cthangover.Mods.Dialog.Tests
+{
+ using Cthangover.Core.Mods.Testing;
+ using Cthangover.Core.Scenes;
+ using Cthangover.Mods.Dialog.Scenarios;
+
+ /// when jumps on the shared condition language, evaluated when the line runs.
+ public class WhenCommandStrategyTests
+ {
+ private static string Outcome(string scenario)
+ {
+ var dlg = ScenarioParser.Parse(scenario);
+ var runtime = new DialogRuntime();
+ runtime.SetDialogQueueAndRun(null, dlg.Queue, null);
+ runtime.Run();
+ return runtime.GetVariable("outcome");
+ }
+
+ [ModTest("when/true_false_and_else")]
+ static void TrueFalseAndElse(ModTestContext ctx)
+ {
+ ModAssert.Equal("yes", Outcome("when true -> :yes\nset outcome no\nend\n:yes\nset outcome yes\nend\n"), "true jumps");
+ ModAssert.Equal("no", Outcome("when false -> :yes\nset outcome no\nend\n:yes\nset outcome yes\nend\n"), "false falls through");
+ ModAssert.Equal("other", Outcome("when false -> :yes else=other\nset outcome no\nend\n:yes\nset outcome yes\nend\n:other\nset outcome other\nend\n"), "false takes else");
+ }
+
+ [ModTest("when/reads_state_when_the_line_runs")]
+ static void ReadsStateWhenTheLineRuns(ModTestContext ctx)
+ {
+ ScenarioCondition.SetFlag("test_when_flag", "0");
+ try
+ {
+ const string scenario = "flag test_when_flag 2\nwhen \"test_when_flag >= 2 && !false\" -> :yes\nset outcome no\nend\n:yes\nset outcome yes\nend\n";
+ ModAssert.Equal("yes", Outcome(scenario), "a flag set two lines earlier counts");
+ }
+ finally
+ {
+ ScenarioCondition.SetFlag("test_when_flag", "0");
+ }
+ }
+ }
+}