diff --git a/content/_schema/dialogue-pool.schema.json b/content/_schema/dialogue-pool.schema.json new file mode 100644 index 000000000..da764d30d --- /dev/null +++ b/content/_schema/dialogue-pool.schema.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dialogue-pool.schema.json", + "title": "Dialogue Line Pool", + "description": "Tagged dialogue lines scoped by location + role (D-028, D-035).", + "type": "object", + "required": ["location", "role", "lines"], + "additionalProperties": false, + "properties": { + "location": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "Location slug this dialogue pool belongs to" + }, + "role": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "Template role slug (e.g. dock-worker, bar-owner)" + }, + "lines": { + "type": "array", + "items": { "$ref": "#/$defs/dialogue_line" }, + "minItems": 1 + } + }, + "$defs": { + "dialogue_line": { + "type": "object", + "required": ["id", "text", "role", "access", "trust", "situation"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*_d_[0-9]{3}$", + "description": "Stable line ID: {location_slug}_d_{###}" + }, + "text": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "Template role this line belongs to" + }, + "access": { + "type": "array", + "items": { + "type": "string", + "enum": ["public", "insider", "authority", "peer", "hostile"] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Access tiers this line is available at (multi-tier eligibility)" + }, + "trust": { + "type": "string", + "enum": ["surface", "real", "secret"], + "description": "Minimum trust level required" + }, + "situation": { + "type": "string", + "enum": [ + "arrival", "shift_start", "shift_end", "shift_transition", + "bar_evening", "night_shift", "investigation", "confrontation", + "social", "alone", "emergency", "routine", "observation" + ], + "description": "Situation context when this line can fire" + }, + "topic": { + "type": "string", + "enum": [ + "colleague", "routine", "cargo", "money", "trust", + "danger", "institution", "personal", "investigation" + ], + "description": "Topic tag for selection weighting" + }, + "mood": { + "type": "string", + "enum": [ + "fond", "comfortable", "worried", "suspicious", + "analytical", "conflicted", "concerned", "relieved" + ], + "description": "Mood tag for selection weighting" + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "Freeform tags for additional filtering" + }, + "knowledge_grant": { + "type": "object", + "description": "Knowledge the player gains from hearing this line", + "properties": { + "fact_id": { "type": "string" }, + "confidence": { + "type": "string", + "enum": ["suspects", "knows_of", "knows_details", "direct"] + } + }, + "required": ["fact_id", "confidence"] + } + } + } + } +} diff --git a/content/_schema/district.schema.json b/content/_schema/district.schema.json new file mode 100644 index 000000000..564540542 --- /dev/null +++ b/content/_schema/district.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "district.schema.json", + "title": "District Metadata", + "description": "District definition file — one per district directory (D-036).", + "type": "object", + "required": ["canonical_id", "display_name", "system", "station", "district", "description", "locations", "npc_count"], + "additionalProperties": false, + "properties": { + "canonical_id": { + "type": "string", + "pattern": "^[a-z]+\\.[a-z]+\\.[a-z-]+$", + "description": "Canonical ID in format {system}.{station}.{district}" + }, + "display_name": { + "type": "string", + "minLength": 1 + }, + "system": { + "type": "string", + "pattern": "^[a-z]+$" + }, + "station": { + "type": "string", + "pattern": "^[a-z]+$" + }, + "district": { + "type": "string", + "pattern": "^[a-z-]+$" + }, + "description": { + "type": "string", + "minLength": 1 + }, + "locations": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$" + }, + "minItems": 1, + "uniqueItems": true + }, + "npc_count": { + "type": "integer", + "minimum": 0 + } + } +} diff --git a/content/_schema/fact-catalog.schema.json b/content/_schema/fact-catalog.schema.json new file mode 100644 index 000000000..e1a6063f0 --- /dev/null +++ b/content/_schema/fact-catalog.schema.json @@ -0,0 +1,73 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "fact-catalog.schema.json", + "title": "Fact Catalog", + "description": "Fact definitions — one file per fact category in global/knowledge/ (D-035).", + "type": "object", + "required": ["category", "facts"], + "additionalProperties": false, + "properties": { + "category": { + "type": "string", + "description": "Fact category matching the filename" + }, + "facts": { + "type": "array", + "items": { "$ref": "#/$defs/fact" }, + "minItems": 1 + } + }, + "$defs": { + "fact": { + "type": "object", + "required": ["fact_id", "description", "discoverable_by"], + "additionalProperties": false, + "properties": { + "fact_id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_-]*$", + "description": "Stable unique fact identifier" + }, + "description": { + "type": "string", + "minLength": 1 + }, + "discoverable_by": { + "type": "array", + "items": { + "type": "string", + "enum": ["smuggler", "detective"] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Which playable characters can discover this fact" + }, + "abstract": { + "type": "boolean", + "default": false, + "description": "If true, fact cannot reach Direct confidence (only inferred)" + }, + "progression": { + "type": "array", + "items": { "$ref": "#/$defs/confidence_level" }, + "description": "Confidence level descriptions in ascending order" + } + } + }, + "confidence_level": { + "type": "object", + "required": ["confidence", "text"], + "additionalProperties": false, + "properties": { + "confidence": { + "type": "string", + "enum": ["suspects", "knows_of", "knows_details", "direct"] + }, + "text": { + "type": "string", + "description": "Player-facing description at this confidence level" + } + } + } + } +} diff --git a/content/_schema/location.schema.json b/content/_schema/location.schema.json new file mode 100644 index 000000000..77362c9b9 --- /dev/null +++ b/content/_schema/location.schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "location.schema.json", + "title": "Location Definition", + "description": "Location metadata — one file per location (D-025, D-036).", + "type": "object", + "required": ["canonical_id", "display_name"], + "additionalProperties": false, + "properties": { + "canonical_id": { + "type": "string", + "pattern": "^[a-z]+\\.[a-z]+\\.[a-z-]+\\.location\\.[a-z][a-z0-9-]*$", + "description": "Full canonical ID: {system}.{station}.{district}.location.{slug}" + }, + "display_name": { + "type": "string", + "minLength": 1 + }, + "description": { + "type": "string" + }, + "tile_bounds": { + "type": "object", + "description": "Rectangular tile bounds for this location", + "properties": { + "x_min": { "type": "integer" }, + "y_min": { "type": "integer" }, + "x_max": { "type": "integer" }, + "y_max": { "type": "integer" }, + "z": { "type": "integer" } + }, + "required": ["x_min", "y_min", "x_max", "y_max", "z"] + }, + "sightlines": { + "type": "object", + "description": "Sightline properties for LOS computation", + "properties": { + "open": { + "type": "boolean", + "description": "True if the location is open-plan (no internal walls)" + }, + "notes": { "type": "string" } + } + }, + "ambient_sound": { + "type": "string", + "description": "Reference to ambient sound asset" + }, + "social_site": { + "type": "string", + "description": "Social site template this location belongs to" + } + } +} diff --git a/content/_schema/monologue-pool.schema.json b/content/_schema/monologue-pool.schema.json new file mode 100644 index 000000000..e266b5d32 --- /dev/null +++ b/content/_schema/monologue-pool.schema.json @@ -0,0 +1,117 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "monologue-pool.schema.json", + "title": "Monologue Line Pool", + "description": "Tagged monologue lines, hard-partitioned by character (D-032, D-035).", + "type": "object", + "required": ["character", "location", "lines"], + "additionalProperties": false, + "properties": { + "character": { + "type": "string", + "enum": ["smuggler", "detective"], + "description": "Playable character this pool belongs to (hard partition per D-032)" + }, + "location": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "Location slug, or 'general' for location-independent lines" + }, + "lines": { + "type": "array", + "items": { "$ref": "#/$defs/monologue_line" }, + "minItems": 1 + } + }, + "$defs": { + "monologue_line": { + "type": "object", + "required": ["id", "text", "trigger"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*_m_[sd]_[0-9]{3}$", + "description": "Stable line ID: {location_slug}_m_{s|d}_{###}" + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 160, + "description": "Line text — 160 char max per D-059" + }, + "trigger": { + "type": "string", + "enum": [ + "enter_location", "observe_npc", "hear_sound", + "observe_anomaly", "post_conversation", "discover_evidence", + "witness_interaction", "time_idle", "return_visit" + ], + "description": "What causes this line to fire" + }, + "prerequisites": { + "type": "object", + "description": "AND-only prerequisite conditions", + "properties": { + "facts": { + "type": "array", + "items": { "$ref": "#/$defs/fact_prerequisite" } + }, + "entity_attributes": { + "type": "array", + "items": { "$ref": "#/$defs/attribute_prerequisite" } + }, + "relationship": { + "type": "object", + "properties": { + "target": { "type": "string" }, + "state": { + "type": "string", + "enum": ["unknown", "known", "friendly", "person_of_interest", "hostile"] + } + } + } + } + }, + "priority": { + "type": "integer", + "minimum": 0, + "maximum": 10, + "default": 5, + "description": "Selection priority (higher = more likely to fire)" + }, + "cooldown": { + "type": "integer", + "minimum": 0, + "description": "Minimum ticks before this line can fire again" + }, + "tags": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "fact_prerequisite": { + "type": "object", + "required": ["fact_id", "min_confidence"], + "additionalProperties": false, + "properties": { + "fact_id": { "type": "string" }, + "min_confidence": { + "type": "string", + "enum": ["suspects", "knows_of", "knows_details", "direct"] + } + } + }, + "attribute_prerequisite": { + "type": "object", + "required": ["entity", "key", "value"], + "additionalProperties": false, + "properties": { + "entity": { "type": "string" }, + "key": { "type": "string" }, + "value": { "type": "string" } + } + } + } +} diff --git a/content/_schema/npc-profile.schema.json b/content/_schema/npc-profile.schema.json new file mode 100644 index 000000000..584135461 --- /dev/null +++ b/content/_schema/npc-profile.schema.json @@ -0,0 +1,209 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "npc-profile.schema.json", + "title": "NPC Profile", + "description": "10-axis NPC model profile — one file per NPC (D-024, D-034).", + "type": "object", + "required": ["canonical_id", "display_name", "tier", "pattern", "motivation"], + "additionalProperties": false, + "properties": { + "canonical_id": { + "type": "string", + "pattern": "^npc:[a-z][a-z0-9-]*$", + "description": "Short-form canonical ID: npc:{slug}" + }, + "display_name": { + "type": "string", + "minLength": 1 + }, + "tier": { + "type": "integer", + "enum": [1, 2, 3], + "description": "NPC tier: 1 = conspiracy (full depth), 2 = template (social context), 3 = filler (atmosphere)" + }, + "pattern": { + "type": "string", + "enum": ["FRIEND", "MIRROR", "ANCHOR", "GHOST", "CATALYST", "THRESHOLD", "REMNANT", "SYSTEM", "NOBODY"], + "description": "Thematic pattern — System A (D-050)" + }, + "motivation": { + "type": "string", + "enum": ["HANDLER", "WITNESS", "TURNCOAT", "CIVILIAN", "OPERATOR", "SKEPTIC"], + "description": "Functional motivation — System B (D-050)" + }, + "description": { + "type": "string" + }, + "want": { + "type": "object", + "description": "Primary want/need driving this NPC", + "properties": { + "primary": { "type": "string" }, + "intensity": { "type": "integer", "minimum": 0, "maximum": 10 }, + "description": { "type": "string" } + }, + "required": ["primary"] + }, + "secret": { + "type": "string", + "description": "The NPC's hidden truth — what they don't want the player to know" + }, + "relationships": { + "type": "array", + "items": { "$ref": "#/$defs/relationship" }, + "description": "Named relationships to other NPCs" + }, + "tolerance": { + "type": "object", + "description": "Stress tolerance threshold", + "properties": { + "threshold": { "type": "integer" }, + "description": { "type": "string" } + } + }, + "routine": { + "type": "object", + "description": "Summary of daily routine (full schedule in routines/schedules.yaml)", + "properties": { + "summary": { "type": "string" } + } + }, + "information": { + "type": "object", + "description": "What this NPC knows", + "properties": { + "knows": { + "type": "array", + "items": { "type": "string" }, + "description": "Fact IDs this NPC knows" + }, + "access_tier": { + "type": "string", + "enum": ["public", "insider", "authority", "peer", "hostile"] + } + } + }, + "contentment": { + "type": "object", + "properties": { + "level": { "type": "integer", "minimum": -10, "maximum": 10 }, + "description": { "type": "string" } + } + }, + "personality": { + "type": "object", + "description": "Personality traits and behavioral tendencies", + "additionalProperties": { "type": "string" } + }, + "tells": { + "type": "array", + "items": { "$ref": "#/$defs/tell" }, + "description": "Observable behavioral tells (D-024)" + }, + "skills": { + "type": "object", + "description": "Skill set and combat capability", + "properties": { + "combat_trained": { "type": "boolean" }, + "skills": { + "type": "object", + "additionalProperties": { "type": "integer", "minimum": 0, "maximum": 10 } + } + } + }, + "triangle_membership": { + "type": "array", + "items": { "type": "string" }, + "description": "Triangle slugs this NPC participates in" + }, + "trust_levels": { + "type": "object", + "description": "What the NPC reveals at each trust level", + "properties": { + "surface": { "type": "string" }, + "real": { "type": "string" }, + "secret": { "type": "string" } + } + }, + "friend_arc": { + "type": "object", + "description": "FRIEND arc data — only valid on FRIEND-pattern Tier 1 NPCs (D-034)", + "properties": { + "bonded_character": { + "type": "string", + "enum": ["smuggler", "detective"] + }, + "phases": { + "type": "array", + "items": { "$ref": "#/$defs/friend_phase" } + } + }, + "required": ["bonded_character", "phases"] + }, + "dual_lens": { + "type": "object", + "description": "Authoring-only: how smuggler vs detective perceives this NPC", + "properties": { + "smuggler": { "type": "string" }, + "detective": { "type": "string" } + } + }, + "notes": { + "type": "string", + "description": "Authoring-only: design notes" + } + }, + "if": { + "properties": { "pattern": { "const": "FRIEND" } } + }, + "then": { + "required": ["friend_arc"] + }, + "$defs": { + "relationship": { + "type": "object", + "required": ["target", "kind"], + "additionalProperties": false, + "properties": { + "target": { + "type": "string", + "pattern": "^npc:[a-z][a-z0-9-]*$" + }, + "kind": { + "type": "string", + "enum": ["colleague", "friend", "rival", "romantic", "family", "superior", "subordinate"] + }, + "trust": { + "type": "integer", + "minimum": -10, + "maximum": 10 + }, + "notes": { "type": "string" } + } + }, + "tell": { + "type": "object", + "required": ["trigger", "behavior"], + "additionalProperties": false, + "properties": { + "trigger": { "type": "string" }, + "behavior": { "type": "string" }, + "visible_to": { + "type": "string", + "enum": ["forward", "peripheral", "any"] + } + } + }, + "friend_phase": { + "type": "object", + "required": ["phase", "description"], + "additionalProperties": false, + "properties": { + "phase": { "type": "integer", "minimum": 1, "maximum": 5 }, + "description": { "type": "string" }, + "trigger": { "type": "string" }, + "routine_deviation": { "type": "string" } + } + } + } +} diff --git a/content/_schema/routine.schema.json b/content/_schema/routine.schema.json new file mode 100644 index 000000000..c8e13e8fb --- /dev/null +++ b/content/_schema/routine.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "routine.schema.json", + "title": "NPC Routine Schedules", + "description": "Daily routine schedules — all NPCs in one file per district for cross-NPC validation (D-034).", + "type": "object", + "required": ["district", "schedules"], + "additionalProperties": false, + "properties": { + "district": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "District slug this routine file belongs to" + }, + "schedules": { + "type": "array", + "items": { "$ref": "#/$defs/npc_schedule" }, + "minItems": 1 + } + }, + "$defs": { + "npc_schedule": { + "type": "object", + "required": ["npc", "entries"], + "additionalProperties": false, + "properties": { + "npc": { + "type": "string", + "pattern": "^npc:[a-z][a-z0-9-]*$", + "description": "NPC short-form canonical ID" + }, + "entries": { + "type": "array", + "items": { "$ref": "#/$defs/routine_entry" }, + "minItems": 1 + }, + "deviations": { + "type": "array", + "items": { "$ref": "#/$defs/deviation" }, + "description": "Conditional schedule overrides (FRIEND arc staging, etc.)" + } + } + }, + "routine_entry": { + "type": "object", + "required": ["phase", "location"], + "additionalProperties": false, + "properties": { + "phase": { + "type": "string", + "enum": ["morning", "afternoon", "evening", "night"], + "description": "Day phase for this entry" + }, + "location": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "Location slug" + }, + "tile": { + "type": "object", + "properties": { + "x": { "type": "integer" }, + "y": { "type": "integer" } + }, + "required": ["x", "y"], + "description": "Specific tile coordinates within the location" + }, + "activity": { + "type": "string", + "description": "What the NPC is doing at this location/time" + } + } + }, + "deviation": { + "type": "object", + "required": ["trigger", "location"], + "additionalProperties": false, + "properties": { + "trigger": { + "type": "string", + "description": "Condition that activates this deviation" + }, + "phase": { + "type": "string", + "enum": ["morning", "afternoon", "evening", "night"] + }, + "location": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$" + }, + "tile": { + "type": "object", + "properties": { + "x": { "type": "integer" }, + "y": { "type": "integer" } + }, + "required": ["x", "y"] + }, + "activity": { "type": "string" } + } + } + } +} diff --git a/content/_schema/triangle.schema.json b/content/_schema/triangle.schema.json new file mode 100644 index 000000000..55b594a90 --- /dev/null +++ b/content/_schema/triangle.schema.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "triangle.schema.json", + "title": "Triangle Definition", + "description": "3-NPC relationship triangle — self-contained forks, no cross-triangle cascade in v0.1 (D-024, D-047).", + "type": "object", + "required": ["canonical_id", "display_name", "members"], + "additionalProperties": false, + "properties": { + "canonical_id": { + "type": "string", + "pattern": "^[a-z][a-z0-9-]*$", + "description": "Triangle slug" + }, + "display_name": { + "type": "string", + "minLength": 1 + }, + "description": { + "type": "string" + }, + "members": { + "type": "array", + "items": { "$ref": "#/$defs/member" }, + "minItems": 3, + "maxItems": 3, + "description": "Exactly 3 NPC members" + }, + "forks": { + "type": "array", + "items": { "$ref": "#/$defs/fork" }, + "description": "Possible fork points in this triangle" + }, + "resolution_states": { + "type": "array", + "items": { "$ref": "#/$defs/resolution" }, + "description": "Terminal states this triangle can reach" + } + }, + "$defs": { + "member": { + "type": "object", + "required": ["npc", "role"], + "additionalProperties": false, + "properties": { + "npc": { + "type": "string", + "pattern": "^npc:[a-z][a-z0-9-]*$", + "description": "NPC short-form canonical ID" + }, + "role": { + "type": "string", + "description": "This NPC's role within the triangle" + } + } + }, + "fork": { + "type": "object", + "required": ["id", "condition"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_-]*$" + }, + "condition": { + "type": "string", + "description": "What triggers this fork" + }, + "outcomes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "description": { "type": "string" }, + "effects": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + } + }, + "resolution": { + "type": "object", + "required": ["id", "description"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_-]*$" + }, + "description": { "type": "string" } + } + } + } +}