summer-engine 2.6.6 → 2.8.0

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 (168) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +4 -1
  3. package/.codex-plugin/plugin.json +6 -2
  4. package/.cursor-plugin/plugin.json +5 -1
  5. package/.opencode/INSTALL.md +1 -1
  6. package/.opencode/plugins/summer.js +2 -2
  7. package/AGENTS.md +11 -7
  8. package/CHANGELOG.md +40 -0
  9. package/GEMINI.md +9 -7
  10. package/README.md +63 -21
  11. package/_persona/summer/SKILL.md +1 -0
  12. package/assets/autopilot/README.md +51 -0
  13. package/assets/autopilot/autopilot.gd +222 -0
  14. package/assets/autopilot/probe_base.gd +262 -0
  15. package/assets/autopilot/run.sh +78 -0
  16. package/commands/gameskill.md +11 -2
  17. package/commands/summer.md +13 -0
  18. package/dist/bin/summer.js +12 -0
  19. package/dist/commands/config.d.ts +2 -0
  20. package/dist/commands/config.js +53 -0
  21. package/dist/commands/create.d.ts +1 -0
  22. package/dist/commands/create.js +38 -6
  23. package/dist/commands/debug.d.ts +2 -0
  24. package/dist/commands/debug.js +44 -0
  25. package/dist/commands/doctor.js +112 -8
  26. package/dist/commands/list.js +1 -1
  27. package/dist/commands/login.d.ts +19 -0
  28. package/dist/commands/login.js +123 -36
  29. package/dist/commands/logout.js +3 -16
  30. package/dist/commands/logs.d.ts +2 -0
  31. package/dist/commands/logs.js +19 -0
  32. package/dist/commands/mcp.js +7 -2
  33. package/dist/commands/open.js +1 -1
  34. package/dist/commands/publish.d.ts +2 -0
  35. package/dist/commands/publish.js +20 -0
  36. package/dist/commands/releases.d.ts +2 -0
  37. package/dist/commands/releases.js +20 -0
  38. package/dist/commands/run.js +1 -1
  39. package/dist/commands/skills.js +1 -1
  40. package/dist/lib/api-client.d.ts +15 -4
  41. package/dist/lib/api-client.js +138 -13
  42. package/dist/lib/async-op-lifecycle.d.ts +7 -2
  43. package/dist/lib/async-op-lifecycle.js +32 -9
  44. package/dist/lib/auth.d.ts +40 -12
  45. package/dist/lib/auth.js +173 -37
  46. package/dist/lib/cloud/engine-bridge.d.ts +17 -4
  47. package/dist/lib/cloud/engine-bridge.js +32 -6
  48. package/dist/lib/config.d.ts +20 -0
  49. package/dist/lib/config.js +142 -0
  50. package/dist/lib/creator.d.ts +93 -0
  51. package/dist/lib/creator.js +393 -0
  52. package/dist/lib/debug-report.d.ts +50 -0
  53. package/dist/lib/debug-report.js +308 -0
  54. package/dist/lib/engine.d.ts +35 -2
  55. package/dist/lib/engine.js +240 -6
  56. package/dist/lib/game-task-plan.js +4 -4
  57. package/dist/lib/mcp-boot-notice.d.ts +3 -0
  58. package/dist/lib/mcp-boot-notice.js +7 -0
  59. package/dist/lib/mcp-log.d.ts +3 -0
  60. package/dist/lib/mcp-log.js +72 -0
  61. package/dist/lib/public-surface-paths.d.ts +13 -0
  62. package/dist/lib/public-surface-paths.js +42 -0
  63. package/dist/lib/skills-registry.d.ts +33 -1
  64. package/dist/lib/skills-registry.js +55 -0
  65. package/dist/lib/store.d.ts +14 -0
  66. package/dist/lib/store.js +145 -0
  67. package/dist/lib/summer-compatibility.d.ts +6 -0
  68. package/dist/lib/summer-compatibility.js +22 -0
  69. package/dist/mcp/server.d.ts +10 -3
  70. package/dist/mcp/server.js +104 -11
  71. package/dist/mcp/tools/asset-tools.js +33 -12
  72. package/dist/mcp/tools/cloud-tools.js +15 -15
  73. package/dist/mcp/tools/creator-tools.d.ts +2 -0
  74. package/dist/mcp/tools/creator-tools.js +105 -0
  75. package/dist/mcp/tools/debug-tools.js +44 -1
  76. package/dist/mcp/tools/file-tools.d.ts +2 -0
  77. package/dist/mcp/tools/file-tools.js +154 -0
  78. package/dist/mcp/tools/generate-tools.js +159 -11
  79. package/dist/mcp/tools/project-tools.js +37 -27
  80. package/dist/mcp/tools/scene-tools.js +106 -29
  81. package/dist/mcp/tools/visual-tools.js +4 -3
  82. package/dist/mcp/tools/with-engine.js +4 -4
  83. package/hooks/session-start.sh +5 -5
  84. package/package.json +3 -1
  85. package/references/collaborative-protocol.md +50 -0
  86. package/references/gd-style.md +121 -0
  87. package/references/godot-version.md +51 -0
  88. package/references/mcp-tools-reference.md +199 -0
  89. package/references/public-surface-language.md +30 -0
  90. package/references/summer-folder.md +90 -0
  91. package/references/template-registry.md +73 -0
  92. package/skills/2d-assets/character-portrait/SKILL.md +17 -12
  93. package/skills/2d-assets/concept-art/SKILL.md +10 -9
  94. package/skills/2d-assets/create-asset-sheet/SKILL.md +17 -15
  95. package/skills/2d-assets/pixel-art/SKILL.md +36 -20
  96. package/skills/2d-assets/skybox-panorama/SKILL.md +32 -37
  97. package/skills/2d-assets/sprite-sheet/SKILL.md +40 -35
  98. package/skills/2d-assets/tileable-texture/SKILL.md +28 -22
  99. package/skills/2d-assets/ui-graphics/SKILL.md +46 -32
  100. package/skills/2d-assets/use-widget-asset/SKILL.md +8 -6
  101. package/skills/3d-assets/character-model/SKILL.md +34 -19
  102. package/skills/3d-assets/environment-kit/SKILL.md +11 -5
  103. package/skills/3d-assets/organic-model/SKILL.md +8 -5
  104. package/skills/3d-assets/prop-model/SKILL.md +9 -3
  105. package/skills/3d-assets/vehicle-model/SKILL.md +32 -19
  106. package/skills/README.md +1 -1
  107. package/skills/ai-and-npcs/design-npc/SKILL.md +31 -13
  108. package/skills/animation/animation-tree/SKILL.md +39 -15
  109. package/skills/animation/facial-and-lipsync/SKILL.md +25 -8
  110. package/skills/animation/generate-motion/SKILL.md +47 -15
  111. package/skills/animation/procedural-animation/SKILL.md +31 -13
  112. package/skills/animation/retarget/SKILL.md +127 -79
  113. package/skills/audio/adaptive-music/SKILL.md +24 -20
  114. package/skills/audio/ambient-bed/SKILL.md +22 -16
  115. package/skills/audio/audio-direction/SKILL.md +15 -10
  116. package/skills/audio/music-track/SKILL.md +18 -12
  117. package/skills/audio/sound-effect/SKILL.md +17 -12
  118. package/skills/audio/voice-line/SKILL.md +42 -42
  119. package/skills/character-controllers/fps-controller/SKILL.md +26 -16
  120. package/skills/debugging/debug/SKILL.md +74 -20
  121. package/skills/deployment/export-and-ship/SKILL.md +105 -38
  122. package/skills/deployment/remote-deploy/SKILL.md +140 -0
  123. package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +26 -1
  124. package/skills/gameplay-mechanics/design-mechanic/SKILL.md +23 -15
  125. package/skills/level-design/design-level/SKILL.md +22 -8
  126. package/skills/level-design/scene-to-level/SKILL.md +13 -7
  127. package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +8 -8
  128. package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +2 -7
  129. package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +26 -18
  130. package/skills/performance/tune-performance/SKILL.md +51 -32
  131. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +34 -5
  132. package/skills/rendering-and-lighting/art-direction/SKILL.md +6 -8
  133. package/skills/scene-and-project/brainstorm-game/SKILL.md +7 -9
  134. package/skills/scene-and-project/make-game/SKILL.md +2 -1
  135. package/skills/scene-and-project/play/SKILL.md +10 -4
  136. package/skills/scene-and-project/scene-composition/SKILL.md +21 -8
  137. package/skills/scene-and-project/summer-cloud/SKILL.md +92 -0
  138. package/skills/scripting-patterns/gdscript-patterns/SKILL.md +1 -1
  139. package/skills/ui-and-ux/ui-basics/SKILL.md +16 -7
  140. package/skills/video/animated-loop/SKILL.md +63 -39
  141. package/skills/video/cinematic-cutscene/SKILL.md +66 -30
  142. package/skills/video/trailer-shot/SKILL.md +30 -21
  143. package/skills/visual-effects/_building-blocks/additive-billboard-particles.md +25 -16
  144. package/skills/visual-effects/_building-blocks/trauma-shake-snippet.md +5 -3
  145. package/skills/visual-effects/game-feel/SKILL.md +51 -33
  146. package/skills/visual-effects/recipes/dissolve/SKILL.md +51 -9
  147. package/skills/visual-effects/recipes/fire/SKILL.md +136 -49
  148. package/skills/visual-effects/recipes/hit-spark/SKILL.md +45 -8
  149. package/skills/visual-effects/recipes/lightning/SKILL.md +59 -27
  150. package/skills/visual-effects/recipes/magic-glow/SKILL.md +59 -26
  151. package/skills/visual-effects/recipes/muzzle-flash/SKILL.md +68 -25
  152. package/skills/visual-effects/recipes/smoke/SKILL.md +89 -28
  153. package/skills/visual-effects/recipes/water-ripple/SKILL.md +83 -23
  154. package/skills/workflow/brainstorming/SKILL.md +1 -1
  155. package/skills/workflow/debugging-game-feel/SKILL.md +29 -9
  156. package/skills/workflow/diagnosing-perf-regressions/SKILL.md +46 -21
  157. package/skills/workflow/gameskill/SKILL.md +11 -2
  158. package/skills/workflow/headless-scripting/SKILL.md +407 -0
  159. package/skills/workflow/investigating-bugs/SKILL.md +4 -2
  160. package/skills/workflow/mcpupdate/SKILL.md +9 -9
  161. package/skills/workflow/playtesting-a-feature/SKILL.md +94 -21
  162. package/skills/workflow/skill-create/SKILL.md +7 -7
  163. package/skills/workflow/skill-test/SKILL.md +3 -3
  164. package/skills/workflow/using-summer/SKILL.md +32 -3
  165. package/skills/workflow/verification-before-completion/SKILL.md +2 -0
  166. package/skills/workflow/writing-plans/SKILL.md +7 -7
  167. package/skills/workflow/writing-skills/SKILL.md +7 -5
  168. package/skills/workflow/writing-skills/testing-skills-with-subagents.md +1 -1
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: video
7
7
  user-invocable: true
8
- allowed-tools: Read Grep Glob Write Edit summer_generate_video summer_generate_image summer_generate_audio summer_search_assets summer_import_from_url summer_add_node summer_set_prop
8
+ allowed-tools: Read Grep Glob Write Edit summer_generate_video summer_check_job summer_generate_image summer_generate_audio summer_search_assets summer_import_from_url summer_add_node summer_set_prop
9
9
  paths: ["assets/video/**", "cinematics/**", "trailers/**"]
10
10
  ---
11
11
 
@@ -38,10 +38,11 @@ If the user wants 5 seconds of marketing footage, that's `video/trailer-shot`. I
38
38
 
39
39
  ```
40
40
  Read .summer/GameSoul.md
41
- summer_search_assets(query="cutscene", filter={ kind: "video" })
42
- summer_search_assets(query="<character name> reference", filter={ kind: "image" })
41
+ summer_search_assets(query="<character name> reference", assetType="2d_image", source="all")
43
42
  ```
44
43
 
44
+ `summer_search_assets` has no video filter — `assetType` is one of `2d_image`, `animation`, `3d_model`, `audio`, `music`, `all`, and there is no video member. To find prior cutscene clips, list your own assets with `summer_list_my_assets(query="cutscene")` and read the titles.
45
+
45
46
  If a reference image of the character already exists from a prior `concept-art` or `character-portrait` run, **reuse it** as the `imageUrl` — that single decision is the difference between the character looking like themselves and looking like a stranger.
46
47
 
47
48
  ### 2. Plan the shot list
@@ -58,25 +59,28 @@ For any character or hero scene, generate a reference still with `summer_generat
58
59
 
59
60
  ```
60
61
  summer_generate_image(
61
- prompt="<subject>, <setting>, cinematic lighting, film still, <art style from GameSoul.md>",
62
- model="nano-banana-2",
63
- options={ image_size: "landscape_16_9" }
62
+ prompt="<subject>, <setting>, cinematic lighting, film still, wide 16:9 framing, <art style from GameSoul.md>",
63
+ model="nano-banana-2"
64
64
  )
65
65
  ```
66
66
 
67
+ `summer_generate_image` has no size or aspect argument — `options.image_size` is dropped without an error and every MCP image comes back at the server's 1:1 default. Ask for the framing in the prompt, or generate the reference in the Summer dashboard (which exposes aspect ratio) and pass its URL. For image-to-video this matters more than usual: the clip's framing is inherited from the reference, so a square reference gives you a square cutscene.
68
+
67
69
  Show the user the still and confirm it's the right look before video-ing it. Regenerating a $0.05 still beats regenerating a $0.50 video.
68
70
 
69
71
  ### 4. Pick the model
70
72
 
71
- | Model | Cost | Speed | When |
72
- |---|---|---|---|
73
- | `ltx` | ~$0.10 | ~30s | Iteration, blocking shots, B-roll, throwaway tests |
74
- | `kling` | ~$0.50 | 2-4 min | Hero shots, character cutscenes, anything the player will sit and watch |
75
- | `kling-turbo` | ~$0.30 | 1-2 min | Same as kling when iteration speed matters more than the last 10% of quality |
76
- | `veo3` | ~$1.00 | 3-5 min | Pitch decks, premium dialogue scenes with synced lip motion, short-form ad |
77
- | `minimax` | ~$0.40 | 2-3 min | Stylized / anime-leaning content; better at non-photoreal looks than kling |
73
+ **This skill always passes `imageUrl`, which puts every call on the image-to-video route — and that route only accepts three models.** `kling-turbo` and `veo3` are text-to-video only; asking for either with an `imageUrl` set is a 400 `invalid_model`, not a silent downgrade.
78
74
 
79
- Default policy: **`ltx` first** to validate the prompt and shot framing. If it lands the composition but quality is rough, escalate to `kling`. Only reach for `veo3` if dialogue lip-sync matters and the user has approved the cost.
75
+ | Model | i2v? | Cost | Speed | When |
76
+ |---|---|---|---|---|
77
+ | `ltx` | yes | $0.10 | ~30s | Iteration, blocking shots, B-roll, throwaway tests |
78
+ | `kling` | yes | $0.50 | 2-4 min | Hero shots, character cutscenes, anything the player will sit and watch |
79
+ | `minimax` | yes | $0.25 | 2-3 min | Stylized / anime-leaning content; better at non-photoreal looks than kling |
80
+ | `kling-turbo` | **no** | $0.30 | 1-2 min | Text-to-video only — usable only if you drop the reference image |
81
+ | `veo3` | **no** | $1.00 | 3-5 min | Text-to-video only — usable only if you drop the reference image |
82
+
83
+ Default policy: **`ltx` first** to validate the prompt and shot framing. If it lands the composition but quality is rough, escalate to `kling`. `veo3` is a real tradeoff, not an upgrade: reaching for it means giving up `imageUrl`, which is the one thing that holds character identity across shots. Take that trade only when on-camera lip-sync matters more than continuity, and only with the user's approval on cost.
80
84
 
81
85
  ### 5. Generate each shot
82
86
 
@@ -90,7 +94,19 @@ summer_generate_video(
90
94
  )
91
95
  ```
92
96
 
93
- Returns `{ asset: { fileUrl } }`. Show the user the URL and ask:
97
+ `duration` accepts **only 5 or 10** — the server coerces anything else to 5. There is no 3s, 4s, 7s or 8s clip.
98
+
99
+ Video generation is **asynchronous**. The call returns immediately with `{ success, queued: true, jobId, model, resolvedModel, estimatedCost, duration }` and **no `fileUrl`** — the clip is rendered by a background worker. Poll for it:
100
+
101
+ ```
102
+ summer_check_job(jobId="<jobId from the generate call>")
103
+ ```
104
+
105
+ Only once the job reports `completed` do you have an asset and a URL. Do not tell the user a shot is done off the back of the generate call — at that moment nothing has rendered yet.
106
+
107
+ `aspectRatio` is applied on the text-to-video path only; on image-to-video the framing comes from the reference image, so a `9:16` cutscene needs a `9:16` reference, not a `9:16` argument.
108
+
109
+ When the job completes, show the user the URL and ask:
94
110
 
95
111
  > Shot 1 of 3 done. Land or regenerate? If land, I'll move to shot 2.
96
112
 
@@ -110,20 +126,37 @@ Lip-sync caveat: if the video shows the character clearly mouthing words and the
110
126
 
111
127
  ### 7. Import and wire as a VideoStreamPlayer
112
128
 
129
+ **Transcode to Ogg Theora first.** The generated clip is an `.mp4`, and Summer Engine cannot load `.mp4` — the only video container in the build is `.ogv`. Measured against the shipped binary (4.6.1):
130
+
131
+ ```
132
+ $ Summer --headless --path <proj> -s res://probe.gd
133
+ VideoStream exts: ["ogv", "tres", "res"]
134
+ ERROR: No loader found for resource: res://clip.mp4 (expected type: unknown)
135
+ ```
136
+
137
+ Nothing transcodes for you — `summer_import_from_url` downloads the bytes, runs Godot's import pipeline, and *fails the import* if the file never becomes a loadable resource. Convert with ffmpeg or DaVinci before importing:
138
+
139
+ ```
140
+ ffmpeg -i intro_shot_01.mp4 -c:v libtheora -q:v 8 -an intro_shot_01.ogv
141
+ ```
142
+
143
+ Then:
144
+
113
145
  ```
114
- summer_import_from_url(url="<fileUrl>", path="assets/video/cinematics/intro_shot_01.mp4")
146
+ summer_import_from_url(url="<hosted .ogv url>", path="res://assets/video/cinematics/intro_shot_01.ogv")
115
147
  ```
116
148
 
117
- Build a controller scene at `cinematics/Intro.tscn`:
149
+ Build a controller scene at `res://cinematics/Intro.tscn`. Every scene-mutating tool takes an explicit `scenePath`, and `summer_set_prop`'s property argument is named `key`, not `property`:
118
150
 
119
151
  ```
120
- summer_add_node(parent=".", type="Control", name="Intro")
121
- summer_add_node(parent="./Intro", type="ColorRect", name="Fade") # black, alpha 1.0 → 0.0
122
- summer_add_node(parent="./Intro", type="VideoStreamPlayer", name="Video")
123
- summer_set_prop(path="./Intro/Video", property="stream", value="res://assets/video/cinematics/intro_shot_01.mp4")
124
- summer_set_prop(path="./Intro/Video", property="autoplay", value=true)
125
- summer_set_prop(path="./Intro/Video", property="expand", value=true)
126
- summer_set_prop(path="./Intro/Fade", property="anchors_preset", value=15)
152
+ summer_create_scene(path="res://cinematics/Intro.tscn", rootName="Intro", allow_temporary_scene_mutation=true)
153
+ summer_add_node(scenePath="res://cinematics/Intro.tscn", parent=".", type="ColorRect", name="Fade") # black, alpha 1.0 → 0.0
154
+ summer_add_node(scenePath="res://cinematics/Intro.tscn", parent=".", type="VideoStreamPlayer", name="Video")
155
+ summer_set_prop(scenePath="res://cinematics/Intro.tscn", path="./Video", key="stream", value="res://assets/video/cinematics/intro_shot_01.ogv")
156
+ summer_set_prop(scenePath="res://cinematics/Intro.tscn", path="./Video", key="autoplay", value=true)
157
+ summer_set_prop(scenePath="res://cinematics/Intro.tscn", path="./Video", key="expand", value=true)
158
+ summer_set_prop(scenePath="res://cinematics/Intro.tscn", path="./Fade", key="anchors_preset", value=15)
159
+ summer_save_scene(scenePath="res://cinematics/Intro.tscn")
127
160
  ```
128
161
 
129
162
  Attach a `Tween` script that fades the `ColorRect` from black to clear over 0.5s on `_ready`, then back to black when `Video.finished` fires, then `queue_free()`s the scene. For multi-shot cutscenes, queue the next `VideoStreamPlayer` in the `finished` signal handler.
@@ -139,11 +172,11 @@ Pattern: `<subject> + <action> + <camera move> + <lighting> + <stylistic anchor>
139
172
  | Opening establishing shot | `kling` | `wide shot of a fog-shrouded medieval village at dawn, slow dolly-in toward the church spire, warm low sun, cinematic, anamorphic lens flare` | $0.50 | 5s |
140
173
  | Character intro (hero turns to camera) | `kling` | `young witch with raven on shoulder turns slowly toward camera, candlelit interior, shallow depth of field, cinematic film still` | $0.50 | 5s |
141
174
  | Dialogue close-up (no synced lips) | `kling` | `close-up of a grizzled knight, eyes downcast then looking up, firelight on his face, dust motes, 16mm film grain` | $0.50 | 5s |
142
- | Dialogue close-up (synced lips) | `veo3` | `close-up of the witch saying "they will come for you at dawn", candlelight, cinematic, shallow DOF` | $1.00 | 5s |
175
+ | Dialogue close-up (synced lips) | `veo3` — text-to-video only, drop `imageUrl` | `close-up of the witch saying "they will come for you at dawn", candlelight, cinematic, shallow DOF` | $1.00 | 5s |
143
176
  | Action beat (village burns) | `kling` | `medium wide of a thatched village engulfed in flames at night, embers rising, silhouettes running through smoke, hand-held camera, cinematic` | $0.50 | 5s |
144
177
  | Ending shot (hero walks away) | `kling` | `low wide shot of a cloaked figure walking away across a blasted plain at sunset, slow truck-back, golden hour, dust on the wind` | $0.50 | 5s |
145
178
  | Throwaway iteration / blocking | `ltx` | same prompt as above | $0.10 | 5s |
146
- | Anime / stylized cutscene | `minimax` | `anime-style young swordsman draws blade and steps forward, sakura petals, dramatic wind, Studio Ghibli soft painterly` | $0.40 | 5s |
179
+ | Anime / stylized cutscene | `minimax` | `anime-style young swordsman draws blade and steps forward, sakura petals, dramatic wind, Studio Ghibli soft painterly` | $0.25 | 5s |
147
180
 
148
181
  ### Bad prompts and why
149
182
 
@@ -162,11 +195,14 @@ Pattern: `<subject> + <action> + <camera move> + <lighting> + <stylistic anchor>
162
195
  - **Asking the video model to render dialogue without `veo3`.** `kling` and `ltx` will animate mouths but the motion does not match any audio. Use `veo3` if dialogue is on-camera, or keep the camera off the speaker.
163
196
  - **Using `kling` for blocking iterations.** Burn $0.10 on `ltx` to validate framing and prompt; only spend $0.50 once the composition lands.
164
197
  - **Putting the cutscene on a `VideoStreamPlayer` without a fade.** Cuts straight from gameplay to video are jarring. Always wrap in a `ColorRect` fade in/out.
165
- - **Forgetting to import the file.** `summer_generate_video` returns a `fileUrl` on Cloudinary; until you call `summer_import_from_url`, it isn't in `res://` and the scene can't reference it.
198
+ - **Treating `summer_generate_video` as synchronous.** It returns `{ queued: true, jobId }` and no URL. You must poll `summer_check_job` before there is anything to show or import.
199
+ - **Importing the `.mp4` as-is.** The engine has no MP4 or WebM loader — only Ogg Theora (`.ogv`). Transcode first; an un-transcoded import fails and is rolled back.
200
+ - **Asking for `duration=3` or `duration=8`.** Only 5 and 10 exist; anything else is silently coerced to 5.
201
+ - **Pairing `imageUrl` with `kling-turbo` or `veo3`.** Neither has an image-to-video endpoint — that combination is a 400.
166
202
 
167
203
  ## Edge cases
168
204
 
169
- - **Vertical cutscene for a mobile target.** Set `aspectRatio="9:16"`. Reference image must also be 9:16 — generate it with `image_size: "portrait_16_9"` or the framing will crop wrong on the video.
205
+ - **Vertical cutscene for a mobile target.** On the image-to-video path `aspectRatio` is not applied at all — the clip inherits the reference image's framing. So the reference *must* be 9:16, and MCP image generation cannot produce that: make it in the Summer dashboard, or crop a square generation to 9:16, then pass its URL as `imageUrl`.
170
206
  - **Cutscene must match an in-engine character precisely.** No video model will hit pixel-perfect identity. Either (a) accept the artistic license, (b) render the cutscene in-engine with `AnimationPlayer` instead of generating it, or (c) generate a reference image *from* a screenshot of the in-engine character (image-to-image first, then image-to-video).
171
207
  - **Dialogue is too long for a 5s shot.** Either trim the line, or split: 5s of speaker, 5s of listener reaction (cheaper because the listener doesn't need synced lips).
172
208
  - **Cutscene needs to play and the player isn't looking.** Pause the gameplay timer, push a `Control` overlay with the `VideoStreamPlayer`, restore on finish.
@@ -179,7 +215,7 @@ If the Studio MCP server isn't running, the user can do all of this through the
179
215
  1. Generate a reference image in the Image tab.
180
216
  2. Open the Video tab, paste the reference URL into the image-to-video field.
181
217
  3. Set model, duration, aspect ratio, and prompt as listed above.
182
- 4. Download the `.mp4` and drop it into `assets/video/cinematics/` in the project, then re-import via the Godot editor.
218
+ 4. Download the `.mp4`, transcode it to Ogg Theora (`ffmpeg -i in.mp4 -c:v libtheora -q:v 8 -an out.ogv`), and drop the `.ogv` into `assets/video/cinematics/`. Summer Engine has no MP4 or WebM loader — `.ogv` is the only video container it can open.
183
219
 
184
220
  Print the exact prompt + model + duration + aspect ratio so the user can paste it into the dashboard verbatim.
185
221
 
@@ -201,4 +237,4 @@ Once the cutscene is generated and wired:
201
237
  - `audio/music-track` — score the cutscene.
202
238
  - `2d-assets/concept-art` — generate the reference image axis if no character reference exists yet.
203
239
  - `2d-assets/character-portrait` — produce a high-fidelity locked character portrait for use as `imageUrl`.
204
- - `references/mcp-tools-reference.md` — `summer_generate_video` parameter schema and error codes.
240
+ - `../../../references/mcp-tools-reference.md` — `summer_generate_video` parameter schema and error codes.
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: video
7
7
  user-invocable: true
8
- allowed-tools: Read Grep Glob Write Edit summer_generate_video summer_generate_image summer_search_assets summer_import_from_url summer_add_node summer_set_prop
8
+ allowed-tools: Read Grep Glob Write Edit summer_generate_video summer_check_job summer_generate_image summer_search_assets summer_import_from_url summer_add_node summer_set_prop
9
9
  paths: ["assets/video/**", "trailers/**", "marketing/**"]
10
10
  ---
11
11
 
@@ -56,23 +56,26 @@ For shots featuring a specific character, weapon, or environment, generate a ref
56
56
 
57
57
  ```
58
58
  summer_generate_image(
59
- prompt="<subject>, <archetype-driven framing>, cinematic, dramatic lighting, film still",
60
- model="nano-banana-2",
61
- options={ image_size: "landscape_16_9" }
59
+ prompt="<subject>, <archetype-driven framing>, cinematic, dramatic lighting, film still, wide 16:9 framing",
60
+ model="nano-banana-2"
62
61
  )
63
62
  ```
64
63
 
65
- For pure environment / abstract / VFX shots, you can skip this and prompt text-to-video directly.
64
+ `summer_generate_image` has no size or aspect argument — `options.image_size` is dropped without an error and every MCP image comes back at the server's 1:1 default. Ask for the framing in the prompt, or build the reference in the Summer dashboard (which exposes aspect ratio) and pass its URL. Since image-to-video inherits the reference's framing, a square reference gives you a square trailer shot.
65
+
66
+ For pure environment / abstract / VFX shots, you can skip this and prompt text-to-video directly — that also unlocks `aspectRatio`, `kling-turbo`, and `veo3`.
66
67
 
67
68
  ### 3. Pick the model
68
69
 
69
- | Model | Cost | Speed | When |
70
- |---|---|---|---|
71
- | `kling` | ~$0.50 | 2-4 min | Default for trailer hero shots — best motion + composition |
72
- | `kling-turbo` | ~$0.30 | 1-2 min | Same look as kling, faster, slight quality dip |
73
- | `veo3` | ~$1.00 | 3-5 min | Pitch deck for investors, the one shot that has to be perfect, premium social ad |
74
- | `ltx` | ~$0.10 | ~30s | B-roll, throwaway tests, blocking the composition before committing |
75
- | `minimax` | ~$0.40 | 2-3 min | Anime / stylized trailer looks |
70
+ The available models depend on whether you pass `imageUrl`. With a reference image (step 2) the call goes to the image-to-video route, which accepts only `ltx`, `kling`, and `minimax`; `kling-turbo` and `veo3` exist on the text-to-video route only, and pairing either with an `imageUrl` is a 400 `invalid_model`.
71
+
72
+ | Model | Text-to-video | Image-to-video | Cost | Speed | When |
73
+ |---|---|---|---|---|---|
74
+ | `kling` | yes | yes | $0.50 | 2-4 min | Default for trailer hero shots — best motion + composition |
75
+ | `ltx` | yes | yes | $0.10 | ~30s | B-roll, throwaway tests, blocking the composition before committing |
76
+ | `minimax` | yes | yes | $0.25 | 2-3 min | Anime / stylized trailer looks |
77
+ | `kling-turbo` | yes | **no** | $0.30 | 1-2 min | Same look as kling, faster, slight quality dip — but you lose `imageUrl` |
78
+ | `veo3` | yes | **no** | $1.00 | 3-5 min | Pitch deck for investors, the one shot that has to be perfect — but you lose `imageUrl` |
76
79
 
77
80
  Default: **`ltx` first to block the composition, then `kling` for the final**. For the *one* hero shot that drives the Steam page, escalate to `veo3` only if `kling` failed twice.
78
81
 
@@ -88,9 +91,11 @@ summer_generate_video(
88
91
  )
89
92
  ```
90
93
 
91
- For vertical social (TikTok, Reels, Shorts): `aspectRatio="9:16"` and frame the subject vertically in your reference image.
94
+ `duration` accepts **only 5 or 10** — anything else is coerced to 5.
92
95
 
93
- For square (Instagram feed): `aspectRatio="1:1"`.
96
+ The call is **asynchronous**: it returns `{ success, queued: true, jobId, model, resolvedModel, estimatedCost, duration }` and **no URL**. Poll `summer_check_job(jobId="<jobId>")` until it reports `completed` before showing the user anything or claiming the shot is done.
97
+
98
+ `aspectRatio` (`16:9`, `9:16`, `1:1`, ...) is applied on the **text-to-video path only**. The moment you pass `imageUrl` it is ignored and the clip inherits the reference image's framing — so vertical social needs a genuinely 9:16 reference, and square needs a square one. MCP image generation is square-only, so build non-square references in the Summer dashboard (which exposes aspect ratio) or crop them before use.
94
99
 
95
100
  ### 5. Review against trailer logic
96
101
 
@@ -107,10 +112,13 @@ If "regenerate", revise *one* axis (camera move, lighting, or framing) — don't
107
112
 
108
113
  ### 6. Import for archive (optional)
109
114
 
110
- Trailer footage usually lives on the user's local disk and goes straight into the NLE. But for shots that double as in-engine splash content, import:
115
+ Trailer footage usually lives on the user's local disk and goes straight into the NLE, where the generated `.mp4` is exactly what you want.
116
+
117
+ For shots that double as **in-engine** splash content, transcode first — Summer Engine has no MP4 or WebM loader, and `.ogv` (Ogg Theora) is the only video container it can open. `summer_import_from_url` runs Godot's import pipeline and rejects the file if it never becomes a loadable resource, so importing the `.mp4` fails outright rather than half-working.
111
118
 
112
119
  ```
113
- summer_import_from_url(url="<fileUrl>", path="assets/video/trailers/hero_boss_reveal.mp4")
120
+ ffmpeg -i hero_boss_reveal.mp4 -c:v libtheora -q:v 8 -an hero_boss_reveal.ogv
121
+ summer_import_from_url(url="<hosted .ogv url>", path="res://assets/video/trailers/hero_boss_reveal.ogv")
114
122
  ```
115
123
 
116
124
  For pure trailer use (DaVinci / Premiere), the user can grab the `fileUrl` and download directly.
@@ -142,7 +150,7 @@ Pattern: `<archetype framing> + <subject> + <action verb> + <camera move> + <lig
142
150
  | Vertical social hero | `kling` | `vertical close-up of a young witch raising hands, magical wind, hair flowing, candle-warm key, cinematic, framed for 9:16` | $0.50 | 5s |
143
151
  | Pitch-deck premium shot | `veo3` | `wide cinematic of a fleet of airships emerging through cloud cover at dawn, slow truck forward, golden god-rays, soaring orchestral cinematic` | $1.00 | 5s |
144
152
  | Cheap B-roll iteration | `ltx` | `mid shot of a knight walking through fog, slow truck backward, dawn light, cinematic` | $0.10 | 5s |
145
- | Anime style hero | `minimax` | `anime-style hero girl draws a katana in slow motion, sakura petals swirling, dramatic side-light, dynamic camera, Ghibli painterly` | $0.40 | 5s |
153
+ | Anime style hero | `minimax` | `anime-style hero girl draws a katana in slow motion, sakura petals swirling, dramatic side-light, dynamic camera, Ghibli painterly` | $0.25 | 5s |
146
154
 
147
155
  ### Bad prompts and why
148
156
 
@@ -160,7 +168,8 @@ Pattern: `<archetype framing> + <subject> + <action verb> + <camera move> + <lig
160
168
  - **Skipping the reference image for the hero shot.** The boss in the Steam capsule must look like the boss in the game. Always lock identity with `imageUrl`.
161
169
  - **Using `veo3` for B-roll.** Burn $0.10 on `ltx` for blocking and B-roll; reserve `veo3` for the one shot that has to be perfect.
162
170
  - **Generating shots without composing the trailer first.** Make a 6-shot list before generating shot 1. Otherwise you'll generate shots that don't intercut.
163
- - **Using `aspectRatio="16:9"` for vertical social.** TikTok / Reels / Shorts are `9:16`. Pillarboxed 16:9 reads as a desktop YouTube embed and gets demoted by the algorithm.
171
+ - **Assuming `aspectRatio="9:16"` reframed an image-to-video call.** It is ignored whenever `imageUrl` is set — the reference image decides the framing. Pillarboxed 16:9 on TikTok / Reels / Shorts reads as a desktop YouTube embed and gets demoted by the algorithm, so fix the reference, not the argument.
172
+ - **Treating `summer_generate_video` as synchronous.** It returns `{ queued: true, jobId }` and no URL. Poll `summer_check_job` before handing the user a link.
164
173
  - **Forgetting that Studio doesn't cut video.** The user has to take the shots into an NLE. Tell them, don't pretend Studio can stitch.
165
174
  - **Asking for "the camera does X then Y then Z".** Pick one camera move per shot.
166
175
 
@@ -168,7 +177,7 @@ Pattern: `<archetype framing> + <subject> + <action verb> + <camera move> + <lig
168
177
 
169
178
  - **Steam capsule animated header (Steam asset).** Steam wants `.webm` at 616x353 or 1920x1080, ≤6s, 30fps, no audio, ≤4MB. Generate at 16:9, transcode in DaVinci, export `.webm` with VP9. The video model gives you the source; format conversion is on you.
170
179
  - **Pitch deck for investors.** One shot, `veo3`, money composition (low-angle hero or wide establishing). Worth the $1.00.
171
- - **Vertical for TikTok / Shorts.** `aspectRatio="9:16"`, reference image at portrait, frame the subject in the upper-middle so titles can sit at the bottom.
180
+ - **Vertical for TikTok / Shorts.** The reference image must genuinely be portrait — `aspectRatio` is ignored once `imageUrl` is set. Frame the subject in the upper-middle so titles can sit at the bottom.
172
181
  - **Subject is a UGC environment the player built.** Take a screenshot of the in-engine view, run it through `summer_generate_image` as `referenceImageUrl` to "upgrade" the look, then use that as the video reference. The model can stylize a screenshot; it can't invent the user's level.
173
182
  - **Logo splash with motion graphics.** Generate a static logo card in `summer_generate_image` (or use the user's existing logo PNG), then prompt video as `static composition of <logo>, only background particles drifting, cinematic, room for title text` — keep the foreground motionless.
174
183
 
@@ -178,7 +187,7 @@ If the Studio MCP server isn't running, route the user to the Studio web dashboa
178
187
 
179
188
  1. Image tab → generate the reference still.
180
189
  2. Video tab → image-to-video with the reference URL, model `kling`, duration 5s, aspect ratio per platform.
181
- 3. Download the `.mp4` directly into the user's editing project (DaVinci / Premiere / CapCut).
190
+ 3. Download the `.mp4` directly into the user's editing project (DaVinci / Premiere / CapCut). No transcode needed for an NLE — only in-engine playback requires `.ogv`.
182
191
 
183
192
  Print the exact prompt + model + duration + aspect ratio so the user can paste it into the dashboard verbatim.
184
193
 
@@ -200,4 +209,4 @@ Once the shots are generated:
200
209
  - `audio/music-track` — trailer score.
201
210
  - `audio/sound-effect` — cut SFX (whooshes, impacts, stingers).
202
211
  - `2d-assets/concept-art` — generate the reference image axis when no asset exists yet.
203
- - `references/mcp-tools-reference.md` — `summer_generate_video` parameter schema and error codes.
212
+ - `../../../references/mcp-tools-reference.md` — `summer_generate_video` parameter schema and error codes.
@@ -1,28 +1,37 @@
1
1
  # Additive Billboard Particles — Canonical Material Settings
2
2
 
3
- Reusable `BaseMaterial3D` configuration for additive particle billboards (motes, sparks, glow, ash). Used by `magic-glow`, optionally substitutable in `hit-spark` and `fire` if a custom shader is overkill.
3
+ Reusable material configuration for additive particle billboards (motes, sparks, glow, ash). Used by `magic-glow`, optionally substitutable in `hit-spark` and `fire` if a custom shader is overkill.
4
+
5
+ `BaseMaterial3D` is abstract — `BaseMaterial3D.new()` is a parse error ("Native class
6
+ "BaseMaterial3D" cannot be constructed as it is abstract"). Instantiate
7
+ `StandardMaterial3D`; the `BaseMaterial3D.*` enum constants below are still the right
8
+ way to name the values.
4
9
 
5
10
  ## Settings
6
11
 
7
- ```
8
- transparency: BaseMaterial3D.TRANSPARENCY_ALPHA
9
- blend_mode: BaseMaterial3D.BLEND_MODE_ADD
10
- shading_mode: BaseMaterial3D.SHADING_MODE_UNSHADED
11
- billboard_mode: BaseMaterial3D.BILLBOARD_PARTICLES
12
- emission_enabled: true
13
- emission_energy_multiplier: 3.0 // 1.0–5.0 typical; bump for HDR bloom
14
- disable_receive_shadows: true
15
- shadow_to_opacity: false
16
- no_depth_test: false // true only if particles must read on TOP of opaque geometry
17
- albedo_color: Color(R, G, B, 1.0)
18
- emission: Color(R, G, B) // usually same as albedo for single-color motes
12
+ ```gdscript
13
+ var bm := StandardMaterial3D.new()
14
+ bm.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
15
+ bm.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
16
+ bm.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
17
+ bm.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
18
+ bm.emission_enabled = true
19
+ bm.emission_energy_multiplier = 3.0 # 1.0–5.0 typical; bump for HDR bloom
20
+ bm.disable_receive_shadows = true
21
+ bm.shadow_to_opacity = false
22
+ bm.no_depth_test = false # true only if particles must read on TOP of opaque geometry
23
+ bm.billboard_keep_scale = true # BILLBOARD_PARTICLES drops per-particle scale without this
24
+ bm.albedo_color = Color(R, G, B, 1.0)
25
+ bm.emission = Color(R, G, B) # usually same as albedo for single-color motes
19
26
  ```
20
27
 
21
28
  ## When to use this vs a custom shader
22
29
 
23
- - **Use this `BaseMaterial3D` config** when the effect is just a tinted soft-circle mote (magic glow, sparkles, dust). Less code, faster iteration, free Godot-managed billboard math.
24
- - **Use a custom `ShaderMaterial`** when you need per-particle color ramps over `CUSTOM.x` age (fire), shape masks (muzzle flash star burst), or stretched streaks (hit-spark). Those can't be expressed in `BaseMaterial3D`.
30
+ - **Use this `StandardMaterial3D` config** when the effect is just a tinted soft-circle mote (magic glow, sparkles, dust). Less code, faster iteration, free Godot-managed billboard math.
31
+ - **Per-particle color over age does NOT need a custom shader.** `ParticleProcessMaterial` has `color_ramp`, `color_initial_ramp`, `alpha_curve` and `emission_curve` (all verified present in 4.6) — those drive the particle's `COLOR` from age and feed straight into a plain `StandardMaterial3D`. Reach for a ramp before reaching for a shader.
32
+ - **Use a custom `ShaderMaterial`** when you need per-fragment shape masks (muzzle flash star burst), noise-distorted UVs (fire, smoke), or anything that varies *within* the quad. Those are what `StandardMaterial3D` genuinely can't express.
33
+ - If a custom shader does need the particle age, note that the age lives in `INSTANCE_CUSTOM.y` (normalized by `INSTANCE_CUSTOM.w`, the lifetime), that `INSTANCE_CUSTOM.x` is the rotation angle — not the age — and that `INSTANCE_CUSTOM` is **vertex-stage only**. Pass it to `fragment()` through a `varying`.
25
34
 
26
35
  ## Performance note
27
36
 
28
- Additive overdraw is fillrate-bound. Keep `draw_pass_1.size <= 0.2` for motes/sparks; larger quads at full additive intensity destroy mobile fillrate budgets fast.
37
+ Additive overdraw is fillrate-bound. `draw_pass_1` is a `Mesh`, so the size dial is on the mesh: keep `QuadMesh.size` at or under `Vector2(0.2, 0.2)` for motes/sparks. Larger quads at full additive intensity destroy mobile fillrate budgets fast.
@@ -22,8 +22,7 @@ func _process(delta: float) -> void:
22
22
  if camera == null:
23
23
  return
24
24
  if trauma > 0.0:
25
- var s = trauma * trauma // perceived intensity grows quadratically
26
- var t = Time.get_ticks_msec() * 0.01
25
+ var s := trauma * trauma # perceived intensity grows quadratically
27
26
  camera.h_offset = max_offset.x * s * (randf() * 2.0 - 1.0)
28
27
  camera.v_offset = max_offset.y * s * (randf() * 2.0 - 1.0)
29
28
  camera.rotation.x += max_pitch * s * (randf() * 2.0 - 1.0) * 0.1
@@ -37,7 +36,10 @@ func add_trauma(amount: float) -> void:
37
36
  trauma = clamp(trauma + amount, 0.0, 1.0)
38
37
  ```
39
38
 
40
- Register `CameraShake` as autoload in Project Settings.
39
+ Register `CameraShake` as autoload in Project Settings. An autoload is a node at
40
+ `/root/CameraShake`, not an engine singleton — reach it with
41
+ `get_node_or_null("/root/CameraShake")`. `Engine.has_singleton("CameraShake")` is
42
+ always `false` for an autoload (measured), so never gate the call on it.
41
43
 
42
44
  ## Usage
43
45
 
@@ -1,17 +1,22 @@
1
1
  ---
2
2
  name: game-feel
3
- description: Use when the user says the game "feels flat", "lacks impact", "needs juice", "needs punch", or asks for hit feedback, screen shake, camera shake, audio ducking, or general "game feel". Walks the user through the canonical Godot 4.5 game-feel stack — hit-flash + trauma camera shake + audio ducking — wired together so a single hit fires all three. Trigger on "vfx", "juice", "punch", "feels flat", "game feel", "hit flash", "screen shake", "camera shake", "ducking".
3
+ description: Use when a Summer game "feels flat", "lacks impact", "needs juice", or "needs punch", or the user asks for hit feedback, screen shake, camera shake, audio ducking, or general game feel. Walks through Summer Engine's hit-flash, trauma camera shake, and audio-ducking stack, wired so one hit fires all three. Trigger on "vfx", "juice", "punch", "feels flat", "game feel", "hit flash", "screen shake", "camera shake", "ducking".
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: visual-effects
7
7
  user-invocable: true
8
- allowed-tools: Read Grep Edit Write summer_get_scene_tree summer_inspect_node summer_inspect_resource summer_add_node summer_set_prop summer_set_resource_property summer_connect_signal summer_save_scene summer_get_script_errors summer_play summer_stop
8
+ allowed-tools: Read Grep Edit Write summer_write_file summer_read_file summer_get_scene_tree summer_inspect_node summer_inspect_resource summer_add_node summer_set_prop summer_set_resource_property summer_connect_signal summer_project_setting summer_save_scene summer_get_script_errors summer_get_debugger_errors summer_play summer_stop
9
9
  paths: ["**/*.gd", "**/*.tscn", "**/*.tres"]
10
10
  ---
11
11
 
12
12
  # /vfx — The Trio That Makes Games Feel Punchy
13
13
 
14
- Particles don't make a game feel good. Screen shake alone doesn't either. What makes a hit *land in the body* is three systems firing on the same frame: a sub-frame **hit flash** on the body that got hit, **trauma-based camera shake** with quadratic falloff so big hits saturate, and **audio ducking** that briefly squashes Music/Ambient so the impact SFX punches through. Skip any one and the game drops back to "tech demo". This skill installs the canonical Godot 4.5 stack — three small, self-contained systems wired to a single signal — so one `hit()` call fires all three.
14
+ Particles do not make a game feel good. Screen shake alone does not either.
15
+ What makes a hit land is three systems firing on the same frame: a sub-frame
16
+ **hit flash** on the body that got hit, **trauma-based camera shake** with
17
+ quadratic falloff so big hits saturate, and **audio ducking** that briefly
18
+ squashes Music/Ambient so the impact SFX punches through. This skill installs
19
+ the canonical Summer Engine stack as three small systems wired to one signal.
15
20
 
16
21
  ## Step 1 — Ask what feels flat
17
22
 
@@ -62,13 +67,15 @@ World/
62
67
  HitFlash (Node) # holds the script, no transform
63
68
  ```
64
69
 
65
- **MCP path:**
70
+ **MCP path** (every scene-mutating call takes an explicit `scenePath`):
66
71
 
67
72
  ```
68
- summer_add_node(parent="./World/Enemy", type="Node", name="HitFlash")
73
+ summer_add_node(scenePath="res://levels/world.tscn", parent="./World/Enemy", type="Node", name="HitFlash")
74
+ summer_set_prop(scenePath="res://levels/world.tscn", path="./World/Enemy/HitFlash", key="script", value="res://scripts/vfx/hit_flash.gd")
69
75
  ```
70
76
 
71
- Then write `scripts/vfx/hit_flash.gd`:
77
+ Then write `scripts/vfx/hit_flash.gd` (via `summer_write_file`, which requires exactly
78
+ one of `create_only` or `expected_sha256`):
72
79
 
73
80
  ```gdscript
74
81
  class_name HitFlash
@@ -110,7 +117,7 @@ func flash() -> void:
110
117
 
111
118
  **Tunable knobs:** `duration` 0.06–0.12s — under 0.06 reads as a flicker, over 0.12 looks slow. `emission_energy_multiplier` 1.5–3.0.
112
119
 
113
- > CRITICAL: do NOT inline a `StandardMaterial3D` as a `sub_resource` via `summer_set_prop`. Build it in script (as above) or save it to `materials/flash.tres`. Inline sub_resources break `summer_set_resource_property` silently — see `references/mcp-tools-reference.md` § "Trap".
120
+ > CRITICAL: do NOT inline a `StandardMaterial3D` as a `sub_resource` via `summer_set_prop`. Build it in script (as above) or save it to `materials/flash.tres`. Inline sub_resources break `summer_set_resource_property` silently — see `../../../references/mcp-tools-reference.md` § "Trap".
114
121
 
115
122
  ## Step 5 — Install Section 2 (Trauma Camera Shake)
116
123
 
@@ -121,8 +128,8 @@ func flash() -> void:
121
128
  **Node tree:** the script attaches to the existing `./World/Player/Camera3D`. No new nodes.
122
129
 
123
130
  ```
124
- summer_inspect_node "./World/Player/Camera3D"
125
- # attach scripts/vfx/camera_shake.gd
131
+ summer_inspect_node(path="./World/Player/Camera3D")
132
+ summer_set_prop(scenePath="res://levels/world.tscn", path="./World/Player/Camera3D", key="script", value="res://scripts/vfx/camera_shake.gd")
126
133
  ```
127
134
 
128
135
  `scripts/vfx/camera_shake.gd`:
@@ -138,6 +145,11 @@ extends Camera3D
138
145
  var _trauma: float = 0.0
139
146
  var _shake_offset: Vector3 = Vector3.ZERO
140
147
 
148
+ func _ready() -> void:
149
+ # Node has no `groups` property, so there is nothing to set from the outside.
150
+ # Joining the group here is how any actor finds this camera later.
151
+ add_to_group(&"player_camera")
152
+
141
153
  func add_trauma(amount: float) -> void:
142
154
  _trauma = clamp(_trauma + amount, 0.0, 1.0)
143
155
 
@@ -236,12 +248,9 @@ func duck(amount: float, duration: float = 0.5) -> void:
236
248
  var drop_db := -24.0 * amount * weight
237
249
  var target_db := base_db + drop_db
238
250
 
239
- var seq := create_tween()
240
- seq.tween_property(AudioServer, "bus_volume_db", target_db, attack).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)\
241
- .from(base_db)
242
- # NOTE: AudioServer doesn't expose bus volume as an animatable
243
- # property — use a method tween instead. See actual call below.
244
- # (The simpler, real implementation:)
251
+ # AudioServer exposes bus volume through a method, not an animatable
252
+ # property, so tween_method is the only form that works here.
253
+ # `tween_property(AudioServer, "bus_volume_db", ...)` errors at runtime.
245
254
  _tween.tween_method(_set_bus_db.bind(idx), base_db, target_db, attack)\
246
255
  .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
247
256
  _tween.tween_method(_set_bus_db.bind(idx), target_db, base_db, release)\
@@ -255,9 +264,12 @@ func _set_bus_db(idx: int, db: float) -> void:
255
264
  Register it as an autoload:
256
265
 
257
266
  ```
258
- Project Settings → Autoload → Add scripts/vfx/audio_ducker.gd as "AudioDucker"
267
+ summer_project_setting(...) # autoload/AudioDucker = "*res://scripts/vfx/audio_ducker.gd"
259
268
  ```
260
269
 
270
+ (Equivalently, by hand: Project Settings → Autoload → Add `scripts/vfx/audio_ducker.gd`
271
+ as `AudioDucker`.)
272
+
261
273
  **Calling it:**
262
274
 
263
275
  ```gdscript
@@ -290,25 +302,27 @@ func _on_damaged(amount: float) -> void:
290
302
  # 1. Hit flash on this body.
291
303
  _hit_flash.flash()
292
304
  # 2. Camera shake on the player's camera, scaled by hit weight.
293
- var trauma := clamp(0.2 + amount * 0.05, 0.2, 0.9)
305
+ # clampf(), not clamp(): clamp() returns Variant, and inferring a variable from
306
+ # a Variant is an error under Godot's default warning settings.
307
+ var trauma := clampf(0.2 + amount * 0.05, 0.2, 0.9)
294
308
  if _player_camera:
295
309
  _player_camera.add_trauma(trauma)
296
310
  # 3. Audio duck, scaled by hit weight.
297
- var duck_amount := clamp(0.3 + amount * 0.04, 0.3, 1.0)
311
+ var duck_amount := clampf(0.3 + amount * 0.04, 0.3, 1.0)
298
312
  AudioDucker.duck(duck_amount, 0.45)
299
313
  ```
300
314
 
301
- Add the camera to a group so any actor can find it:
302
-
303
- ```
304
- summer_add_node(...) # ./World/Player/Camera3D already exists
305
- summer_set_prop(path="./World/Player/Camera3D", key="groups", value='["player_camera"]')
306
- ```
315
+ The camera joins `player_camera` from its own `_ready()` (Step 5). `Node` has no
316
+ `groups` property, so `summer_set_prop(key="groups", ...)` cannot do this — and
317
+ `summer_set_prop` only accepts a string, number, or boolean anyway, never an array.
318
+ The two ways that do work are the `add_to_group()` call in `_ready()` or a `groups =
319
+ ["player_camera"]` line written into the `.tscn` node header.
307
320
 
308
- Or wire the signal explicitly via MCP if you prefer:
321
+ Or wire the signal explicitly via MCP if you prefer (`emitter`/`receiver`, not
322
+ `from`/`to`, and `scenePath` is required):
309
323
 
310
324
  ```
311
- summer_connect_signal(from="./World/Enemy", signal="damaged", to="./World/Enemy/HitFlash", method="flash")
325
+ summer_connect_signal(scenePath="res://levels/world.tscn", emitter="./World/Enemy", signal="damaged", receiver="./World/Enemy/HitFlash", method="flash")
312
326
  ```
313
327
 
314
328
  ## Step 8 — Scaling intensity per hit weight
@@ -328,10 +342,11 @@ Because trauma is squared inside the camera, the *felt* difference between 0.35
328
342
  ## Step 9 — Verify
329
343
 
330
344
  ```
331
- summer_save_scene
332
- summer_get_script_errors
345
+ summer_save_scene(scenePath="res://levels/world.tscn")
346
+ summer_get_script_errors # all three scripts parsed?
333
347
  summer_play
334
348
  # user reproduces a hit
349
+ summer_get_debugger_errors # runtime errors from the tweens / bus lookups
335
350
  summer_stop
336
351
  ```
337
352
 
@@ -355,10 +370,12 @@ Tune one knob at a time:
355
370
  | Equal pitch/yaw shake | `yaw_factor ≤ 0.5` | Eyes are sensitive to vertical motion; equal axes feel mechanical |
356
371
  | `Engine.time_scale` for every hit | Reserve hit-stop for big events only (boss hits, deaths) | Time-scale dips on every hit feel laggy, not impactful |
357
372
  | Inline `StandardMaterial3D` sub_resource via `summer_set_prop` | Build in script or save standalone `.tres` | Inline sub_resources silently break `summer_set_resource_property` |
373
+ | `summer_set_prop(key="groups", value='["player_camera"]')` | `add_to_group(&"player_camera")` in `_ready()`, or a `groups = [...]` line in the `.tscn` | `Node` has no `groups` property, and `summer_set_prop` takes only string/number/boolean — never an array |
374
+ | `BaseMaterial3D.new()` | `StandardMaterial3D.new()` | `BaseMaterial3D` is abstract; constructing it is a parse error that kills the whole script |
358
375
 
359
376
  ## Collaborative protocol
360
377
 
361
- This skill writes 3 GDScript files and adds 1 node + 1 autoload entry. Always ask before each section is applied. Group related writes into one ask: "I'm about to add HitFlash + CameraShake + AudioDucker autoload, wire one signal. OK?" See `references/collaborative-protocol.md`.
378
+ This skill writes 3 GDScript files and adds 1 node + 1 autoload entry. Always ask before each section is applied. Group related writes into one ask: "I'm about to add HitFlash + CameraShake + AudioDucker autoload, wire one signal. OK?" See `../../../references/collaborative-protocol.md`.
362
379
 
363
380
  ## When NOT to use this skill
364
381
 
@@ -369,10 +386,11 @@ This skill writes 3 GDScript files and adds 1 node + 1 autoload entry. Always as
369
386
 
370
387
  ## See also
371
388
 
372
- - `references/mcp-tools-reference.md` — full MCP tool list, especially the inline-sub_resource trap
373
- - `references/godot-version.md` — Godot 4.5 API notes
374
- - `references/collaborative-protocol.md` — "May I write" pattern
375
- - `references/gd-style.md` — typed GDScript conventions
389
+ - `../../../references/mcp-tools-reference.md` — full MCP tool list, especially the inline-sub_resource trap
390
+ - `../../../references/godot-version.md` — Summer compatibility and
391
+ version-sensitive API notes
392
+ - `../../../references/collaborative-protocol.md` — "May I write" pattern
393
+ - `../../../references/gd-style.md` — typed GDScript conventions
376
394
  - `audio/audio-direction/SKILL.md` — bus layout setup (prerequisite for ducking)
377
395
  - `post-processing/screen-shake/SKILL.md` — deeper trauma variants (Perlin noise, stacked sources)
378
396
  - `visual-effects/gpuparticles-3d-basics/SKILL.md` — particles to layer on top of the trio