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; } = "";
}