Scenario conditions in dialogs: option if= hides choices, when moved here from stat checks; command schemas

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cthangoverandClaude Opus 5.5 committed 2026-10-24 10:01:11 +02:00
1 parent dde5262aa0
commit 68ed11cadc
14 files changed
+542 -3

No files matched your search

@@ -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;
/// <summary>
/// A conditional jump in the middle of a dialog:
/// <code>when "stat(cha) &gt;= 5 &amp;&amp; door == 'open'" -> :persuaded [else=refused]</code>
/// 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 <c>else</c> when it does not (or goes on).
/// <para/>
/// It is the RPG Maker "Conditional Branch" without a new syntax: the condition is the
/// same text a scenario header's <c>condition:</c> takes. Several words without quotes
/// are joined back with spaces, so short conditions need no quotes.
/// </summary>
public class WhenCommandStrategy : IScenarioCommandStrategy, IScenarioCommandSpec
{
/// <inheritdoc />
public string Command => "when";
/// <inheritdoc />
public int MinPositional => 1;
/// <inheritdoc />
public IReadOnlyList<string> AllowedNamed => new[] { "else" };
/// <inheritdoc />
public string JumpTargetNamedKey => "else";
/// <inheritdoc />
public void Execute(DialogQueue dlg, List<string> positional, Dictionary<string, string> 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(':');
}
}
+4 -1
View File
@@ -103,7 +103,10 @@ namespace Cthangover.Mods.Dialog.Scenarios
if (pendingSelect.Variants == null)
pendingSelect.Variants = new List<SelectVariant>();
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;
}
+17 -2
View File
@@ -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;
/// <summary>Substitutes variables in the variants and context text, then shows the answer box; the runtime stays paused until a variant is picked.</summary>
/// <summary>
/// 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.
/// </summary>
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));
}
}
@@ -23,6 +23,16 @@ namespace Cthangover.Mods.Dialog.Action.Impls
/// <summary>Target action ID to jump to via <see cref="DialogRuntime.TryGoTo"/> when this variant is selected.</summary>
public string GoTo { get; set; }
/// <summary>
/// Condition of the shared condition language (<c>option "…" if=… -> :label</c>); 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.
/// </summary>
public string Condition { get; set; }
/// <summary>Whether the option is offered now.</summary>
public bool IsShown() => string.IsNullOrWhiteSpace(Condition) || ScenarioCondition.Evaluate(Condition);
/// <summary>Generates a new unique action ID for use as a GoTo target (e.g. with <see cref="DialogQueue.Point"/>).</summary>
public static string Point => Guid.NewGuid().ToString();