Files
TheriapolisV3/Theriapolis.Core/Data/QuestDef.cs
T

178 lines
6.1 KiB
C#
Raw Normal View History

using System.Text.Json.Serialization;
namespace Theriapolis.Core.Data;
/// <summary>
/// Phase 6 M4 — JSON-loaded quest definition. A quest is a directed graph
/// of steps; the engine starts at <see cref="EntryStep"/>, evaluates each
/// active step's <see cref="QuestStepDef.TriggerConditions"/> per tick,
/// and runs <see cref="QuestStepDef.OnEnter"/> + <see cref="QuestStepDef.Outcomes"/>
/// when the step fires.
///
/// Author convention: one tree per file in
/// <c>Content/Data/quests/*.json</c>. <see cref="Id"/> matches the
/// filename. Step ids are strings local to the tree.
/// </summary>
public sealed record QuestDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("title")]
public string Title { get; init; } = "";
[JsonPropertyName("description")]
public string Description { get; init; } = "";
/// <summary>True when the quest doesn't appear in the journal until first activation (Maw discovery, etc.).</summary>
[JsonPropertyName("hidden")]
public bool Hidden { get; init; } = false;
/// <summary>Step id the engine activates on quest start.</summary>
[JsonPropertyName("entry_step")]
public string EntryStep { get; init; } = "";
[JsonPropertyName("steps")]
public QuestStepDef[] Steps { get; init; } = System.Array.Empty<QuestStepDef>();
/// <summary>
/// Optional: triggers that auto-start this quest. The engine checks
/// these against world state on every tick; when any fires, the quest
/// activates at <see cref="EntryStep"/>. Empty = manual-start (e.g.
/// dialogue's <c>start_quest</c> effect).
/// </summary>
[JsonPropertyName("auto_start_when")]
public QuestConditionDef[] AutoStartWhen { get; init; } = System.Array.Empty<QuestConditionDef>();
}
public sealed record QuestStepDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("title")]
public string Title { get; init; } = "";
[JsonPropertyName("description")]
public string Description { get; init; } = "";
/// <summary>Optional waypoint hint — anchor or role tag the player should head toward.</summary>
[JsonPropertyName("waypoint")]
public string Waypoint { get; init; } = "";
/// <summary>Conditions that fire this step's onEnter + outcomes when ALL true.</summary>
[JsonPropertyName("trigger_conditions")]
public QuestConditionDef[] TriggerConditions { get; init; } = System.Array.Empty<QuestConditionDef>();
/// <summary>Effects applied once when the step fires.</summary>
[JsonPropertyName("on_enter")]
public QuestEffectDef[] OnEnter { get; init; } = System.Array.Empty<QuestEffectDef>();
/// <summary>Step ids this step transitions into (any one is selected via outcome conditions).</summary>
[JsonPropertyName("outcomes")]
public QuestOutcomeDef[] Outcomes { get; init; } = System.Array.Empty<QuestOutcomeDef>();
/// <summary>True if reaching this step completes the quest (success).</summary>
[JsonPropertyName("completes_quest")]
public bool CompletesQuest { get; init; } = false;
/// <summary>True if reaching this step fails the quest.</summary>
[JsonPropertyName("fails_quest")]
public bool FailsQuest { get; init; } = false;
}
public sealed record QuestOutcomeDef
{
/// <summary>Step id to transition to. <c>"&lt;end&gt;"</c> closes the quest.</summary>
[JsonPropertyName("next")]
public string Next { get; init; } = "";
/// <summary>Conditions for THIS outcome to be selected. Empty = always.</summary>
[JsonPropertyName("when")]
public QuestConditionDef[] When { get; init; } = System.Array.Empty<QuestConditionDef>();
[JsonPropertyName("effects")]
public QuestEffectDef[] Effects { get; init; } = System.Array.Empty<QuestEffectDef>();
}
/// <summary>Trigger / outcome predicate.</summary>
public sealed record QuestConditionDef
{
/// <summary>
/// One of: "flag_set", "flag_clear", "flag_at_least", "enter_anchor",
/// "enter_role_proximity", "npc_dead", "npc_alive", "time_elapsed_seconds",
/// "rep_at_least", "rep_below", "has_item", "not_has_item",
/// "quest_complete", "quest_active", "dialogue_choice".
/// </summary>
[JsonPropertyName("kind")]
public string Kind { get; init; } = "";
[JsonPropertyName("flag")]
public string Flag { get; init; } = "";
[JsonPropertyName("anchor")]
public string Anchor { get; init; } = "";
[JsonPropertyName("role")]
public string Role { get; init; } = "";
[JsonPropertyName("npc")]
public string Npc { get; init; } = "";
[JsonPropertyName("faction")]
public string Faction { get; init; } = "";
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("quest")]
public string Quest { get; init; } = "";
[JsonPropertyName("value")]
public int Value { get; init; }
[JsonPropertyName("seconds")]
public long Seconds { get; init; }
}
/// <summary>Quest-step side effect.</summary>
public sealed record QuestEffectDef
{
/// <summary>
/// One of: "set_flag", "clear_flag", "give_item", "take_item",
/// "give_xp", "rep_event", "spawn_npc", "despawn_npc",
/// "start_quest", "end_quest", "fail_quest".
/// </summary>
[JsonPropertyName("kind")]
public string Kind { get; init; } = "";
[JsonPropertyName("flag")]
public string Flag { get; init; } = "";
[JsonPropertyName("value")]
public int Value { get; init; } = 1;
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("qty")]
public int Qty { get; init; } = 1;
[JsonPropertyName("xp")]
public int Xp { get; init; }
[JsonPropertyName("event")]
public DialogueRepEventDef? Event { get; init; }
[JsonPropertyName("quest")]
public string Quest { get; init; } = "";
/// <summary>For spawn_npc: resident template id (named takes precedence).</summary>
[JsonPropertyName("template")]
public string Template { get; init; } = "";
/// <summary>For spawn_npc/despawn_npc: which named role tag is being mutated.</summary>
[JsonPropertyName("role")]
public string Role { get; init; } = "";
}