@ericrisco/rsc 0.1.34 → 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 (130) hide show
  1. package/README.md +28 -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/agent-eval/SKILL.md +1 -1
  7. package/skills/analyze/SKILL.md +1 -1
  8. package/skills/brand-voice/SKILL.md +1 -1
  9. package/skills/brand-voice/references/voice-guide-template.md +14 -0
  10. package/skills/building-agents/SKILL.md +2 -2
  11. package/skills/competitor-watch/SKILL.md +9 -4
  12. package/skills/competitor-watch/references/tracker-schema.md +19 -0
  13. package/skills/constitution/references/constitution-template.md +10 -0
  14. package/skills/content-engine/SKILL.md +1 -1
  15. package/skills/content-engine/references/brief-and-pipeline.md +15 -1
  16. package/skills/course-builder/references/grounding-and-scoping.md +23 -0
  17. package/skills/course-storytelling/references/learner-grounding.md +22 -2
  18. package/skills/decision-records/SKILL.md +14 -1
  19. package/skills/decision-records/references/templates.md +20 -0
  20. package/skills/design/references/brand-grounding.md +11 -1
  21. package/skills/game-design/SKILL.md +170 -0
  22. package/skills/game-design/evals/README.md +13 -0
  23. package/skills/game-design/evals/cases.yaml +47 -0
  24. package/skills/game-design/references/mechanics-and-loops.md +78 -0
  25. package/skills/game-design/references/playtesting-and-scope.md +58 -0
  26. package/skills/game-design/references/progression-and-economy.md +70 -0
  27. package/skills/game-storytelling/SKILL.md +159 -0
  28. package/skills/game-storytelling/evals/README.md +28 -0
  29. package/skills/game-storytelling/evals/cases.yaml +51 -0
  30. package/skills/game-storytelling/references/branching-and-state.md +134 -0
  31. package/skills/game-storytelling/references/dialogue-tools.md +117 -0
  32. package/skills/game-storytelling/references/environmental-storytelling.md +71 -0
  33. package/skills/gamedev-multiplayer/SKILL.md +250 -0
  34. package/skills/gamedev-multiplayer/evals/README.md +12 -0
  35. package/skills/gamedev-multiplayer/evals/cases.yaml +44 -0
  36. package/skills/gamedev-multiplayer/references/godot-high-level-multiplayer.md +133 -0
  37. package/skills/gamedev-multiplayer/references/prediction-and-latency.md +104 -0
  38. package/skills/gamedev-multiplayer/references/unity-and-unreal-netcode.md +166 -0
  39. package/skills/gamedev-pathing/SKILL.md +242 -0
  40. package/skills/gamedev-pathing/evals/README.md +14 -0
  41. package/skills/gamedev-pathing/evals/cases.yaml +49 -0
  42. package/skills/gamedev-pathing/references/navmesh-workflow.md +110 -0
  43. package/skills/gamedev-pathing/references/search-algorithms.md +135 -0
  44. package/skills/gamedev-pathing/references/steering-and-avoidance.md +120 -0
  45. package/skills/gamedev-physics/SKILL.md +225 -0
  46. package/skills/gamedev-physics/evals/README.md +12 -0
  47. package/skills/gamedev-physics/evals/cases.yaml +54 -0
  48. package/skills/gamedev-physics/references/character-controllers.md +120 -0
  49. package/skills/gamedev-physics/references/determinism-and-queries.md +124 -0
  50. package/skills/gamedev-physics/references/layers-and-masks.md +115 -0
  51. package/skills/gamedev-shaders/SKILL.md +252 -0
  52. package/skills/gamedev-shaders/evals/README.md +14 -0
  53. package/skills/gamedev-shaders/evals/cases.yaml +45 -0
  54. package/skills/gamedev-shaders/references/effect-recipes.md +205 -0
  55. package/skills/gamedev-shaders/references/godot-shading-language.md +133 -0
  56. package/skills/gamedev-shaders/references/unity-and-unreal-shaders.md +169 -0
  57. package/skills/gamedev-shipping/SKILL.md +236 -0
  58. package/skills/gamedev-shipping/evals/README.md +9 -0
  59. package/skills/gamedev-shipping/evals/cases.yaml +55 -0
  60. package/skills/gamedev-shipping/references/mobile-and-desktop.md +127 -0
  61. package/skills/gamedev-shipping/references/stores-and-ci.md +162 -0
  62. package/skills/gamedev-shipping/references/web-export-headers.md +140 -0
  63. package/skills/godot/SKILL.md +319 -0
  64. package/skills/godot/evals/README.md +13 -0
  65. package/skills/godot/evals/cases.yaml +50 -0
  66. package/skills/godot/references/export-and-testing.md +102 -0
  67. package/skills/godot/references/gdscript-style.md +84 -0
  68. package/skills/godot/references/godot3-to-4-traps.md +89 -0
  69. package/skills/godot/references/nodes-scenes-resources.md +109 -0
  70. package/skills/harness/SKILL.md +3 -3
  71. package/skills/harness/references/daily-curation-automation.md +8 -6
  72. package/skills/harness/references/obsidian-scaffolding.md +16 -9
  73. package/skills/harness/references/wiki-article-template.md +31 -14
  74. package/skills/harness/references/wiki-index-template.md +7 -3
  75. package/skills/harness/references/wiki-protocol.md +120 -54
  76. package/skills/harness/references/wiki-worklog-template.md +5 -1
  77. package/skills/instagram-api/SKILL.md +8 -1
  78. package/skills/instagram-api/references/insights-metrics.md +8 -1
  79. package/skills/knowledge-ops/SKILL.md +9 -9
  80. package/skills/knowledge-ops/evals/cases.yaml +1 -1
  81. package/skills/knowledge-ops/references/gardening-playbook.md +18 -8
  82. package/skills/level-design/SKILL.md +175 -0
  83. package/skills/level-design/evals/README.md +35 -0
  84. package/skills/level-design/evals/cases.yaml +58 -0
  85. package/skills/level-design/references/blockout-to-art.md +118 -0
  86. package/skills/level-design/references/guidance-and-readability.md +152 -0
  87. package/skills/level-design/references/pacing-and-encounters.md +142 -0
  88. package/skills/linkedin-api/SKILL.md +10 -1
  89. package/skills/linkedin-strategy/SKILL.md +12 -0
  90. package/skills/linkedin-strategy/references/wiki-records.md +27 -1
  91. package/skills/marketing/references/brand-grounding.md +22 -2
  92. package/skills/medium-strategy/SKILL.md +12 -0
  93. package/skills/medium-strategy/references/wiki-records.md +39 -1
  94. package/skills/medium-writing/SKILL.md +19 -0
  95. package/skills/plan/references/plan-template.md +12 -1
  96. package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
  97. package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
  98. package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
  99. package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
  100. package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
  101. package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
  102. package/skills/shortform-packaging/SKILL.md +7 -2
  103. package/skills/shortform-packaging/references/package-templates.md +13 -2
  104. package/skills/shortform-strategy/SKILL.md +30 -6
  105. package/skills/shortform-strategy/references/learning-loop-template.md +9 -1
  106. package/skills/specify/references/spec-template.md +12 -1
  107. package/skills/tiktok-api/SKILL.md +6 -2
  108. package/skills/tiktok-api/references/wiki-schema.md +38 -4
  109. package/skills/unity/SKILL.md +282 -0
  110. package/skills/unity/evals/README.md +8 -0
  111. package/skills/unity/evals/cases.yaml +43 -0
  112. package/skills/unity/references/build-and-platforms.md +65 -0
  113. package/skills/unity/references/input-system.md +102 -0
  114. package/skills/unity/references/lifecycle-deep-dive.md +75 -0
  115. package/skills/unreal/SKILL.md +270 -0
  116. package/skills/unreal/evals/README.md +12 -0
  117. package/skills/unreal/evals/cases.yaml +45 -0
  118. package/skills/unreal/references/blueprint-cpp-parity.md +109 -0
  119. package/skills/unreal/references/gameplay-framework.md +95 -0
  120. package/skills/unreal/references/packaging.md +83 -0
  121. package/skills/verify/SKILL.md +11 -1
  122. package/skills/youtube-api/SKILL.md +7 -2
  123. package/skills/youtube-api/references/wiki-schema.md +25 -4
  124. package/skills/youtube-packaging/SKILL.md +10 -2
  125. package/skills/youtube-packaging/references/description-and-chapters.md +70 -2
  126. package/skills/youtube-strategy/SKILL.md +9 -2
  127. package/skills/youtube-strategy/references/wiki-records.md +39 -6
  128. package/skills/youtube-strategy/scripts/verify.sh +4 -0
  129. package/skills/youtube-thumbnails/SKILL.md +10 -5
  130. package/skills/youtube-thumbnails/references/experiment-log-format.md +28 -2
@@ -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.
@@ -0,0 +1,50 @@
1
+ skill: godot
2
+
3
+ should_trigger:
4
+ - prompt: "Write a Godot 4 top-down player controller for a CharacterBody2D with 8-direction movement and a dash."
5
+ why: "Core use case: authoring a typed GDScript CharacterBody2D using the velocity + move_and_slide() flow."
6
+ - prompt: "My CharacterBody2D just won't move — I call move_and_slide(velocity) every frame and nothing happens. What's wrong?"
7
+ why: "Symptom-only trigger routing here via the Godot 4 velocity-property + no-arg move_and_slide fix (they passed the Godot 3 positional arg)."
8
+ - prompt: "Convert this Godot 3 script to Godot 4 — it uses onready, yield, .instance() and connect(\"hit\", self, \"_on_hit\")."
9
+ why: "The version-contract ban-list is the skill's center of gravity; each named API has a modern replacement here."
10
+ - prompt: "How do I connect a signal between two nodes in Godot and pass the health value along?"
11
+ why: "Signals via Callable (node.sig.connect / .emit) with typed params is a named trigger."
12
+ - prompt: "Set up an autoload EventBus in Godot so my enemies can broadcast that they died without referencing the score UI."
13
+ why: "Autoload/singleton + EventBus decoupling pattern is explicitly owned by this skill."
14
+ - prompt: "My GDScript won't type-check — how do I properly type an Array of a custom class and cast a colliding body?"
15
+ why: "Static typed GDScript (typed Arrays, `as` casts, typed params/returns) is a core section."
16
+ - prompt: "Show me the same Godot player script in both GDScript and C#, including how the signal and export differ."
17
+ why: "GDScript ↔ C# parity (partial class, [Export]/[Signal], base._Ready()) is a first-class deliverable."
18
+ - prompt: "Escribe un script de Godot 4 para que un CharacterBody3D salte y tenga gravedad."
19
+ why: "Spanish trigger for a Godot 4 CharacterBody3D controller — squarely this skill."
20
+
21
+ should_not_trigger:
22
+ - prompt: "Write a Unity C# MonoBehaviour that moves a Rigidbody with FixedUpdate and input."
23
+ route_to: "unity"
24
+ why: "Unity's MonoBehaviour/Rigidbody/FixedUpdate model is a different engine; C# alone doesn't make it Godot."
25
+ - prompt: "Set up an Unreal Engine character with a Blueprint jump and a C++ movement component."
26
+ route_to: "unreal"
27
+ why: "Unreal Blueprint/UE C++ is a separate engine skill."
28
+ - prompt: "Write a .gdshader that does a dissolve effect with a noise texture and an edge glow."
29
+ why: "Shader authoring in .gdshader/visual shaders is delegated to gamedev-shaders, not GDScript gameplay code."
30
+ route_to: "gamedev-shaders"
31
+ - prompt: "Build a godot-cpp GDExtension purely as a CMake project and set up the compiler toolchain and SCons/CMake flags."
32
+ route_to: "cpp"
33
+ why: "The native C++ build/tooling side is cpp; this skill only points at GDExtension from the gameplay side."
34
+ - prompt: "Design the core game loop and progression economy for my roguelike before I build anything."
35
+ route_to: "game-design"
36
+ why: "Mechanics/loop/feel design precedes scripting and belongs to game-design."
37
+ - prompt: "Implement client-side prediction and server reconciliation for my Godot multiplayer shooter's netcode."
38
+ route_to: "gamedev-multiplayer"
39
+ why: "Deep netcode/RPC/synchronizer work is the multiplayer sibling; this skill stops at single-player node/signal basics."
40
+
41
+ capability:
42
+ - scenario: "Author a Godot 4.x 2D platformer player: a typed CharacterBody2D with gravity, run, and a floor-only jump, that emits a signal when it dies, plus the equivalent C# version."
43
+ must_include:
44
+ - "extends CharacterBody2D and sets the `velocity` property, then calls `move_and_slide()` with NO arguments (not move_and_slide(velocity, ...))"
45
+ - "Applies gravity via get_gravity() * delta (or a project-settings gravity) scaled by delta, inside _physics_process, and does not multiply the velocity passed to move_and_slide by delta again"
46
+ - "Uses gravity/jump only when appropriate with is_on_floor(), and reads input via Input.get_axis / Input.is_action_just_pressed"
47
+ - "Declares a signal (e.g. `signal died`) and emits it with `died.emit()`, never emit_signal string form as the primary API"
48
+ - "Is statically typed: typed params (delta: float), typed vars (:= / : Type), and a -> void / typed return where relevant"
49
+ - "Uses @export for tunables (speed, jump_velocity) and @onready + $ or %UniqueName for node refs — no onready/export without @, no yield, no .instance()"
50
+ - "C# version uses `public partial class ... : CharacterBody2D`, `_PhysicsProcess(double delta)` calling `base` where a base defines it, `[Export]`, `[Signal] ...EventHandler`, `EmitSignal(SignalName....)`, sets `Velocity` then `MoveAndSlide()`"
@@ -0,0 +1,102 @@
1
+ # Export, testing & GDScript↔C# parity (Godot 4.x)
2
+
3
+ ## GDScript ↔ C# full parity table
4
+
5
+ C# needs the **.NET (Mono) build** of Godot 4. Members are PascalCase; scripts are `partial` classes
6
+ extending the node type; annotations become attributes; signals become source-generated `event`s.
7
+
8
+ | Concept | GDScript | C# |
9
+ | --- | --- | --- |
10
+ | Class decl | `extends Node2D` (+ `class_name Foo`) | `public partial class Foo : Node2D` |
11
+ | Ready | `func _ready():` | `public override void _Ready()` |
12
+ | Physics | `func _physics_process(delta):` (`delta: float`) | `public override void _PhysicsProcess(double delta)` |
13
+ | Chain parent | `super()` / `super._ready()` | `base._Ready()` |
14
+ | Export | `@export var speed := 300.0` | `[Export] public float Speed { get; set; } = 300f;` |
15
+ | Export range | `@export_range(0,1) var v := 0.5` | `[Export(PropertyHint.Range, "0,1")] public float V { get; set; }` |
16
+ | Signal decl | `signal hit(dmg: int)` | `[Signal] public delegate void HitEventHandler(int dmg);` |
17
+ | Emit | `hit.emit(10)` | `EmitSignal(SignalName.Hit, 10)` |
18
+ | Connect | `node.hit.connect(_on_hit)` | `node.Hit += OnHit;` |
19
+ | Disconnect | `node.hit.disconnect(_on_hit)` | `node.Hit -= OnHit;` |
20
+ | Child (direct) | `$Sprite2D` | `GetNode<Sprite2D>("Sprite2D")` |
21
+ | Child (unique) | `%HealthBar` | `GetNode<ProgressBar>("%HealthBar")` |
22
+ | onready | `@onready var s := $Sprite2D` | assign in `_Ready()` |
23
+ | Preload | `const B := preload("res://b.tscn")` | `GD.Load<PackedScene>("res://b.tscn")` |
24
+ | Instance | `B.instantiate()` | `b.Instantiate<Node2D>()` |
25
+ | Free | `queue_free()` | `QueueFree()` |
26
+ | Print | `print(x)` | `GD.Print(x)` |
27
+ | Move | set `velocity` → `move_and_slide()` | set `Velocity` → `MoveAndSlide()` |
28
+ | Await signal | `await obj.sig` | `await ToSignal(obj, Node.SignalName.Sig)` |
29
+ | Await frame | `await get_tree().process_frame` | `await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame)` |
30
+ | Group | `add_to_group("enemies")` | `AddToGroup("enemies")` |
31
+ | Random | `randf_range(0,1)` | `GD.RandRange(0,1)` |
32
+
33
+ C# gotchas: use the generated `SignalName`/`PropertyName`/`MethodName` string caches, not raw
34
+ strings; C# `Vector2`/`Vector3` fields can't be mutated through a property getter (copy → mutate →
35
+ assign back, like `var v = Velocity; v.X = 1; Velocity = v;`); `[Export]` needs a public
36
+ property or field of a Godot-marshalable type.
37
+
38
+ ## GDExtension / C++ (native code)
39
+
40
+ For CPU-hot systems (procedural gen, heavy sims), write a **GDExtension** with `godot-cpp` instead of
41
+ recompiling the engine as a module:
42
+
43
+ - A `.gdextension` manifest points at the compiled shared lib per platform.
44
+ - Classes extend a Godot type and register with `GDREGISTER_CLASS(MyClass)`; expose methods/props via
45
+ `static void _bind_methods()` using `ClassDB::bind_method(...)` and `ADD_PROPERTY(...)`.
46
+ - Build with SCons or CMake against the `godot-cpp` bindings; it loads like any resource, no engine
47
+ rebuild. This is native C++ tooling → pair with the `cpp` skill for the build side.
48
+
49
+ ## Testing
50
+
51
+ Two mature frameworks for Godot 4:
52
+
53
+ - **GUT 9.x** (Godot Unit Test) — GDScript. Test scripts `extends GutTest`; methods named
54
+ `test_*`; assertions like `assert_eq`, `assert_true`, `assert_signal_emitted`. Install via the
55
+ Asset Library or a git submodule under `addons/gut`.
56
+ - **gdUnit4** — GDScript **and** C#, with scene-runner helpers for driving input and awaiting frames;
57
+ ships CI templates and a fluent `assert_that(...)` API.
58
+
59
+ ```gdscript
60
+ # test/test_health.gd
61
+ extends GutTest
62
+
63
+ func test_takes_damage() -> void:
64
+ var h := HealthComponent.new()
65
+ h.max_health = 100
66
+ watch_signals(h)
67
+ h.take_damage(30)
68
+ assert_eq(h.current, 70)
69
+
70
+ func test_emits_died_at_zero() -> void:
71
+ var h := HealthComponent.new()
72
+ watch_signals(h)
73
+ h.take_damage(999)
74
+ assert_signal_emitted(h, "died")
75
+ ```
76
+
77
+ Test **behavior through the public surface** (signals, exported state, method results), not private
78
+ `_underscore` fields. For node-tree tests, `add_child_autofree(node)` so GUT frees it.
79
+
80
+ ## Headless CI & export
81
+
82
+ Godot runs without a display via `--headless` — the backbone of CI:
83
+
84
+ ```bash
85
+ # Run the GUT suite headless and exit with the pass/fail code
86
+ godot --headless -s addons/gut/gut_cmdln.gd -gdir=res://test -ginclude_subdirs -gexit
87
+
88
+ # gdUnit4 headless
89
+ godot --headless -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd -a res://test
90
+
91
+ # Import/validate assets once (populates .godot/, catches broken resources) — good first CI step
92
+ godot --headless --import
93
+
94
+ # Export a release build for a preset defined in export_presets.cfg
95
+ godot --headless --export-release "Linux/X11" build/game.x86_64
96
+ godot --headless --export-release "Web" build/index.html
97
+ ```
98
+
99
+ - Export **templates** for the matching engine version must be installed on the CI runner.
100
+ - Run `--import` before `--export-*` on a clean checkout so `.godot/` is built.
101
+ - A green editor open + a passing headless suite + a successful export per target = shippable; deep
102
+ store/signing/platform work belongs to `gamedev-shipping`.