@ericrisco/rsc 0.1.35 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +8 -2
  2. package/manifest.json +241 -2
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -0
  5. package/scripts/lib/recommend.js +4 -0
  6. package/skills/game-design/SKILL.md +170 -0
  7. package/skills/game-design/evals/README.md +13 -0
  8. package/skills/game-design/evals/cases.yaml +47 -0
  9. package/skills/game-design/references/mechanics-and-loops.md +78 -0
  10. package/skills/game-design/references/playtesting-and-scope.md +58 -0
  11. package/skills/game-design/references/progression-and-economy.md +70 -0
  12. package/skills/game-storytelling/SKILL.md +159 -0
  13. package/skills/game-storytelling/evals/README.md +28 -0
  14. package/skills/game-storytelling/evals/cases.yaml +51 -0
  15. package/skills/game-storytelling/references/branching-and-state.md +134 -0
  16. package/skills/game-storytelling/references/dialogue-tools.md +117 -0
  17. package/skills/game-storytelling/references/environmental-storytelling.md +71 -0
  18. package/skills/gamedev-multiplayer/SKILL.md +250 -0
  19. package/skills/gamedev-multiplayer/evals/README.md +12 -0
  20. package/skills/gamedev-multiplayer/evals/cases.yaml +44 -0
  21. package/skills/gamedev-multiplayer/references/godot-high-level-multiplayer.md +133 -0
  22. package/skills/gamedev-multiplayer/references/prediction-and-latency.md +104 -0
  23. package/skills/gamedev-multiplayer/references/unity-and-unreal-netcode.md +166 -0
  24. package/skills/gamedev-pathing/SKILL.md +242 -0
  25. package/skills/gamedev-pathing/evals/README.md +14 -0
  26. package/skills/gamedev-pathing/evals/cases.yaml +49 -0
  27. package/skills/gamedev-pathing/references/navmesh-workflow.md +110 -0
  28. package/skills/gamedev-pathing/references/search-algorithms.md +135 -0
  29. package/skills/gamedev-pathing/references/steering-and-avoidance.md +120 -0
  30. package/skills/gamedev-physics/SKILL.md +225 -0
  31. package/skills/gamedev-physics/evals/README.md +12 -0
  32. package/skills/gamedev-physics/evals/cases.yaml +54 -0
  33. package/skills/gamedev-physics/references/character-controllers.md +120 -0
  34. package/skills/gamedev-physics/references/determinism-and-queries.md +124 -0
  35. package/skills/gamedev-physics/references/layers-and-masks.md +115 -0
  36. package/skills/gamedev-shaders/SKILL.md +252 -0
  37. package/skills/gamedev-shaders/evals/README.md +14 -0
  38. package/skills/gamedev-shaders/evals/cases.yaml +45 -0
  39. package/skills/gamedev-shaders/references/effect-recipes.md +205 -0
  40. package/skills/gamedev-shaders/references/godot-shading-language.md +133 -0
  41. package/skills/gamedev-shaders/references/unity-and-unreal-shaders.md +169 -0
  42. package/skills/gamedev-shipping/SKILL.md +236 -0
  43. package/skills/gamedev-shipping/evals/README.md +9 -0
  44. package/skills/gamedev-shipping/evals/cases.yaml +55 -0
  45. package/skills/gamedev-shipping/references/mobile-and-desktop.md +127 -0
  46. package/skills/gamedev-shipping/references/stores-and-ci.md +162 -0
  47. package/skills/gamedev-shipping/references/web-export-headers.md +140 -0
  48. package/skills/godot/SKILL.md +319 -0
  49. package/skills/godot/evals/README.md +13 -0
  50. package/skills/godot/evals/cases.yaml +50 -0
  51. package/skills/godot/references/export-and-testing.md +102 -0
  52. package/skills/godot/references/gdscript-style.md +84 -0
  53. package/skills/godot/references/godot3-to-4-traps.md +89 -0
  54. package/skills/godot/references/nodes-scenes-resources.md +109 -0
  55. package/skills/level-design/SKILL.md +175 -0
  56. package/skills/level-design/evals/README.md +35 -0
  57. package/skills/level-design/evals/cases.yaml +58 -0
  58. package/skills/level-design/references/blockout-to-art.md +118 -0
  59. package/skills/level-design/references/guidance-and-readability.md +152 -0
  60. package/skills/level-design/references/pacing-and-encounters.md +142 -0
  61. package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
  62. package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
  63. package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
  64. package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
  65. package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
  66. package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
  67. package/skills/unity/SKILL.md +282 -0
  68. package/skills/unity/evals/README.md +8 -0
  69. package/skills/unity/evals/cases.yaml +43 -0
  70. package/skills/unity/references/build-and-platforms.md +65 -0
  71. package/skills/unity/references/input-system.md +102 -0
  72. package/skills/unity/references/lifecycle-deep-dive.md +75 -0
  73. package/skills/unreal/SKILL.md +270 -0
  74. package/skills/unreal/evals/README.md +12 -0
  75. package/skills/unreal/evals/cases.yaml +45 -0
  76. package/skills/unreal/references/blueprint-cpp-parity.md +109 -0
  77. package/skills/unreal/references/gameplay-framework.md +95 -0
  78. package/skills/unreal/references/packaging.md +83 -0
@@ -0,0 +1,162 @@
1
+ # Stores & CI — Steam, itch.io, Play/App Store, and headless build pipelines
2
+
3
+ Back to the entrypoint: `../SKILL.md`. Store submission mechanics and per-engine CI.
4
+
5
+ ## Steam (Steamworks + SteamPipe)
6
+
7
+ 1. **Partner setup**: Steamworks account, an **App ID**, and the store page (assets, description,
8
+ trailers, tags, pricing) filled in the partner site.
9
+ 2. **Depots**: a depot is a bucket of files for a platform/language (e.g. one Windows depot, one
10
+ macOS depot). Map each to your App ID.
11
+ 3. **SteamPipe upload** with `steamcmd` driven by VDF scripts.
12
+
13
+ `app_build_APPID.vdf`:
14
+
15
+ ```
16
+ "appbuild"
17
+ {
18
+ "appid" "480"
19
+ "desc" "1.2.3 - CI build"
20
+ "buildoutput" "output/"
21
+ "contentroot" "build/"
22
+ "setlive" "" // leave empty; set a branch live in the site, or "beta"
23
+ "depots" { "481" "depot_build_481.vdf" }
24
+ }
25
+ ```
26
+
27
+ `depot_build_481.vdf`:
28
+
29
+ ```
30
+ "DepotBuild"
31
+ {
32
+ "DepotID" "481"
33
+ "FileMapping" { "LocalPath" "*" "DepotPath" "." "recursive" "1" }
34
+ "FileExclusion" "*.pdb"
35
+ }
36
+ ```
37
+
38
+ Upload:
39
+
40
+ ```bash
41
+ steamcmd +login <builder_account> +run_app_build $(pwd)/app_build_APPID.vdf +quit
42
+ ```
43
+
44
+ - Push to a **beta branch** first, test, then set it as the default (live) branch in the partner site.
45
+ - Use a dedicated **build account** with 2FA handled for CI (Steam Guard); never the studio owner login.
46
+ - `.pdb`/debug symbols usually excluded from the shipped depot.
47
+
48
+ ## itch.io (butler)
49
+
50
+ `butler` is itch's CLI. A **channel** encodes the platform:
51
+
52
+ ```bash
53
+ butler push build/windows user/game:windows --userversion 1.2.3
54
+ butler push build/html5 user/game:html5 --userversion 1.2.3
55
+ butler push build/osx.zip user/game:mac --userversion 1.2.3
56
+ butler status user/game # see channels + versions
57
+ ```
58
+
59
+ - Channel names containing `windows`/`osx`/`linux`/`android` tag the platform for the itch app;
60
+ `html5`/`web` marks a browser build.
61
+ - For threaded web builds, in the uploaded file's settings tick **"This file will be played in
62
+ the browser" ▸ SharedArrayBuffer support** so itch serves COOP/COEP (see `web-export-headers.md`).
63
+ - CI auth: `BUTLER_API_KEY` env var (from itch API keys). `butler push` is idempotent and diff-based.
64
+
65
+ ## Google Play
66
+
67
+ - Upload an **AAB**; enable **Play App Signing**.
68
+ - Meet the current **target API level** requirement (Play enforces a recent level for new uploads and updates).
69
+ - Complete: **content rating** (IARC questionnaire), **Data safety** form, store listing (icon,
70
+ feature graphic, screenshots per device, short/full description), pricing/countries.
71
+ - Tracks: internal → closed → open → production. Promote a build up the tracks.
72
+ - `versionCode` strictly increasing; can't reuse one.
73
+
74
+ ## Apple App Store
75
+
76
+ - Archive + upload to **App Store Connect** (Xcode Organizer / Transporter).
77
+ - App Store record: name, subtitle, keywords, description, screenshots per device size, app icon,
78
+ privacy nutrition labels, **age rating** questionnaire, pricing.
79
+ - **TestFlight** for beta before submitting for review.
80
+ - The store notarizes automatically — no manual `notarytool` for App Store builds.
81
+
82
+ ## Age ratings (shared)
83
+
84
+ - **IARC** questionnaire generates ratings for Google Play, Microsoft Store, Nintendo eShop in one pass.
85
+ - **ESRB** (NA), **PEGI** (EU), **USK** (DE), **CERO** (JP) as required by platform/region.
86
+ - Apple has its own age-rating questionnaire in App Store Connect.
87
+ - Answer truthfully — misdeclaring content (violence, gambling, user-generated content) risks removal.
88
+
89
+ ## CI/CD — headless builds
90
+
91
+ Principles: run the export headless, feed a **version/build number** from the CI run, restore
92
+ **signing material from secrets** at runtime, upload the artifact.
93
+
94
+ ### Godot (GitHub Actions)
95
+
96
+ ```yaml
97
+ name: export
98
+ on: { push: { tags: ['v*'] } }
99
+ jobs:
100
+ build:
101
+ runs-on: ubuntu-latest
102
+ container: barichello/godot-ci:4.5.1 # image bundles matching export templates
103
+ steps:
104
+ - uses: actions/checkout@v4
105
+ - name: Prepare templates
106
+ run: |
107
+ mkdir -p ~/.local/share/godot/export_templates/4.5.1.stable
108
+ mv /root/.local/share/godot/export_templates/4.5.1.stable/* \
109
+ ~/.local/share/godot/export_templates/4.5.1.stable/
110
+ - name: Export Web + Linux
111
+ run: |
112
+ mkdir -p build/web build/linux
113
+ godot --headless --export-release "Web" build/web/index.html
114
+ godot --headless --export-release "Linux/X11" build/linux/game.x86_64
115
+ - uses: actions/upload-artifact@v4
116
+ with: { name: builds, path: build/ }
117
+ ```
118
+
119
+ The template version in the image **must** equal the editor build (`4.5.1` here). For Android in CI,
120
+ add JDK 17 + the Android SDK and restore the keystore from a secret.
121
+
122
+ ### Unity (GitHub Actions)
123
+
124
+ ```yaml
125
+ - uses: game-ci/unity-builder@v4
126
+ with:
127
+ targetPlatform: Android
128
+ buildMethod: BuildScript.PerformBuild
129
+ env:
130
+ UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
131
+ UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
132
+ UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
133
+ ```
134
+
135
+ - License activation is required (personal/pro); store it in secrets via `game-ci/activate`.
136
+ - Matrix over `targetPlatform` for multi-target. Keystore + passwords come from secrets for signed Android.
137
+
138
+ ### Unreal (self-hosted)
139
+
140
+ - The engine install is multi-GB, so Unreal CI needs a **self-hosted runner** with the engine + SDKs installed.
141
+ - Drive with `RunUAT BuildCookRun ... -clientconfig=Shipping -cook -stage -pak -package -archivedirectory=...`.
142
+
143
+ ### Versioning
144
+
145
+ ```yaml
146
+ env:
147
+ BUILD_NUMBER: ${{ github.run_number }} # monotonic → versionCode / CFBundleVersion / Steam desc
148
+ VERSION: ${{ github.ref_name }} # semver tag → versionName / marketing version
149
+ ```
150
+
151
+ - Semver tag (`v1.2.3`) triggers a release build; the run number is the strictly-increasing integer.
152
+ - Feed `BUILD_NUMBER` into Android `versionCode`, iOS `CFBundleVersion`, and the Steam build `desc`.
153
+ - Publish signing secrets (keystore base64, App Store Connect API key, `BUTLER_API_KEY`, `STEAM_*`) as encrypted repo/environment secrets — never in the workflow file.
154
+
155
+ ## Checklist
156
+
157
+ - [ ] Store page assets + metadata + age rating complete for the target store.
158
+ - [ ] Steam: depots mapped, SteamPipe VDFs correct, pushed to a beta branch first, build account with Steam Guard.
159
+ - [ ] itch: correct channel names, SAB checkbox for web, `BUTLER_API_KEY` in CI.
160
+ - [ ] Play: AAB + Play App Signing + target API level + Data safety.
161
+ - [ ] App Store: archive uploaded, TestFlight tested, privacy labels + age rating done.
162
+ - [ ] CI: headless export, version/build number from the run, signing material from secrets, artifact uploaded.
@@ -0,0 +1,140 @@
1
+ # Web export — cross-origin isolation, headers, and hosts
2
+
3
+ Back to the entrypoint: `../SKILL.md`. This is the single deepest source of "my web build
4
+ works locally but breaks when hosted" bugs. Read it before you host a threaded WebAssembly game.
5
+
6
+ ## Why the headers exist
7
+
8
+ Modern engines run the game loop on Web Workers and share memory between them with
9
+ **`SharedArrayBuffer`**. After Spectre/Meltdown, browsers only expose `SharedArrayBuffer` (and
10
+ high-resolution timers) to pages that are **cross-origin isolated**. A page becomes isolated only
11
+ when the server sends **both**:
12
+
13
+ ```
14
+ Cross-Origin-Opener-Policy: same-origin
15
+ Cross-Origin-Embedder-Policy: require-corp
16
+ ```
17
+
18
+ - **COOP: same-origin** severs the page from cross-origin windows/openers (its own browsing-context group).
19
+ - **COEP: require-corp** requires every subresource to explicitly opt in to being embedded (via CORP or CORS), so no untrusted cross-origin resource shares the isolated process.
20
+
21
+ With both present, `self.crossOriginIsolated === true` and `SharedArrayBuffer` is defined. Miss
22
+ either and you get one of: `SharedArrayBuffer is not defined`, a JS exception during engine init,
23
+ an infinite loading bar, or a blank canvas. The **files are correct** — the *response headers* are the bug.
24
+
25
+ Quick self-test in the browser console on the hosted page:
26
+
27
+ ```js
28
+ console.log(self.crossOriginIsolated); // must be true for threaded builds
29
+ console.log(typeof SharedArrayBuffer); // must be "function"
30
+ ```
31
+
32
+ ## Do you even need the headers?
33
+
34
+ | Situation | Headers needed? |
35
+ | --- | --- |
36
+ | Godot 4 web export with **Thread Support ON** | Yes — both. |
37
+ | Godot 4 web export with Thread Support OFF (single-thread) | No — but audio/perf are worse. |
38
+ | Unity WebGL with **multithreading** / threads enabled | Yes — both. |
39
+ | Unity WebGL single-threaded (default older setups) | No. |
40
+ | A build that only *uses* WASM but not threads/SAB | No. |
41
+
42
+ If you can host somewhere that sets headers, **keep threads on** — it's a real perf win. Only
43
+ disable threads when your host genuinely can't set headers and the shim below isn't viable.
44
+
45
+ ## Setting the headers per host
46
+
47
+ **Netlify** — `public/_headers` (or the publish dir):
48
+
49
+ ```
50
+ /*
51
+ Cross-Origin-Opener-Policy: same-origin
52
+ Cross-Origin-Embedder-Policy: require-corp
53
+ ```
54
+
55
+ **Vercel** — `vercel.json`:
56
+
57
+ ```json
58
+ {
59
+ "headers": [{
60
+ "source": "/(.*)",
61
+ "headers": [
62
+ { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
63
+ { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
64
+ ]
65
+ }]
66
+ }
67
+ ```
68
+
69
+ **nginx**:
70
+
71
+ ```nginx
72
+ location / {
73
+ add_header Cross-Origin-Opener-Policy same-origin always;
74
+ add_header Cross-Origin-Embedder-Policy require-corp always;
75
+ # serve pre-compressed wasm/pck if present
76
+ gzip_static on;
77
+ }
78
+ # correct MIME + long cache for the payload
79
+ types { application/wasm wasm; }
80
+ ```
81
+
82
+ **Apache** — `.htaccess`:
83
+
84
+ ```apache
85
+ Header set Cross-Origin-Opener-Policy "same-origin"
86
+ Header set Cross-Origin-Embedder-Policy "require-corp"
87
+ AddType application/wasm .wasm
88
+ ```
89
+
90
+ **Cloudflare Pages** — a `_headers` file like Netlify's, or a Transform Rule adding both response headers.
91
+
92
+ **itch.io** — no file needed: in the upload settings tick **"This file will be played in the
93
+ browser"**, then check **"SharedArrayBuffer support"**. itch serves your build from a domain that
94
+ sends COOP/COEP for you. This is the fastest correct web host for a jam/demo.
95
+
96
+ **GitHub Pages / plain static CDN with no header control** — you **cannot** set response headers.
97
+ Two escapes:
98
+
99
+ 1. Export **without** threads (single-threaded) — always works, no headers, some perf loss.
100
+ 2. The **`coi-serviceworker`** shim: a small service worker (`coi-serviceworker.js`) you include
101
+ from the page; on first load it registers, reloads once, and thereafter re-serves every
102
+ response with COOP/COEP injected, faking cross-origin isolation client-side. Caveats: it needs
103
+ HTTPS, adds a reload, and won't help resources it can't intercept. Good enough for GitHub Pages demos.
104
+
105
+ ## COEP fallout — the second-order breakage
106
+
107
+ `COEP: require-corp` also applies to **every** subresource the page loads. Anything cross-origin
108
+ (web fonts, analytics scripts, remote images, iframes, a leaderboard API on another domain) is
109
+ **blocked** unless it sends `Cross-Origin-Resource-Policy: cross-origin` (or proper CORS). Symptoms:
110
+ fonts vanish, a third-party embed 404s in the network tab with a CORP error.
111
+
112
+ Fixes, in order of preference:
113
+ 1. **Self-host** the asset (same origin ⇒ no CORP needed). Best for fonts and small scripts.
114
+ 2. Ask the third party to send `Cross-Origin-Resource-Policy: cross-origin` (or use their CORS endpoint).
115
+ 3. Switch that page to **`COEP: credentialless`** — a looser mode that loads cross-origin resources
116
+ without credentials and still grants isolation in supporting browsers. Narrower support than `require-corp`; test.
117
+
118
+ ## Engine specifics
119
+
120
+ **Godot 4 web**
121
+ - `Editor ▸ Manage Export Templates` must match the editor build; then a **Web** export preset.
122
+ - **Thread Support** toggle drives whether you need the headers. **Extensions Support** enables GDExtension on web.
123
+ - Output: `index.html` + `.js` + `.wasm` + `.pck` (+ worker/audio files). Serve `.wasm` as `application/wasm` and enable gzip/brotli — the `.wasm`/`.pck` are the bulk of the download.
124
+ - **C#/.NET edition has no web export in Godot 4.x** (planned for a later release with .NET 10). If the project is C#, either port the web-facing build to GDScript or drop web as a target. Do not promise a C# web build.
125
+ - Headless CI export: `godot --headless --export-release "Web" build/index.html`.
126
+
127
+ **Unity WebGL**
128
+ - `File ▸ Build Profiles ▸ Web`; Player Settings ▸ Publishing: enable/disable threads, set the
129
+ **compression format** (Brotli/Gzip) and keep **Decompression Fallback** on if the host can't
130
+ send `Content-Encoding` — otherwise the browser can't inflate the payload.
131
+ - Threaded WebGL ⇒ same COOP/COEP requirement.
132
+
133
+ ## Checklist
134
+
135
+ - [ ] Decide threads on/off; if on, both headers are mandatory.
136
+ - [ ] Headers set at the **host** (or itch's SAB checkbox, or the shim on GitHub Pages).
137
+ - [ ] `self.crossOriginIsolated === true` verified in the console on the deployed URL.
138
+ - [ ] `.wasm` served with `application/wasm` MIME and gzip/brotli.
139
+ - [ ] No cross-origin subresource silently blocked by COEP (check the network tab).
140
+ - [ ] Godot + web ⇒ confirmed GDScript, not C#/.NET.
@@ -0,0 +1,319 @@
1
+ ---
2
+ name: godot
3
+ description: "Use when writing, reviewing, or debugging Godot 4.x games in GDScript (typed) or C# — nodes/scenes/custom Resources, autoload singletons and an EventBus, signals via Callable, @onready/@export/@tool, static typing, CharacterBody2D/3D + velocity + move_and_slide(), _process vs _physics_process, .tscn/.tres text formats, and headless GUT/gdUnit4 tests. Enforces Godot 4 APIs and rejects Godot 3 leftovers: yield→await, onready→@onready, .instance()→.instantiate(), KinematicBody→CharacterBody, Spatial→Node3D, string connect(\"sig\",obj,\"m\")→Callable. Triggers: 'write a Godot player controller', 'my move_and_slide does nothing', 'port this Godot 3 script to 4', 'connect a signal in Godot', 'GDScript won't type-check', 'CharacterBody2D won't move', 'add an autoload', 'escribe un script de Godot', 'por qué no se mueve mi personaje'. NOT Unity MonoBehaviour or Unreal (route to unity/unreal); NOT pure C++/CMake tooling unrelated to GDExtension (that is cpp); NOT shader code in .gdshader (that is gamedev-shaders)."
4
+ tags: [godot, gdscript, game-dev, csharp, godot4]
5
+ recommends: [gamedev-shaders, gamedev-multiplayer, gamedev-physics, gamedev-pathing, gamedev-shipping, game-design, cpp]
6
+ profiles: [full]
7
+ origin: risco
8
+ ---
9
+
10
+ # Godot 4.x (GDScript + C#)
11
+
12
+ Write, review, and debug Godot **4.x** games — 2D and 3D — the way the engine is designed:
13
+ scenes as reusable units, composition over deep node trees, signals for decoupling, static
14
+ typing for speed and safety. GDScript (typed) is primary; C# parity snippets sit beside it.
15
+
16
+ ## Version contract — read first
17
+
18
+ **This is Godot 4.x. Never emit Godot 3 APIs.** Godot 4 renamed core nodes, moved annotations
19
+ behind `@`, replaced `yield` with `await`, and switched signal/file/tween APIs. A Godot 3 snippet
20
+ will not even parse in 4.x. Before writing or accepting any line, check it against this ban-list:
21
+
22
+ | Never (Godot 3) | Always (Godot 4.x) |
23
+ | --- | --- |
24
+ | `yield(timer, "timeout")` | `await timer.timeout` |
25
+ | `onready var x = ...` | `@onready var x = ...` |
26
+ | `export var hp = 3` | `@export var hp := 3` |
27
+ | `tool` (script mode line) | `@tool` (annotation, first line) |
28
+ | `KinematicBody` / `KinematicBody2D` | `CharacterBody3D` / `CharacterBody2D` |
29
+ | `Spatial` | `Node3D` |
30
+ | `Area` / `RigidBody` / `StaticBody` | `Area3D` / `RigidBody3D` / `StaticBody3D` |
31
+ | `Sprite` | `Sprite2D` |
32
+ | `scene.instance()` | `scene.instantiate()` |
33
+ | `move_and_slide(velocity, UP)` (positional) | set `velocity` property, then `move_and_slide()` — **no args** |
34
+ | `connect("hit", self, "_on_hit")` | `node.hit.connect(_on_hit)` (Callable) |
35
+ | `File.new()` / `Directory.new()` | `FileAccess.open(...)` / `DirAccess.open(...)` |
36
+ | standalone `Tween` node + `interpolate_property` | `create_tween()` → `tween.tween_property(...)` |
37
+ | `OS.get_ticks_msec` for gameplay timing | `Time.get_ticks_msec()` (OS timing moved to `Time`) |
38
+ | `PoolByteArray` / `PoolVector2Array` | `PackedByteArray` / `PackedVector2Array` |
39
+
40
+ **Lifecycle overrides must chain the parent** with `super()` (Godot 3 called it implicitly; Godot 4
41
+ does not). If you override `_ready`, `_process`, `_init`, etc. in a script that `extends` another
42
+ script defining them, call `super()` / `super._ready()` or the base logic silently never runs.
43
+
44
+ **Silent-breakers** (compile fine, behave wrong — the dangerous class):
45
+
46
+ - `Array.slice(begin, end)` — `end` is now **exclusive** (was inclusive in Godot 3). `[1,2,3,4].slice(1,3)` → `[2,3]`.
47
+ - `Camera2D.zoom` is **inverted** vs Godot 3: a **larger** zoom now means zoomed **in** (magnified). `Vector2(2,2)` = 2× magnification, not half.
48
+ - `TileMap` is **deprecated** → use one `TileMapLayer` node per layer (since 4.3).
49
+ - Angles are radians; `_process(delta)` delta is a `float` (GDScript) / `double` (C#) in **seconds**.
50
+
51
+ Full table with every rename → `references/godot3-to-4-traps.md`.
52
+
53
+ ## Project & scene organization
54
+
55
+ - The **scene** (`.tscn`) is the reusable unit — a self-contained tree you instance many times
56
+ (a Player, a Bullet, a HUD). Prefer **composition**: small scenes/nodes assembled, not one
57
+ 60-node monolith. If a subtree has its own behavior, make it its own scene.
58
+ - One responsibility per script. Attach behavior to the scene's **root**; child nodes are parts.
59
+ - `class_name Foo` registers a global type usable in the inspector and as `Foo.new()`. Use it for
60
+ reusable scripts and custom Resources; skip it for one-off scene scripts.
61
+ - **Files**: `snake_case.gd` / `snake_case.tscn` for scenes and scripts; `PascalCase` for node
62
+ names in the tree and for `class_name`. Group by feature (`player/`, `enemy/`, `ui/`), not by type.
63
+
64
+ ## Nodes vs scenes vs scripts vs custom Resources
65
+
66
+ | You need | Use |
67
+ | --- | --- |
68
+ | A thing in the tree that renders / moves / collides / processes | a **Node** (typed subclass) |
69
+ | A reusable, instanceable bundle of nodes | a **scene** (`.tscn`) |
70
+ | Behavior attached to a node | a **script** (`.gd` / `.cs`) |
71
+ | Pure data (stats, items, dialogue, level config) with no place in the tree | a **custom `Resource`** (`.tres`) |
72
+
73
+ Custom `Resource`s are Godot's typed, savable, inspector-editable data objects — reach for them
74
+ instead of loose Dictionaries or JSON for game data. See `references/nodes-scenes-resources.md`.
75
+
76
+ ```gdscript
77
+ class_name EnemyStats extends Resource
78
+ @export var max_health: int = 30
79
+ @export var speed: float = 120.0
80
+ @export var loot_table: Array[ItemDrop] = []
81
+ ```
82
+
83
+ ## Autoloads / singletons + EventBus
84
+
85
+ Register a script or scene as an **autoload** (Project → Project Settings → Globals/Autoload) to get
86
+ one always-present instance reachable by name from anywhere. Use it for cross-cutting state (save
87
+ game, audio, run config) — not as a dumping ground.
88
+
89
+ The **EventBus** pattern decouples unrelated systems: an autoload that owns only signals. Emitters
90
+ and listeners never reference each other, just the bus.
91
+
92
+ ```gdscript
93
+ # event_bus.gd (autoload named "Events")
94
+ extends Node
95
+ signal enemy_died(position: Vector2, xp: int)
96
+ signal score_changed(new_score: int)
97
+ ```
98
+ ```gdscript
99
+ # emitter # listener (anywhere)
100
+ Events.enemy_died.emit(global_position, 10)
101
+ Events.enemy_died.connect(_on_enemy_died)
102
+ ```
103
+
104
+ Keep gameplay logic in nodes; let the bus carry the *notification*, not the behavior.
105
+
106
+ ## Node access & lifecycle
107
+
108
+ - `_init()` runs at construction (no tree, no `@onready` yet). `_ready()` runs once the node and
109
+ all children are in the tree — do node wiring here.
110
+ - `@onready var x = $Path` defers the assignment to `_ready`, so the child exists. Never grab
111
+ children in `_init`.
112
+ - **Prefer unique names**: mark a node **Unique Name in Owner** (`%`) and access `%HealthBar`
113
+ instead of the fragile, refactor-breaking `get_node("../../UI/HealthBar")`. `$Foo` is fine for a
114
+ direct child.
115
+ - Cache node lookups in `@onready` vars; don't call `get_node` every frame.
116
+
117
+ ```gdscript
118
+ extends CharacterBody2D
119
+ @onready var sprite: Sprite2D = $Sprite2D
120
+ @onready var health_bar: ProgressBar = %HealthBar # unique name, position-independent
121
+
122
+ func _ready() -> void:
123
+ super() # chain the parent's _ready if the base defines one
124
+ health_bar.value = 100
125
+ ```
126
+
127
+ ## Signals
128
+
129
+ Signals are Godot's decoupling primitive. In Godot 4 you connect a **Callable**, not strings.
130
+
131
+ - **Declare** with typed params; **name in the past tense** for facts that happened
132
+ (`health_depleted`, `item_collected`), present-tense imperative only for requests.
133
+ - **Connect**: `node.signal_name.connect(_on_thing)` — a direct method reference, checked at parse
134
+ time. Add `CONNECT_ONE_SHOT` for auto-disconnect after one fire.
135
+ - **Disconnect discipline**: a connection to a node that gets freed is cleaned up automatically, but
136
+ connections you make to *long-lived* objects (autoloads, the bus) from a short-lived node must be
137
+ disconnected in `_exit_tree()`, or use `CONNECT_ONE_SHOT`, to avoid calls into freed instances.
138
+
139
+ ```gdscript
140
+ signal health_depleted
141
+ signal health_changed(current: int, max: int)
142
+
143
+ func take_damage(amount: int) -> void:
144
+ _health -= amount
145
+ health_changed.emit(_health, _max_health)
146
+ if _health <= 0:
147
+ health_depleted.emit()
148
+ ```
149
+
150
+ ## @export / @tool (inspector config)
151
+
152
+ `@export` exposes a variable in the Inspector so designers tune it without touching code. Use ranges,
153
+ groups, and typed exports so the inspector gives real widgets and validation.
154
+
155
+ ```gdscript
156
+ @export var title: String = "Level 1"
157
+ @export_range(0.0, 1.0, 0.05) var volume := 0.8
158
+ @export_group("Movement")
159
+ @export var speed: float = 300.0
160
+ @export var jump_velocity: float = -400.0
161
+ @export var projectile: PackedScene # drag a .tscn in the inspector
162
+ @export var stats: EnemyStats # a custom Resource slot
163
+ ```
164
+
165
+ `@tool` at the top of a script runs it **in the editor** too — for gizmos, procedural previews, or
166
+ validating exported data. Guard runtime-only code with `if Engine.is_editor_hint(): return`.
167
+
168
+ ## Static (typed) GDScript
169
+
170
+ Type everything. Typed GDScript is faster (the VM skips dynamic dispatch) and catches errors at parse
171
+ time. Use `:=` when the type is inferable, `: Type` when it isn't, and avoid `Variant`/untyped.
172
+
173
+ ```gdscript
174
+ var speed: float = 300.0 # explicit
175
+ var dir := Vector2.ZERO # inferred
176
+ var enemies: Array[Enemy] = [] # typed array
177
+
178
+ func distance_to(target: Node2D) -> float:
179
+ return global_position.distance_to(target.global_position)
180
+
181
+ func _on_body_entered(body: Node) -> void:
182
+ var enemy := body as Enemy # safe cast → null if wrong type, no crash
183
+ if enemy:
184
+ enemy.take_damage(10)
185
+ ```
186
+
187
+ Naming: `snake_case` vars/funcs/signals, `PascalCase` types/`class_name`/nodes, `CONSTANT_CASE`
188
+ consts, tabs for indent, lines < 100 cols. Cheat-sheet → `references/gdscript-style.md`.
189
+
190
+ ## `_process` vs `_physics_process`
191
+
192
+ - `_physics_process(delta)` — **fixed** tick (default 60 Hz), the same every step. All movement,
193
+ `move_and_slide()`, forces, and collision-dependent logic go here.
194
+ - `_process(delta)` — runs **once per rendered frame** (variable rate). Use for visuals, UI, and
195
+ non-physics polish.
196
+ - **Always scale rate-based change by `delta`** so behavior is framerate-independent. `move_and_slide()`
197
+ and `move_and_collide()` already fold in `delta` internally — do **not** multiply the velocity you
198
+ hand them by `delta` again.
199
+
200
+ ## Resources & data — `.tres` / `.tscn` are strict text formats
201
+
202
+ `.tscn` and `.tres` are line-oriented text with a strict header/section grammar. **Do not hand-edit
203
+ them past trivial value tweaks, and never launch on a file you hand-authored without validating** —
204
+ one bad `ext_resource` id, `[node]` line, or `load_steps` count corrupts the whole scene and Godot
205
+ refuses to open it. Prefer editing through the editor or building Resources in code and `ResourceSaver.save()`.
206
+
207
+ - **`preload("res://x.tscn")`** resolves at **parse/compile** time — the dependency is baked in; use
208
+ for assets you always need.
209
+ - **`load("res://x.tscn")`** resolves at **runtime** — use for dynamic/optional paths (and to avoid
210
+ circular preloads). Both return a `PackedScene`; call `.instantiate()` to get a node.
211
+
212
+ ## 2D / 3D bodies quickstart
213
+
214
+ The move-anything-controllable body is `CharacterBody2D` / `CharacterBody3D`. The Godot 4 flow is:
215
+ **write the `velocity` property, then call `move_and_slide()` with no arguments.**
216
+
217
+ ```gdscript
218
+ extends CharacterBody2D
219
+ @export var speed: float = 300.0
220
+ @export var jump_velocity: float = -400.0
221
+
222
+ func _physics_process(delta: float) -> void:
223
+ if not is_on_floor():
224
+ velocity += get_gravity() * delta # get_gravity(): project-configured vector
225
+ if Input.is_action_just_pressed("jump") and is_on_floor():
226
+ velocity.y = jump_velocity
227
+ var dir := Input.get_axis("move_left", "move_right")
228
+ velocity.x = dir * speed
229
+ move_and_slide() # NO args in Godot 4 — reads the velocity property
230
+ ```
231
+
232
+ 3D is identical with `CharacterBody3D`, `Vector3`, and an X/Z input plane; body/area suffixes are
233
+ `3D`. Deeper body/physics tuning → `gamedev-physics`.
234
+
235
+ ## Language parity — GDScript ↔ C#
236
+
237
+ Same engine, same nodes; C# uses PascalCase members, `partial` classes, and attributes. Signals
238
+ become C# `event`s (generated by source-gen). C# support requires the **.NET (Mono) build** of Godot.
239
+
240
+ ```gdscript
241
+ # GDScript
242
+ extends Node
243
+ signal health_depleted
244
+ @export var speed: float = 300.0
245
+
246
+ func _ready() -> void:
247
+ health_depleted.connect(_on_depleted)
248
+ health_depleted.emit()
249
+
250
+ func _on_depleted() -> void:
251
+ print("dead")
252
+ ```
253
+ ```csharp
254
+ // C# — same node, .NET build
255
+ using Godot;
256
+
257
+ public partial class Player : Node
258
+ {
259
+ [Signal] public delegate void HealthDepletedEventHandler();
260
+ [Export] public float Speed { get; set; } = 300.0f;
261
+
262
+ public override void _Ready()
263
+ {
264
+ base._Ready(); // chain the parent (== super())
265
+ HealthDepleted += OnDepleted; // connect via the generated event
266
+ EmitSignal(SignalName.HealthDepleted);
267
+ }
268
+
269
+ private void OnDepleted() => GD.Print("dead");
270
+ }
271
+ ```
272
+
273
+ `move_and_slide()`→`MoveAndSlide()`, `$Node`→`GetNode<T>("Node")`, `%Node`→`GetNode<T>("%Node")`,
274
+ `preload`→`GD.Load<T>(...)`. Full parity table → `references/export-and-testing.md`.
275
+
276
+ **GDExtension / C++**: for hot native code, build a `godot-cpp` GDExtension (`.gdextension` file,
277
+ `GDREGISTER_CLASS`, `_bind_methods()`) rather than a Godot module — no engine recompile, and it loads
278
+ like any other library. That is native-tooling territory → pair with `cpp`.
279
+
280
+ ## Export & testing
281
+
282
+ - **Test with GUT 9.x** (GDScript, `extends GutTest`) or **gdUnit4** (GDScript + C#). Put tests
283
+ under `test/` or `res://tests/`; assert behavior, not private state.
284
+ - **Run headless in CI**: `godot --headless -s addons/gut/gut_cmdln.gd -gdir=res://test -gexit`
285
+ (GUT). Export via templates: `godot --headless --export-release "Linux/X11" build/game.x86_64`.
286
+ - Validate a project before shipping: open in the editor once (catches broken `.tscn`/`.tres`),
287
+ then export-check per platform. Details → `references/export-and-testing.md`.
288
+
289
+ ## Guardrails / gotchas
290
+
291
+ - Freeing: call `queue_free()` (safe, end of frame), not `free()` mid-signal. Guard reused refs with
292
+ `is_instance_valid(node)`.
293
+ - Never busy-wait; `await get_tree().create_timer(1.0).timeout` or a signal.
294
+ - `get_node`/`$` on a missing path returns `null` and errors — prefer `%UniqueName` and typed `@onready`.
295
+ - Don't multiply by `delta` twice (see `move_and_slide`). Don't put physics in `_process`.
296
+ - One `TileMapLayer` per layer; `TileMap` is deprecated.
297
+ - Editor-touching code must guard on `Engine.is_editor_hint()` under `@tool`.
298
+
299
+ ## Related skills
300
+
301
+ - [`game-design`](../game-design/SKILL.md) — mechanics, loops, and feel *before* you script them.
302
+ - [`gamedev-shaders`](../gamedev-shaders/SKILL.md) — `.gdshader` / visual shaders (out of scope here).
303
+ - [`gamedev-physics`](../gamedev-physics/SKILL.md) — joints, RigidBody tuning, deep collision layers.
304
+ - [`gamedev-pathing`](../gamedev-pathing/SKILL.md) — NavigationAgent, A*, steering beyond the basics.
305
+ - [`gamedev-multiplayer`](../gamedev-multiplayer/SKILL.md) — `MultiplayerSynchronizer`/RPC/netcode.
306
+ - [`gamedev-shipping`](../gamedev-shipping/SKILL.md) — store builds, signing, platform export at scale.
307
+ - [`cpp`](../cpp/SKILL.md) — the GDExtension / `godot-cpp` native side and its CMake/build tooling.
308
+
309
+ ## Checklist
310
+
311
+ - [ ] Zero Godot 3 APIs — checked every line against the ban-list (`yield`/`onready`/`instance()`/`KinematicBody`/`Spatial`/string `connect`/`File`).
312
+ - [ ] `move_and_slide()` called with **no args**; `velocity` set as a property; not multiplied by `delta` twice.
313
+ - [ ] Overridden lifecycle methods call `super()` where a base defines them.
314
+ - [ ] Signals connected via Callable (`x.sig.connect(_on_x)`), named past-tense, disconnected/one-shot where needed.
315
+ - [ ] Everything typed (`:` / `:=`, typed params/returns, typed Arrays); no stray `Variant`.
316
+ - [ ] Physics in `_physics_process`, visuals in `_process`; rate-based values scaled by `delta`.
317
+ - [ ] Node access via `@onready` + `%UniqueName`, not deep `get_node("../..")` chains.
318
+ - [ ] `.tscn`/`.tres` not hand-corrupted; project opens in-editor before export.
319
+ - [ ] C# snippets use `partial`, `[Export]`/`[Signal]`, `base._Ready()`, PascalCase (if C# requested).
@@ -0,0 +1,13 @@
1
+ # Eval harness — `godot` skill
2
+
3
+ These cases are read by the rsc skill-eval harness (a Claude agent with the full skill catalog
4
+ loaded) or by hand. They assert two things. **Triggering**: the `godot` description fires on each
5
+ `should_trigger` prompt — including symptom-only and Spanish phrasings — and stays quiet on the
6
+ `should_not_trigger` near-misses, routing each to its named sibling (`unity`, `unreal`,
7
+ `gamedev-shaders`, `cpp`, `game-design`, `gamedev-multiplayer`) instead. **Capability**: with
8
+ `SKILL.md` and its references loaded, the generated Godot code satisfies every
9
+ `capability.must_include` bullet — the Godot 4 flow (`velocity` property + no-arg `move_and_slide()`),
10
+ typed GDScript, Callable signals, `@export`/`@onready`, zero Godot 3 leftovers, and a correct C#
11
+ parity block — and clearly beats the no-skill baseline. Because LLM routing is non-deterministic,
12
+ run 3–5 trials per prompt and pass on a majority. No network is required; the capability rubric is
13
+ judged by reading the output against the bullets.