From 91a4abfe863708eb6cb9e6ebc88c65d1748dfa7a Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 31 May 2026 21:16:10 -0400 Subject: [PATCH] NEO-112: add QuestDef schemas and prototype quest catalog --- content/quests/prototype_quests.json | 120 ++++++++++++++++++ content/schemas/quest-def.schema.json | 37 ++++++ .../schemas/quest-objective-def.schema.json | 59 +++++++++ content/schemas/quest-step-def.schema.json | 29 +++++ 4 files changed, 245 insertions(+) create mode 100644 content/quests/prototype_quests.json create mode 100644 content/schemas/quest-def.schema.json create mode 100644 content/schemas/quest-objective-def.schema.json create mode 100644 content/schemas/quest-step-def.schema.json diff --git a/content/quests/prototype_quests.json b/content/quests/prototype_quests.json new file mode 100644 index 0000000..bdc3857 --- /dev/null +++ b/content/quests/prototype_quests.json @@ -0,0 +1,120 @@ +{ + "schemaVersion": 1, + "quests": [ + { + "id": "prototype_quest_gather_intro", + "displayName": "Intro: Salvage Run", + "prerequisiteQuestIds": [], + "steps": [ + { + "id": "gather_intro_step_salvage", + "displayName": "Gather scrap metal", + "objectives": [ + { + "id": "gather_intro_obj_scrap", + "kind": "gather_item", + "itemId": "scrap_metal_bulk", + "quantity": 3 + } + ] + } + ] + }, + { + "id": "prototype_quest_refine_intro", + "displayName": "Intro: Refine Stock", + "prerequisiteQuestIds": ["prototype_quest_gather_intro"], + "steps": [ + { + "id": "refine_intro_step_craft", + "displayName": "Refine scrap", + "objectives": [ + { + "id": "refine_intro_obj_recipe", + "kind": "craft_recipe", + "recipeId": "refine_scrap_standard", + "quantity": 1 + } + ] + } + ] + }, + { + "id": "prototype_quest_combat_intro", + "displayName": "Intro: Clear the Pocket", + "prerequisiteQuestIds": [], + "steps": [ + { + "id": "combat_intro_step_encounter", + "displayName": "Clear the combat pocket", + "objectives": [ + { + "id": "combat_intro_obj_encounter", + "kind": "encounter_complete", + "encounterId": "prototype_combat_pocket" + } + ] + } + ] + }, + { + "id": "prototype_quest_operator_chain", + "displayName": "Operator Chain", + "prerequisiteQuestIds": [ + "prototype_quest_gather_intro", + "prototype_quest_refine_intro", + "prototype_quest_combat_intro" + ], + "steps": [ + { + "id": "chain_step_gather", + "displayName": "Bulk salvage", + "objectives": [ + { + "id": "chain_obj_gather", + "kind": "gather_item", + "itemId": "scrap_metal_bulk", + "quantity": 5 + } + ] + }, + { + "id": "chain_step_refine", + "displayName": "Refine stock", + "objectives": [ + { + "id": "chain_obj_refine", + "kind": "craft_recipe", + "recipeId": "refine_scrap_standard", + "quantity": 1 + } + ] + }, + { + "id": "chain_step_stim", + "displayName": "Craft field stim", + "objectives": [ + { + "id": "chain_obj_stim", + "kind": "craft_recipe", + "recipeId": "make_field_stim_mk0", + "quantity": 1 + } + ] + }, + { + "id": "chain_step_token", + "displayName": "Hand off contract token", + "objectives": [ + { + "id": "chain_obj_token", + "kind": "inventory_has_item", + "itemId": "contract_handoff_token", + "quantity": 1 + } + ] + } + ] + } + ] +} diff --git a/content/schemas/quest-def.schema.json b/content/schemas/quest-def.schema.json new file mode 100644 index 0000000..557ca9d --- /dev/null +++ b/content/schemas/quest-def.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://neon-sprawl.local/schemas/quest-def.json", + "title": "QuestDef", + "description": "Single quest row for catalogs (e.g. content/quests/*_quests.json). IDs are stable forever—rename display in displayName only. See docs/decomposition/modules/E7_M1_QuestStateMachine.md.", + "type": "object", + "additionalProperties": false, + "required": ["id", "displayName", "prerequisiteQuestIds", "steps"], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Immutable quest key for accept/progress APIs and telemetry." + }, + "displayName": { + "type": "string", + "minLength": 1, + "description": "Player-facing label; safe to change without migrating character data." + }, + "prerequisiteQuestIds": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$" + }, + "description": "Quest ids that must be completed before accept; may be empty." + }, + "steps": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "https://neon-sprawl.local/schemas/quest-step-def.json" + }, + "description": "Ordered steps; prototype Slice 1 uses sequential advance only." + } + } +} diff --git a/content/schemas/quest-objective-def.schema.json b/content/schemas/quest-objective-def.schema.json new file mode 100644 index 0000000..190c12e --- /dev/null +++ b/content/schemas/quest-objective-def.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://neon-sprawl.local/schemas/quest-objective-def.json", + "title": "QuestObjectiveDef", + "description": "Single objective row on a QuestStepDef (content/quests/*_quests.json). Kinds cover gather, craft, encounter, and inventory checks for E7.M1 prototype Slice 1.", + "type": "object", + "additionalProperties": false, + "required": ["id", "kind"], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Immutable objective key for progress tracking and HTTP projection." + }, + "kind": { + "type": "string", + "enum": ["gather_item", "craft_recipe", "encounter_complete", "inventory_has_item"], + "description": "Objective completion predicate evaluated server-side on success hooks." + }, + "itemId": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Frozen item catalog id (gather_item, inventory_has_item)." + }, + "quantity": { + "type": "integer", + "minimum": 1, + "description": "Required count for gather_item, craft_recipe, or inventory_has_item." + }, + "recipeId": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Frozen recipe catalog id (craft_recipe)." + }, + "encounterId": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Frozen encounter catalog id (encounter_complete)." + } + }, + "oneOf": [ + { + "properties": { "kind": { "const": "gather_item" } }, + "required": ["itemId", "quantity"] + }, + { + "properties": { "kind": { "const": "craft_recipe" } }, + "required": ["recipeId", "quantity"] + }, + { + "properties": { "kind": { "const": "encounter_complete" } }, + "required": ["encounterId"] + }, + { + "properties": { "kind": { "const": "inventory_has_item" } }, + "required": ["itemId", "quantity"] + } + ] +} diff --git a/content/schemas/quest-step-def.schema.json b/content/schemas/quest-step-def.schema.json new file mode 100644 index 0000000..e7ea489 --- /dev/null +++ b/content/schemas/quest-step-def.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://neon-sprawl.local/schemas/quest-step-def.json", + "title": "QuestStepDef", + "description": "Single step row on a QuestDef (content/quests/*_quests.json). Steps advance sequentially in prototype Slice 1.", + "type": "object", + "additionalProperties": false, + "required": ["id", "displayName", "objectives"], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Immutable step key for progress tracking and HTTP projection." + }, + "displayName": { + "type": "string", + "minLength": 1, + "description": "Player-facing label; safe to change without migrating character data." + }, + "objectives": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "https://neon-sprawl.local/schemas/quest-objective-def.json" + }, + "description": "Objectives that must complete before the step advances." + } + } +}