# Godot Data Driven Config

> Create or extend data-driven config tables (WeaponData, EnemyData, LevelData, etc.) in a Godot 4 project so that designers can tweak numbers in .tres files via natural language. Use this skill whenever the user says something like "give weapons a config table", "extract hardcoded numbers into data", "add a new enemy type", "designer wants to tune damage/hp/speed", "add WeaponData/PlayerData/EnemyData", or asks to make Godot values editable without touching code.

- Skill: `ahang1598/godot-data-driven-config` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds@latest add ahang1598/godot-data-driven-config`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ahang1598/godot-data-driven-config/raw
- Safety review: pending (external: skill-scanner PASS, skillspector WARNING)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ahang1598 (https://skillmd.com/u/ahang1598)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/ahang1598/godot-data-driven-config

---


# Godot Data-Driven Config Skill

## What this skill does

Turns hardcoded `const` / magic numbers inside `.gd` files into a clean **Resource-based config layer** that designers can edit via `.tres` or the Godot Inspector. Also generates a CLI validator (`bash tools/validate_data.sh`) so a human or CI can check "did I break the config table?" in one command.

## When to invoke

Trigger on intents like:
- "Create a WeaponData / EnemyData / LevelData / SkillData for this project"
- "Move these hardcoded numbers into a config table"
- "I want designers to tune damage/hp/speed without touching code"
- "Add a new weapon/enemy type through data"
- "Build an AI-native Godot config workflow"

## Canonical file layout (ALWAYS use this)

```
res://data/
├── specs/                       # JSON specs, source of truth (git-tracked)
│   ├── weapon.spec.json
│   └── ...
├── resources/
│   ├── <name>_data.gd           # class_name <Name>Data extends Resource
│   └── data_manager.gd          # Autoload (AUTOGENERATED — do not hand-edit AUTOGEN regions)
├── <name>s/                     # plural dir, one .tres per entry
│   └── default_<name>.tres
tools/
├── validate_data.gd             # Godot-side CLI validator (autogen)
└── validate_data.sh             # bash wrapper (exit code fix-up)
project.godot                    # [autoload] DataManager="*res://data/resources/data_manager.gd"
```

Rationale: plural-dir + `id` field makes `DirAccess` scanning trivial. Specs under `data/specs/*.spec.json` are the **source of truth**; regenerating `data_manager.gd` / `validate_data.gd` is always a pure function of that directory, so nothing can drift.

## Required workflow

### Step 1 — Discover & propose

1. Read target `.gd` files; collect hardcoded constants and literals.
2. Ask the user which category (weapon/player/enemy/level/skill/item/…). Each category becomes one `<Name>Data` class.
3. Produce a **field mapping table** (source constant → new field → range). Present it, wait for OK (max 1 round of confirmation).

### Step 2 — Write spec JSON under `data/specs/`

Every category is described by exactly one JSON file matching `schemas/field_spec.schema.json`. Commit these to git — they are the source of truth and diff well.

Minimal valid spec:
```json
{
  "name": "weapon",
  "default_id": "default_pistol",
  "fields": [
    { "key": "id", "type": "StringName", "default": "&\"\"", "group": "Identity" },
    { "key": "damage", "type": "int", "default": 10, "range": [0, 9999, 1] }
  ],
  "validators": ["damage >= 0"]
}
```

### Step 3 — Run `scaffold.py` once per new/updated category

```bash
python3 ~/.codebuddy/skills/godot-data-driven-config/scripts/scaffold.py \
    --project-root <abs Godot project path> \
    --spec <abs path to spec>.spec.json
```

The script:
- copies the spec into `res://data/specs/` (creates source-of-truth file)
- writes `res://data/resources/<name>_data.gd` (first time only; re-run with `--force-class` to overwrite)
- writes `res://data/<name>s/default_<name>.tres` (first time only; `--force-tres` to overwrite)
- **rebuilds** `res://data/resources/data_manager.gd` from **all** `data/specs/*.spec.json` — you never lose a previously-added category
- **rebuilds** `res://tools/validate_data.gd` (duck-typed against `Resource`, so cold-start without `.godot/` cache still works)
- writes `res://tools/validate_data.sh` wrapper
- patches `[autoload]` in `project.godot` idempotently

### Step 4 — Migrate consumer `.gd` files

For each file with hardcoded values:
1. Add at top of class:
   ```gdscript
   @export var <name>_data: <Name>Data
   ```
2. In `_ready()`:
   ```gdscript
   if <name>_data == null:
       <name>_data = DataManager.get_<name>()
   ```
3. Replace literal(s): `JUMP_SPEED` → `<name>_data.jump_speed` etc.
4. **Keep purely-technical consts** unchanged (collision masks, shader paths, blend-tree parameter strings).
5. **DO NOT** edit `.tscn` unless necessary — the `DataManager` fallback covers unbound cases.

### Step 5 — Validate

```bash
bash tools/validate_data.sh
# exit 0 = OK
# exit 1 = validation failed (stdout/stderr has reasons)
# exit 2 = environment issue
```

On **cold clone** (no `.godot/` dir), the wrapper auto-refreshes the class cache once via `godot --headless --editor --quit` — designers/CI need no special knowledge.

### Step 6 — Document

Append to `PROJECT_OVERVIEW.md` or `README.md`:
- The mapping table from Step 1
- Designer workflow: "edit `.tres` → run `bash tools/validate_data.sh` → F5".
- Validator command.

## Hard rules

- **One Resource class per category**, never a god-class.
- Numeric fields MUST have `@export_range`.
- Every `<Name>Data` MUST have an `id: StringName` field; filename stem is used as fallback id.
- `DataManager` is ALWAYS an Autoload named `DataManager`.
- `data_manager.gd` returns `Resource` (not `<Name>Data`) from getters; callers cast with `as WeaponData` if they want static typing. This keeps cold-start safe.
- DO NOT mutate a Resource at runtime (shared reference). To modify: `var copy = data.duplicate(); copy.x = ...`.
- DO NOT try to `MultiplayerSynchronizer`-sync a Resource. Instead, guarantee the same `.tres` exists on all peers (git-managed).
- Validator never uses `exit_code` because Godot bug #88055 drops it under `--script`. Use the `VALIDATION_RESULT=OK|FAIL:n` stdout marker (the generated `validate_data.sh` does this).
- DO NOT hand-edit the `# region *_AUTOGEN` blocks; `scaffold.py` rewrites them on every run.

## Cold-start / CI notes

- Godot's `global_script_class_cache.cfg` is created by the editor. For headless/CI, the first run needs `godot --headless --editor --quit` to populate it (the generated wrapper does this automatically when `.godot/` is missing).
- Validator is intentionally written to NOT depend on `class_name` resolution (uses `Resource` base + `res.get("field")` duck-typing), so even if the cache is stale it still works.

## Anti-patterns to avoid

- ❌ Using CSV/JSON/YAML loaders at runtime — Godot's native `.tres` is already text, git-friendly, type-safe, and Inspector-editable.
- ❌ Putting everything in one god-resource — harder to diff, no grouping, single-file merge conflicts.
- ❌ Hardcoding defaults in the Resource script AND in the `.tres` at different values — keep `.tres` authoritative; script defaults only act as fallback when a consumer never binds a resource.
- ❌ Reading `.tres` with `FileAccess` / `JSON.parse` — always use `load()` / `preload()`.

## Smoke-test before declaring done

Run `scripts/selftest.sh` (in this skill) which:
1. Creates a fresh temp Godot project.
2. Runs `scaffold.py` with every example spec.
3. Refreshes class cache + runs the validator.
4. Injects a bad value and confirms the validator now exits 1.
5. Cleans up.

```bash
bash ~/.codebuddy/skills/godot-data-driven-config/scripts/selftest.sh
```

## Deliverables checklist

When you finish, confirm ALL of:
- [ ] `res://data/specs/<name>.spec.json` exists and is committed
- [ ] `res://data/resources/<name>_data.gd` with `class_name` + typed `@export` fields
- [ ] `res://data/<name>s/default_<name>.tres` with sensible defaults
- [ ] `res://data/resources/data_manager.gd` exists and references the new category
- [ ] `project.godot` has `DataManager` in `[autoload]`
- [ ] Consumer `.gd` reference `<name>_data.xxx` (no more magic numbers)
- [ ] `bash tools/validate_data.sh` → exit 0
- [ ] `PROJECT_OVERVIEW.md` / `README.md` updated

## Files in this skill

```
SKILL.md                                  this file
QUICKREF.md                               one-page cheat sheet for the AI
schemas/field_spec.schema.json            JSON schema for designer-facing spec
templates/
  data_class.gd.tmpl                      Resource class template
  data_resource.tres.tmpl                 .tres template
  data_manager.gd.tmpl                    Autoload template (uses Resource base)
  validate_data.gd.tmpl                   CLI validator template (duck-typed)
  validate_data.sh.tmpl                   bash wrapper (exit-code fix-up)
scripts/
  scaffold.py                             end-to-end generator
  selftest.sh                             self-test / regression check
examples/
  weapon_spec.json
  enemy_spec.json
```

