Test Campaign Scaffolder Skill
RPG Pipeline Only — This skill creates actual data files (JSON,
.char,.dtree,.worldmap.json) from a CRPG engine campaign specification. It generates characters with RPG stats, dialogue trees with skill checks, quest files with objectives, encounter files with enemy composition, and worldmaps with interior grid layouts. For non-RPG games, the content-architect should create simpler data files suited to the game's needs. The spec-driven scaffolding workflow pattern applies broadly.
Workflow Context
| Field | Value |
|---|---|
| Assigned Agent | content-architect |
| Sprint Phase | Phase B (Implementation) — used for bulk file generation from specs |
| Directory Scope | data/ (all subdirectories: campaigns, world, characters, dialogue, encounters, quests) |
| Genre | RPG / CRPG only |
| Schema Dependency | Requires all CRPG engine schemas (character, quest, dialogue, encounter) |
| Prerequisite | Requires a spec file from test-campaign-generator in docs/test-campaigns/ |
| Workflow Reference | See docs/agent-team-workflow.md |
This skill CREATES ACTUAL DATA FILES from a test campaign specification. It reads a spec document from docs/test-campaigns/ and generates all the JSON, .char, .dtree, and worldmap files needed, including full interior grid layouts with NPC placements.
CRITICAL: What This Skill Does and Does NOT Do
+------------------------------------------+------------------------------------------+ | THIS SKILL DOES | THIS SKILL DOES NOT DO | +------------------------------------------+------------------------------------------+ | Create JSON files in data/ | Create specification documents | | Create .char files for characters | Design campaign content | | Create .dtree files for dialogues | Ask about tiers, themes, or requirements | | Create .worldmap.json files | Save anything to docs/test-campaigns/ | | Generate interior grid layouts | Make creative decisions about content | | Generate NPC placements on grids | Generate content from scratch | | Generate terrain cells and structures | Add NPCs to game_hud.gd (LEGACY) | | Update main.gd DEFAULT_CAMPAIGN | | +------------------------------------------+------------------------------------------+
This skill REQUIRES a specification file from test-campaign-generator to exist first.
IMPORTANT: This skill does NOT modify game_hud.gd. NPCs are placed on interior grids via interior.definition.npc_placements, not the legacy GameHUD NPC array.
Two-Step Workflow
┌─────────────────────────────────────────────────────────────────────────────┐ │ TEST CAMPAIGN CREATION WORKFLOW │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ STEP 1: test-campaign-generator (PREREQUISITE SKILL) │ │ ───────────────────────────────────────────────────── │ │ Input: User requirements (tier, theme, focus areas) │ │ Output: docs/test-campaigns/[name].md (SPECIFICATION ONLY) │ │ │ │ ↓ │ │ │ │ STEP 2: test-campaign-scaffolder (THIS SKILL) │ │ ───────────────────────────────────────────────── │ │ Input: The spec file from Step 1 │ │ Output: All JSON/data files in data/ directory │ │ - data/campaigns/[id].json │ │ - data/world/[id].worldmap.json (with interior definitions) │ │ - data/characters/npcs/[id].char │ │ - data/characters/enemies/[id].char │ │ - data/dialogue/[id].dtree │ │ - data/encounters/[id].json │ │ - data/quests/[id].json │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
When to Use This Skill
Invoke this skill when the user:
- •Has a test campaign spec and wants to generate the actual data files
- •Says "scaffold the test campaign" or "generate files for test campaign"
- •Says "create the files from the spec"
- •Asks to "implement the test campaign spec"
- •Says "yes" when asked if they want to scaffold after running
test-campaign-generator
DO NOT use this skill if:
- •No spec file exists in
docs/test-campaigns/ - •User is still designing the campaign content
- •User wants to modify the spec (use test-campaign-generator instead)
Prerequisites
Before running this skill:
- •A specification file MUST exist at
docs/test-campaigns/[campaign-name].md - •The spec MUST have been generated by
test-campaign-generator - •The spec MUST contain Content Manifest tables with IDs
- •The spec SHOULD contain Interior Layout sections for locations with
designedinteriors
Spec Sources Supported:
- •Tier-based specs (Tier 1-4)
- •D&D/Tabletop module imports (Tier 5)
- •Custom scope specs
- •Editor focus specs
D&D Module Specs: When scaffolding D&D-sourced specs, this skill applies the standard conversion rules (stats × 5, CR × 5 for levels, etc.) as defined in the spec.
If no spec exists, tell the user:
"No specification file found. Please run
test-campaign-generatorfirst to create a campaign spec, then I can scaffold the files from it."
Input
A test campaign specification file from docs/test-campaigns/[name].md
If multiple specs exist, ask the user which one to scaffold.
Output
Files Generated
data/
├── campaigns/
│ └── [campaign_id].json # Campaign config file
├── world/
│ └── [campaign_id].worldmap.json # Worldmap with locations, routes, AND interior definitions
├── characters/
│ ├── enemies/
│ │ └── [enemy_id].char # Enemy character files
│ └── npcs/
│ └── [npc_id].char # NPC character files
├── dialogue/
│ └── [dialogue_id].dtree # Dialogue tree files
├── encounters/
│ └── [encounter_id].json # Encounter files
└── quests/
└── [quest_id].json # Quest files
Code Modifications
This skill may:
- •Update
scenes/main.gdDEFAULT_CAMPAIGN constant (optional, ask user)
This skill does NOT modify:
- •
crpg_engine/ui/game_hud.gd- NPCs are placed via interior grids, not GameHUD
File Schemas
Campaign File (.json)
{
"id": "[campaign_id]",
"name": "[Campaign Name]",
"description": "[Campaign description]",
"version": 1,
"author": "Test Campaign Scaffolder",
"created_date": "[date]",
"player_character": "player",
"worldmap": "[campaign_id]",
"starting_location": "[location_id]",
"starting_locations": ["[location_id]"],
"starting_npcs": ["[npc_id]"],
"initial_state": {
"gold": 0,
"items": [],
"flags": ["game_started"],
"variables": {}
},
"chapters": [...],
"endings": [...],
"_metadata": {...}
}
Worldmap File (.worldmap.json) - WITH INTERIOR DEFINITIONS
{
"name": "[Worldmap Name]",
"description": "[Description]",
"author": "Test Campaign Scaffolder",
"version": "1.0",
"file_version": 1,
"map_data": {...},
"editor_state": {...},
"regions": [...],
"locations": [
{
"id": "[location_id]",
"name": "[Location Name]",
"type": "[settlement/dungeon/wilderness]",
"region": "[region_id]",
"description": "[Description]",
"position": {"x": 0, "y": 0},
"map_scene": "",
"custom_data": {
"safe_zone": true,
"services": [],
"npcs": [
{"name": "[NPC Name]", "id": "[npc_id]", "conditional": false}
],
"hostile": false,
"encounter": "[encounter_id]"
},
"interior": {
"mode": 1,
"template": "",
"seed": 0,
"definition": {
"grid_width": 12,
"grid_height": 10,
"cells": [1,1,1,1,1,1,...],
"structures": [
{
"id": "struct_0",
"type": 8,
"position": {"x": 6, "y": 5},
"rotation": 0,
"custom_data": {}
}
],
"paths": [],
"zones": [
{
"id": "zone_entrance",
"type": 1,
"rect": {"x": 5, "y": 8, "w": 3, "h": 2},
"custom_data": {}
},
{
"id": "zone_combat",
"type": 9,
"rect": {"x": 2, "y": 3, "w": 4, "h": 2},
"custom_data": {
"encounter_id": "[encounter_id]",
"required_flag": "[flag_name]"
}
}
],
"npc_placements": [
{
"npc_id": "[npc_id]",
"position": {"x": 5, "y": 3},
"facing": "down",
"patrol_path": [],
"conditions": {}
}
],
"interactables": [],
"spawn_points": [
{
"id": "default",
"position": {"x": 6, "y": 9},
"from_location": ""
}
],
"template_id": "",
"generation_seed": 0,
"created_at": "[date]",
"modified_at": "[date]"
}
}
}
],
"routes": [...]
}
Interior Cell Types
When generating the cells array, use these values:
| Value | Type | Description |
|---|---|---|
| 0 | EMPTY | Non-walkable empty space |
| 1 | FLOOR | Basic walkable floor |
| 2 | WALL | Non-walkable wall |
| 3 | DOOR | Walkable door cell |
| 4 | PATH | Walkable path/road |
| 5 | WATER | Non-walkable water |
| 6 | GRASS | Walkable grass terrain |
| 7 | ROAD | Walkable road |
Interior Structure Types
When placing structures, use these values:
| Value | Type | Description |
|---|---|---|
| 0 | NONE | No structure |
| 1 | BUILDING | Building structure |
| 2 | TENT | Tent |
| 3 | STALL | Market stall |
| 4 | WELL | Well |
| 5 | STATUE | Statue |
| 6 | TREE | Tree |
| 7 | ROCK | Rock |
| 8 | CAMPFIRE | Campfire |
| 9 | SHRINE | Shrine |
| 10 | CHEST | Chest |
| 11 | SIGN | Sign |
Interior Zone Types
When defining zones, use these values:
| Value | Type | Description |
|---|---|---|
| 0 | NONE | No special zone |
| 1 | ENTRANCE | Entry point zone |
| 2 | EXIT | Exit point zone |
| 3 | SAFE | Safe zone |
| 4 | DANGER | Danger zone |
| 5 | SHOP | Shop area |
| 6 | REST | Rest area |
| 7 | QUEST | Quest trigger zone |
| 8 | SECRET | Secret area |
| 9 | COMBAT | Combat arena |
Combat Zone Setup (CRITICAL)
When creating COMBAT zones (type 9), the custom_data MUST include:
{
"id": "zone_combat",
"type": 9,
"rect": {"x": 3, "y": 3, "w": 4, "h": 2},
"custom_data": {
"name": "ambush_zone",
"encounter_id": "enc_example_encounter",
"required_flag": "quest_accepted"
}
}
| Field | Required | Description |
|---|---|---|
encounter_id | YES | The encounter ID to trigger when player enters this zone |
required_flag | Optional | Flag that must be set before encounter triggers |
⚠️ IMPORTANT: The encounter trigger system (main._on_zone_entered()) reads encounter_id and required_flag from the zone's custom_data, NOT from the location's custom_data. Without these fields in the zone, the encounter will never trigger!
Character File (.char)
{
"version": 1,
"id": "[character_id]",
"identity": {
"display_name": "[Display Name]",
"titles": [],
"aliases": []
},
"classification": {
"role": 3,
"factions": ["[Faction]"],
"tier": 1,
"tags": ["tag1", "tag2"]
},
"appearance": {
"portrait": "res://assets/sprites/characters/[category]/[sprite_name]/base/south.png",
"sprite_project": "res://assets/sprites/characters/[category]/[sprite_name]",
"description": "[Physical description]",
"height": "medium",
"build": "average"
},
"personality": {
"traits": [],
"ideals": [],
"bonds": [],
"flaws": [],
"background": "[Character background]",
"voice_style": ""
},
"stats": {
"level": 1,
"experience": 0,
"health": 10,
"max_health": 10,
"mana": 0,
"max_mana": 0,
"attributes": {
"strength": 10,
"dexterity": 10,
"constitution": 10,
"intelligence": 10,
"wisdom": 10,
"charisma": 10
},
"skills": {},
"resistances": {},
"abilities": ["ability_id_1", "ability_id_2"],
"equipment": {
"weapon": "",
"armor": "",
"accessories": []
}
},
"relationships": [],
"notes": {
"designer_notes": "",
"quest_notes": "",
"combat_notes": ""
},
"metadata": {
"created_date": "[date]",
"modified_date": "[date]",
"author": "Test Campaign Scaffolder",
"campaign_id": "[campaign_id]"
}
}
IMPORTANT Field Names:
- •
metadata.campaign_id- NOTcampaign(required for Character Creator filtering) - •
appearance.sprite_project- NOTsprite(path to sprite folder WITHOUT/base/) - •
appearance.portrait- Full path to south-facing sprite image
Role Values:
- •1 = player
- •2 = npc
- •3 = enemy
- •4 = companion
Available Placeholder Sprites:
- •NPCs:
quest_givers/village_elder,quest_givers/priest,quest_givers/mysterious_stranger,merchants/merchant,townsfolk/innkeeper,townsfolk/blacksmith,townsfolk/herbalist,guards/guard,guards/guard_captain - •Enemies:
undead/skeleton_warrior,undead/skeleton_archer,undead/ghost,undead/wraith,bandits/bandit_thug,bandits/bandit_archer,bandits/bandit_captain,bosses/bandit_chief,cult/cultist,cult/necromancer,beasts/wolf,demons/imp,military/dark_knight - •Companions:
warrior,mage,rogue,thorne_fighter,lyra_rogue
Available Combat Abilities:
Characters MUST have abilities assigned for combat to be interesting. Available abilities in data/combat/abilities/:
| Ability ID | Type | Element | Resource | Best For |
|---|---|---|---|---|
fireball | attack | fire | mana | mages, cultists |
ice_lance | attack | ice | mana | mages |
lightning_bolt | attack | lightning | mana | mages |
chain_lightning | attack | lightning | mana | mages (AoE) |
healing_word | healing | holy | mana | healers, priests |
mass_heal | healing | holy | mana | healers (AoE) |
poison_cloud | attack | poison | mana | assassins, cultists |
divine_shield | buff | holy | mana | paladins, priests |
shield_bash | attack | physical | stamina | warriors, guards, bandits |
backstab | attack | physical | stamina | rogues, assassins, bandits |
battle_cry | buff | none | stamina | warriors, leaders |
teleport | utility | arcane | mana | mages |
Recommended Abilities by Character Type:
- •Bandits/Thugs:
["shield_bash", "backstab"] - •Bandit Captains:
["shield_bash", "battle_cry"] - •Mages/Cultists:
["fireball", "ice_lance"]or["lightning_bolt", "chain_lightning"] - •Healers/Priests:
["healing_word", "divine_shield"] - •Warriors/Guards:
["shield_bash", "battle_cry"] - •Rogues/Assassins:
["backstab", "poison_cloud"] - •Bosses: 3-4 abilities mixing offense, defense, and utility
Dialogue File (.dtree)
{
"version": 1,
"metadata": {
"dialogue_id": "[dialogue_id]",
"display_name": "[Dialogue Display Name]",
"description": "[Description]",
"location_id": "[location_id]",
"author": "Test Campaign Scaffolder",
"created_date": "[date]",
"modified_date": "[date]",
"campaign_id": "[campaign_id]"
},
"canvas": {...},
"nodes": [...],
"connections": [...]
}
CRITICAL: Dialogue Choice Node Pattern
⚠️ NEVER use a choices array inside a single Choice node! This causes the "[1]" display bug.
The dialogue runtime's _gather_connected_choices function looks for the text property on individual Choice nodes, NOT a choices array.
✅ CORRECT Pattern - Individual Choice nodes:
{
"nodes": [
{"id": "Speaker_greeting", "type": "Speaker", "position_x": 300, "position_y": 200,
"speaker": "npc_example", "text": "Hello traveler, how can I help you?"},
{"id": "Choice_help", "type": "Choice", "position_x": 100, "position_y": 400,
"text": "I need your help."},
{"id": "Choice_info", "type": "Choice", "position_x": 300, "position_y": 400,
"text": "Tell me about this place."},
{"id": "Choice_goodbye", "type": "Choice", "position_x": 500, "position_y": 400,
"text": "Goodbye."}
],
"connections": [
{"from_node": "Speaker_greeting", "from_port": 0, "to_node": "Choice_help", "to_port": 0},
{"from_node": "Speaker_greeting", "from_port": 0, "to_node": "Choice_info", "to_port": 0},
{"from_node": "Speaker_greeting", "from_port": 0, "to_node": "Choice_goodbye", "to_port": 0},
{"from_node": "Choice_help", "from_port": 0, "to_node": "Speaker_help_response", "to_port": 0},
{"from_node": "Choice_info", "from_port": 0, "to_node": "Speaker_info_response", "to_port": 0},
{"from_node": "Choice_goodbye", "from_port": 0, "to_node": "End_1", "to_port": 0}
]
}
Key Points:
- •Each Choice is a separate node with its own
textproperty - •The Speaker node connects to multiple Choice nodes (same
from_port: 0) - •Each Choice node connects to its own response (Speaker or End node)
- •Choice nodes do NOT have a
choicesarray - they ARE the choice
❌ WRONG Pattern - DO NOT USE:
{
"id": "Choice_options", "type": "Choice",
"choices": [
{"text": "Option 1", "next": "Speaker_1"},
{"text": "Option 2", "next": "Speaker_2"}
]
}
This pattern will display "[1]", "[2]" instead of the actual text!
Quest-Dialogue Integration (CRITICAL)
Every quest created MUST have associated dialogues that:
- •Start the quest via a
Questnode withquest_action: "Start" - •Complete the quest via a
Questnode withquest_action: "Complete"
Quest Node Example:
{
"id": "Quest_start_quest1",
"type": "Quest",
"position_x": 300,
"position_y": 600,
"quest_id": "quest_example",
"quest_action": "Start"
}
Dialogue Structure for Quest Givers:
Start → Branch (quest_complete?)
├── TRUE → Speaker (thanks) → FlagSet → Quest (Complete) → End
└── FALSE → Speaker (greeting) → Choice nodes → Speaker (quest info) → Quest (Start) → End
Encounter File (.json)
{
"_meta": {
"created": "[date]",
"modified": "[date]"
},
"id": "[encounter_id]",
"campaign_id": "[campaign_id]",
"display_name": "[Encounter Name]",
"description": "[Description]",
"encounter_type": "combat",
"difficulty": "easy",
"level_range": {"min": 1, "max": 3},
"location_id": "[location_id]",
"enemies": [...],
"victory": {...},
"defeat": {...},
"rewards": {...},
"one_time": true,
"can_flee": true,
"scales_with_level": false,
"flags_set_on_complete": [...],
"trigger_conditions": {...},
"grid_settings": {
"enabled": true
}
}
IMPORTANT: The grid_settings.enabled: true field is REQUIRED for the tactical grid-based combat system. Without it, encounters will use the legacy simple combat UI.
CRITICAL: campaign_id for Editor Filtering
⚠️ ALL content files MUST include campaign_id at the TOP LEVEL of the JSON for editor filtering to work!
The CRPG Engine editors use CampaignRegistry.matches_active_campaign() to filter content by the currently selected campaign in the Campaign Selector dropdown. The editors read campaign_id from the ROOT level of the JSON, NOT from inside _metadata or _meta.
Correct Placement (TOP LEVEL - Required):
{
"_metadata": { ... },
"id": "quest_example",
"campaign_id": "doip", // <-- MUST be at top level!
"name": "Example Quest",
...
}
Wrong Placement (inside _metadata - NOT read by editors):
{
"_metadata": {
"campaign_id": "doip" // <-- Editors do NOT read this!
},
"id": "quest_example",
...
}
| File Type | Required Location | Example |
|---|---|---|
| Quest (.json) | Top-level campaign_id | "campaign_id": "doip" after "id" |
| Encounter (.json) | Top-level campaign_id | "campaign_id": "doip" after "id" |
| Character (.char) | metadata.campaign_id | Inside metadata object |
| Dialogue (.dtree) | metadata.campaign_id | Inside metadata object |
If content doesn't appear in editors:
- •Check that the file has
campaign_idAT THE TOP LEVEL (not just in_metadata) - •Ensure the Campaign Selector dropdown is set to the correct campaign
- •Verify the
campaign_idvalue matches the campaign'sidfield exactly
Quest File (.json)
{
"_metadata": {
"author": "Test Campaign Scaffolder",
"last_modified": "[date]",
"validation_status": "not_validated",
"version": 1
},
"id": "[quest_id]",
"campaign_id": "[campaign_id]",
"name": "[Quest Name]",
"display_name": "[Quest Display Name]",
"type": "side_quest",
"quest_type": "side",
"difficulty": "easy",
"recommended_level": 1,
"quest_giver_id": "[npc_id]",
"starting_location_id": "[location_id]",
"description": {...},
"designer_notes": "[Notes]",
"tags": [...],
"objectives": [...],
"objective_flow": "linear",
"prerequisites": {...},
"outcomes": [...],
"rewards": {...},
"dialogues": [...],
"journal": {...}
}
Workflow
Step 1: Find the Spec File
- •Look in
docs/test-campaigns/for spec files - •If user specified a name, find that spec
- •If multiple specs exist, ask which one to scaffold
- •If no spec exists, tell user to run
test-campaign-generatorfirst
Step 2: Parse the Spec
- •Read the markdown spec file
- •Extract all content from the tables:
- •Locations (ID, Name, Type, Connections, Interior Mode, Grid Size, NPCs)
- •NPCs (ID, Name, Role, Location, Grid Position, Dialogue)
- •Enemies (ID, Name, Type, Level)
- •Quests (ID, Name, Giver, Objectives)
- •Dialogues (ID, Speaker, Quest Links)
- •Encounters (ID, Name, Enemies, Quest Link)
- •Campaign Config (Starting Location, NPCs, Chapters)
- •Interior Layouts (Structures, Zones, Spawn Points)
Step 3: Generate Files in Dependency Order
Create files in this order (respecting dependencies):
- •Enemy character files (no dependencies)
- •NPC character files (no dependencies)
- •Encounter files (depends on enemies)
- •Dialogue files (depends on NPCs, quests)
- •Quest files (depends on NPCs, encounters, dialogues)
- •Worldmap file with interior definitions (depends on locations, NPCs, encounters)
- •Campaign file (depends on worldmap, quests, NPCs)
Step 4: Generate Interior Grids
For each location with interior_mode: designed:
- •
Generate cells array:
- •Create array of size
grid_width * grid_height - •Fill with appropriate terrain type based on theme
- •Use FLOOR (1) or GRASS (6) for walkable areas
- •Mark walls/boundaries as WALL (2) or EMPTY (0)
- •Create array of size
- •
Place structures:
- •Read structure placements from spec
- •Convert structure type names to enum values
- •Add to
structuresarray
- •
Create NPC placements:
- •For each NPC assigned to this location
- •Read grid position from spec (or auto-assign if not specified)
- •Add to
npc_placementsarray with facing direction
- •
Define zones:
- •Read zone definitions from spec
- •Add entrance zone at spawn point
- •Add any shop/quest/danger zones specified
- •
Set spawn point:
- •Read from spec or default to south-center of grid
- •Add to
spawn_pointsarray
Step 5: Ask About Code Modifications
Ask user if they want to update scenes/main.gd DEFAULT_CAMPAIGN.
DO NOT modify crpg_engine/ui/game_hud.gd - this is legacy code.
Step 6: Generate Report
Create a summary of what was created:
## Scaffolding Complete ### Files Created - `data/campaigns/[id].json` - `data/world/[id].worldmap.json` (includes interior definitions) - `data/characters/npcs/[npc].char` - `data/characters/enemies/[enemy].char` - `data/dialogue/[dlg].dtree` - `data/encounters/[enc].json` - `data/quests/[quest].json` ### Interior Grids Generated | Location | Grid Size | Structures | NPCs Placed | Spawn Point | |----------|-----------|------------|-------------|-------------| | [loc_id] | 12x10 | 3 | 2 | (6, 9) | ### Code Modified - `scenes/main.gd` - Updated DEFAULT_CAMPAIGN (if requested) ### Next Steps 1. Run the game (F5) to test 2. Verify NPCs appear on location grids at correct positions 3. Test click-to-move and WASD movement 4. Test NPC interactions and dialogue 5. Test quest flow 6. Validate with Entity Editor
Interior Grid Generation Details
Basic Floor Layout
For a simple interior, generate cells like this:
# Example: 10x8 floor grid with grass border
func generate_floor_grid(width: int, height: int) -> Array[int]:
var cells: Array[int] = []
cells.resize(width * height)
for y in range(height):
for x in range(width):
var idx = y * width + x
# Border is grass, interior is floor
if x == 0 or x == width - 1 or y == 0 or y == height - 1:
cells[idx] = 6 # GRASS
else:
cells[idx] = 1 # FLOOR
return cells
Terrain Themes
Based on location type, use appropriate terrain:
| Location Type | Primary Terrain | Border/Edge |
|---|---|---|
| settlement | FLOOR (1) | GRASS (6) |
| tavern | FLOOR (1) | WALL (2) |
| camp | GRASS (6) | GRASS (6) |
| dungeon | FLOOR (1) | WALL (2) |
| wilderness | GRASS (6) | GRASS (6) |
| road | PATH (4) or ROAD (7) | GRASS (6) |
NPC Placement Rules
- •Always place on walkable terrain - Verify cell type is walkable
- •Avoid spawn point - Don't place NPCs where player spawns
- •Avoid structures - Don't overlap with structure positions
- •Use spec positions - If spec provides (x, y), use those
- •Auto-assign if needed - If no position in spec, find valid walkable cell
Example Invocations
Standard Campaigns:
- •"Scaffold the haunted mine test campaign"
- •"Generate files from the rat cellar spec"
- •"Create the data files for the test campaign"
- •"Yes, scaffold the files" (after test-campaign-generator)
- •"Implement the tier 2 village spec"
D&D Module Imports:
- •"Scaffold the Dragons of Icespire Peak spec"
- •"Generate files from the Lost Mine of Phandelver spec"
- •"Create the CRPG files from my D&D campaign spec"
- •"Scaffold the Phandalin starter campaign"
Integration with Other Skills
| Skill | Relationship |
|---|---|
| test-campaign-generator | PREREQUISITE - creates the spec this skill reads |
| character-creator | Can enhance/edit scaffolded .char files |
| quest-designer | Can enhance/edit scaffolded quest files |
| dialogue-designer | Can enhance/edit scaffolded .dtree files |
| encounter-designer | Can enhance/edit scaffolded encounter files |
| world-builder | Can enhance/edit scaffolded worldmap file |
| campaign-creator | Final step after all content is validated |
| entity-editor validation | Use after scaffolding to validate references |
| error-debugger | Use if generated files have issues |
| changelog-updater | Track test campaigns scaffolded |
Workflow Paths
Standard Bulk Creation:
1. test-campaign-generator → Create specification 2. test-campaign-scaffolder → Generate all files (THIS SKILL) 3. Entity Editor → Validate cross-references 4. Game → Test playthrough
D&D Import Path:
1. test-campaign-generator (D&D mode) → Create spec from module 2. Review/edit spec for CRPG adjustments 3. test-campaign-scaffolder → Generate all files (THIS SKILL) 4. Use individual skills for custom enhancement if needed 5. campaign-creator → Finalize campaign
Hybrid Path (Scaffold + Enhance):
1. test-campaign-generator → Create basic specification 2. test-campaign-scaffolder → Generate skeleton files (THIS SKILL) 3. Use individual skills to enhance: - dialogue-designer → Expand dialogue trees - encounter-designer → Tune encounters - character-creator → Flesh out NPCs 4. campaign-creator → Assemble final campaign
Error Handling
No Spec Found
"No specification file found at docs/test-campaigns/[name].md. Please run `test-campaign-generator` first to create a campaign spec."
Invalid Spec Format
"The spec file is missing required sections. Expected tables for: - Locations (with Interior Mode and Grid Size columns) - NPCs (with Grid Position column) - Enemies - Quests - Dialogues - Encounters Please regenerate the spec using `test-campaign-generator`."
File Already Exists
"File data/quests/[id].json already exists. Overwrite? (y/n)"
Invalid Grid Position
"NPC [npc_id] has grid position (x, y) which is outside grid bounds or on non-walkable terrain. Auto-assigning valid position..."
Checklist Before Completing This Skill
Before finishing, verify:
- • Spec file existed in
docs/test-campaigns/ - • All JSON files created in
data/directory - • All .char files created for NPCs and enemies
- • All .dtree files created for dialogues
- • Worldmap file created with locations and routes
- • Encounter files include
grid_settings.enabled: truefor tactical combat - • Combat zones (type 9) have
encounter_idin zone'scustom_data(NOT in location's custom_data!) - • Combat zones have
required_flagincustom_dataif encounter requires a flag - • Interior definitions included for locations with designed interiors
- • NPC placements included in interior definitions
- • Cells array generated with appropriate terrain
- • Spawn points defined for each interior
- • Campaign file references correct worldmap
- • Did NOT modify game_hud.gd (legacy system)
- • Asked user about updating DEFAULT_CAMPAIGN
- • Provided summary of files created
- • Suggested next steps (run game, test grid movement, validate)
Character File Field Verification:
- •
metadata.campaign_idis set (NOTmetadata.campaign) - •
appearance.sprite_projectis set (NOTappearance.sprite) - •
appearance.portraitpoints to valid sprite file - • Sprite paths do NOT have trailing slashes
- •
stats.abilitiesarray is populated with valid ability IDs (NOT empty!) - • Abilities are appropriate for character type (see Available Combat Abilities table)
Quest File Field Verification:
- •
_metadata.campaign_idis set to the campaign's id (CRITICAL for editor filtering!)
Encounter File Field Verification:
- •
_meta.campaign_idis set to the campaign's id (CRITICAL for editor filtering!)
Dialogue File Field Verification:
- •
metadata.campaign_idis set to the campaign's id (CRITICAL for editor filtering!) - • Choice nodes use individual
textproperty (NOT achoicesarray!) - • Speaker nodes connect to multiple Choice nodes (one connection per choice)
- • Each Choice node connects to its response node (Speaker or End)
- • Quest-giving dialogues have Quest nodes with
quest_action: "Start"or"Complete" - • Branch nodes used for state checking (e.g., quest already complete?)