b451f83174
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
187 lines
6.8 KiB
C#
187 lines
6.8 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace Theriapolis.Core.Data;
|
|
|
|
/// <summary>
|
|
/// Phase 6 M3 — JSON-loaded dialogue tree. Each tree is a directed graph
|
|
/// of nodes; the runner walks from <see cref="Root"/> per option choice.
|
|
/// Nodes are addressed by a string id local to the tree.
|
|
///
|
|
/// Author convention: keep one tree per file in
|
|
/// <c>Content/Data/dialogues/*.json</c>. <see cref="Id"/> matches the
|
|
/// filename (sans extension) so authors can find the file by id.
|
|
/// </summary>
|
|
public sealed record DialogueDef
|
|
{
|
|
[JsonPropertyName("id")]
|
|
public string Id { get; init; } = "";
|
|
|
|
/// <summary>Starting node id when the dialogue opens.</summary>
|
|
[JsonPropertyName("root")]
|
|
public string Root { get; init; } = "";
|
|
|
|
[JsonPropertyName("nodes")]
|
|
public DialogueNodeDef[] Nodes { get; init; } = System.Array.Empty<DialogueNodeDef>();
|
|
}
|
|
|
|
public sealed record DialogueNodeDef
|
|
{
|
|
[JsonPropertyName("id")]
|
|
public string Id { get; init; } = "";
|
|
|
|
/// <summary>"npc" / "pc" / "narration".</summary>
|
|
[JsonPropertyName("speaker")]
|
|
public string Speaker { get; init; } = "npc";
|
|
|
|
/// <summary>Display text. Supports placeholders {pc.name}, {npc.role}, {disposition_label}.</summary>
|
|
[JsonPropertyName("text")]
|
|
public string Text { get; init; } = "";
|
|
|
|
/// <summary>Effects applied automatically when the runner enters this node.</summary>
|
|
[JsonPropertyName("on_enter")]
|
|
public DialogueEffectDef[] OnEnter { get; init; } = System.Array.Empty<DialogueEffectDef>();
|
|
|
|
[JsonPropertyName("options")]
|
|
public DialogueOptionDef[] Options { get; init; } = System.Array.Empty<DialogueOptionDef>();
|
|
}
|
|
|
|
public sealed record DialogueOptionDef
|
|
{
|
|
[JsonPropertyName("text")]
|
|
public string Text { get; init; } = "";
|
|
|
|
/// <summary>Visibility predicates. Option is hidden if any condition fails.</summary>
|
|
[JsonPropertyName("conditions")]
|
|
public DialogueConditionDef[] Conditions { get; init; } = System.Array.Empty<DialogueConditionDef>();
|
|
|
|
/// <summary>
|
|
/// When set, selecting this option rolls the named skill against
|
|
/// <see cref="DialogueSkillCheckDef.Dc"/>. The runner branches into
|
|
/// <see cref="EffectsOnSuccess"/>+<see cref="NextOnSuccess"/> on success
|
|
/// or <see cref="EffectsOnFailure"/>+<see cref="NextOnFailure"/> on
|
|
/// failure. <see cref="Effects"/> and <see cref="Next"/> are ignored
|
|
/// when a skill check is present.
|
|
/// </summary>
|
|
[JsonPropertyName("skill_check")]
|
|
public DialogueSkillCheckDef? SkillCheck { get; init; }
|
|
|
|
/// <summary>Node id to jump to when this option is selected. Empty / "<end>" closes the dialogue.</summary>
|
|
[JsonPropertyName("next")]
|
|
public string Next { get; init; } = "";
|
|
|
|
[JsonPropertyName("effects")]
|
|
public DialogueEffectDef[] Effects { get; init; } = System.Array.Empty<DialogueEffectDef>();
|
|
|
|
[JsonPropertyName("next_on_success")]
|
|
public string NextOnSuccess { get; init; } = "";
|
|
|
|
[JsonPropertyName("next_on_failure")]
|
|
public string NextOnFailure { get; init; } = "";
|
|
|
|
[JsonPropertyName("effects_on_success")]
|
|
public DialogueEffectDef[] EffectsOnSuccess { get; init; } = System.Array.Empty<DialogueEffectDef>();
|
|
|
|
[JsonPropertyName("effects_on_failure")]
|
|
public DialogueEffectDef[] EffectsOnFailure { get; init; } = System.Array.Empty<DialogueEffectDef>();
|
|
}
|
|
|
|
public sealed record DialogueSkillCheckDef
|
|
{
|
|
/// <summary>Skill id (snake_case, matches SkillId.FromJson — e.g. "intimidation", "persuasion").</summary>
|
|
[JsonPropertyName("skill")]
|
|
public string Skill { get; init; } = "";
|
|
|
|
[JsonPropertyName("dc")]
|
|
public int Dc { get; init; }
|
|
}
|
|
|
|
/// <summary>Visibility predicate evaluated when the option is offered.</summary>
|
|
public sealed record DialogueConditionDef
|
|
{
|
|
/// <summary>
|
|
/// One of: "rep_at_least", "rep_below", "has_item", "not_has_item",
|
|
/// "has_flag", "not_has_flag", "ability_min".
|
|
/// </summary>
|
|
[JsonPropertyName("kind")]
|
|
public string Kind { get; init; } = "";
|
|
|
|
/// <summary>Faction id (rep_at_least / rep_below). Empty = effective disposition vs current NPC.</summary>
|
|
[JsonPropertyName("faction")]
|
|
public string Faction { get; init; } = "";
|
|
|
|
/// <summary>Item id (has_item / not_has_item).</summary>
|
|
[JsonPropertyName("id")]
|
|
public string Id { get; init; } = "";
|
|
|
|
/// <summary>Flag id (has_flag / not_has_flag).</summary>
|
|
[JsonPropertyName("flag")]
|
|
public string Flag { get; init; } = "";
|
|
|
|
/// <summary>Ability id (ability_min): "STR" / "DEX" / "CON" / "INT" / "WIS" / "CHA".</summary>
|
|
[JsonPropertyName("ability")]
|
|
public string Ability { get; init; } = "";
|
|
|
|
/// <summary>Numeric threshold for rep / ability. Inclusive lower bound for *_at_least and *_min.</summary>
|
|
[JsonPropertyName("value")]
|
|
public int Value { get; init; }
|
|
}
|
|
|
|
/// <summary>Side effect applied on option selection (or on node enter).</summary>
|
|
public sealed record DialogueEffectDef
|
|
{
|
|
/// <summary>
|
|
/// One of: "set_flag", "clear_flag", "give_item", "take_item",
|
|
/// "rep_event", "open_shop", "start_quest", "give_xp".
|
|
/// </summary>
|
|
[JsonPropertyName("kind")]
|
|
public string Kind { get; init; } = "";
|
|
|
|
/// <summary>Flag id for set_flag/clear_flag.</summary>
|
|
[JsonPropertyName("flag")]
|
|
public string Flag { get; init; } = "";
|
|
|
|
/// <summary>Integer value for set_flag (defaults to 1).</summary>
|
|
[JsonPropertyName("value")]
|
|
public int Value { get; init; } = 1;
|
|
|
|
/// <summary>Item id for give_item/take_item.</summary>
|
|
[JsonPropertyName("id")]
|
|
public string Id { get; init; } = "";
|
|
|
|
/// <summary>Quantity for give_item/take_item (defaults to 1).</summary>
|
|
[JsonPropertyName("qty")]
|
|
public int Qty { get; init; } = 1;
|
|
|
|
/// <summary>RepEvent payload for rep_event.</summary>
|
|
[JsonPropertyName("event")]
|
|
public DialogueRepEventDef? Event { get; init; }
|
|
|
|
/// <summary>Quest id for start_quest. Phase 6 M3 ignores; M4 wires the quest engine.</summary>
|
|
[JsonPropertyName("quest")]
|
|
public string Quest { get; init; } = "";
|
|
|
|
/// <summary>XP magnitude for give_xp.</summary>
|
|
[JsonPropertyName("xp")]
|
|
public int Xp { get; init; }
|
|
}
|
|
|
|
public sealed record DialogueRepEventDef
|
|
{
|
|
/// <summary>RepEventKind name: "Dialogue" / "Quest" / "Combat" / "Rescue" / "Betrayal" / "Gift" / "Trade" / "Aid" / "Crime" / "Misc".</summary>
|
|
[JsonPropertyName("kind")]
|
|
public string Kind { get; init; } = "Dialogue";
|
|
|
|
[JsonPropertyName("magnitude")]
|
|
public int Magnitude { get; init; }
|
|
|
|
[JsonPropertyName("faction")]
|
|
public string Faction { get; init; } = "";
|
|
|
|
/// <summary>If empty, defaults to the current NPC's role tag.</summary>
|
|
[JsonPropertyName("role_tag")]
|
|
public string RoleTag { get; init; } = "";
|
|
|
|
[JsonPropertyName("note")]
|
|
public string Note { get; init; } = "";
|
|
}
|