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