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: visual-effects
7
7
  user-invocable: true
8
- allowed-tools: Read Write Edit summer_add_node summer_set_prop summer_set_resource_property summer_inspect_node summer_save_scene
8
+ allowed-tools: Read Write Edit summer_write_file summer_read_file summer_create_scene summer_add_node summer_set_prop summer_set_resource_property summer_inspect_node summer_save_scene summer_project_setting summer_get_script_errors summer_get_debugger_errors summer_play summer_stop
9
9
  paths: ["**/*.tscn", "**/*.gd", "**/*.gdshader", "addons/vfx/**"]
10
10
  ---
11
11
 
@@ -27,7 +27,9 @@ A lightning bolt is a noisy line from A to B, drawn for ~150 ms with a glow shad
27
27
  - The user wants a continuous beam that holds (e.g., laser sniper holding the trigger) — use the same shader on a `MeshInstance3D` cylinder with continuous emission, not the `ImmediateMesh` one-shot pattern. (See variants for `held-beam`.)
28
28
  - The user wants ambient electricity *on* an object (Frankenstein arcs) — use a custom shader on the mesh with screen-space arcs; this recipe is for two-point bolts.
29
29
  - The user wants a 2D lightning effect on UI — `canvas_item` shader, not this.
30
- - The user wants a tracer line for a bullet — use a simple `Line3D`/`ImmediateMesh` without the noise displacement; skip this recipe.
30
+ - The user wants a tracer line for a bullet — use a simple `ImmediateMesh`
31
+ without noise displacement; skip this recipe. The current Summer build has no
32
+ `Line3D` (`ClassDB.class_exists("Line3D")` is `false`); `Line2D` is 2D-only.
31
33
 
32
34
  ## Recipe
33
35
 
@@ -56,8 +58,7 @@ uniform float flicker_rate : hint_range(0.0, 60.0) = 30.0;
56
58
 
57
59
  void vertex() {
58
60
  // Billboard each segment toward the camera around its own forward axis.
59
- vec3 right = INV_VIEW_MATRIX[0].xyz;
60
- vec3 up = INV_VIEW_MATRIX[1].xyz;
61
+ vec3 up = INV_VIEW_MATRIX[1].xyz;
61
62
  VERTEX += UV.y > 0.5 ? up * thickness * 0.5 : -up * thickness * 0.5;
62
63
  }
63
64
 
@@ -76,8 +77,10 @@ void fragment() {
76
77
  vec3 col = mix(bolt_color.rgb, core_color.rgb, core);
77
78
  float a = (core + halo * 0.4) * intensity * flick;
78
79
 
79
- ALBEDO = col;
80
- EMISSION = col * emission_boost * intensity * flick * (core + halo * 0.5);
80
+ // `render_mode unshaded` outputs ALBEDO and discards EMISSION entirely
81
+ // (scene_forward_clustered.glsl:2962 — `frag_color = vec4(albedo, alpha)`
82
+ // under MODE_UNSHADED), so the bloom boost has to live in ALBEDO.
83
+ ALBEDO = col * emission_boost * flick * (core + halo * 0.5);
81
84
  ALPHA = clamp(a, 0.0, 1.0);
82
85
  }
83
86
  ```
@@ -117,14 +120,14 @@ func cast_lightning(from: Vector3, to: Vector3, intensity_scale: float = 1.0) ->
117
120
  _spawn_sparks(from)
118
121
  _spawn_sparks(to)
119
122
 
120
- if Engine.has_singleton("CameraShake") or _has_autoload("CameraShake"):
121
- var cs := Engine.get_main_loop().root.get_node_or_null("/root/CameraShake")
122
- if cs and cs.has_method("add_trauma"):
123
- cs.add_trauma(trauma_amount * intensity_scale)
124
-
125
- func _has_autoload(name: String) -> bool:
126
- var root := Engine.get_main_loop().root
127
- return root and root.has_node("/root/" + name)
123
+ # An autoload is a node at /root/<Name>, not an engine singleton:
124
+ # Engine.has_singleton("CameraShake") is always false for one (measured).
125
+ # get_node_or_null returns null when nothing is registered — that is the guard.
126
+ # (MainLoop has no `root`, so `var root := Engine.get_main_loop().root` is a
127
+ # parse error that takes the entire script down with it.)
128
+ var cs := get_node_or_null(^"/root/CameraShake")
129
+ if cs and cs.has_method("add_trauma"):
130
+ cs.add_trauma(trauma_amount * intensity_scale)
128
131
 
129
132
  func _build_bolt_node(from: Vector3, to: Vector3, intensity_scale: float) -> Node3D:
130
133
  var holder := Node3D.new()
@@ -193,7 +196,10 @@ func _draw_polyline(mesh: ImmediateMesh, points: PackedVector3Array) -> void:
193
196
  func _spawn_sparks(at: Vector3) -> void:
194
197
  if not ResourceLoader.exists(spark_scene_path):
195
198
  return
196
- var sparks := load(spark_scene_path).instantiate()
199
+ # `var sparks := load(...).instantiate()` is a parse error — load() has no
200
+ # static return type, so `:=` cannot infer. Annotate both steps.
201
+ var packed: PackedScene = load(spark_scene_path)
202
+ var sparks: Node3D = packed.instantiate()
197
203
  add_child(sparks)
198
204
  sparks.global_position = at
199
205
  if sparks.has_method("restart"):
@@ -216,12 +222,28 @@ Recommended: register one `LightningCaster` as an autoload (`/root/Lightning`) s
216
222
 
217
223
  Place one `LightningCaster` per "world" (or autoload it), then call from gameplay code.
218
224
 
225
+ Every scene-mutating call takes an explicit `scenePath`. `summer_set_prop` uses `key`
226
+ (not `property`) and only accepts a string, number, or boolean.
227
+
219
228
  ```
220
- summer_add_node(parent="./World", type="Node3D", name="LightningCaster")
221
- summer_set_prop(path="./World/LightningCaster", property="script", value="res://addons/vfx/lightning/lightning_caster.gd")
222
- summer_set_prop(path="./World/LightningCaster", property="bolt_color", value="Color(0.55, 0.75, 1.0)")
223
- summer_set_prop(path="./World/LightningCaster", property="trauma_amount", value=0.6)
224
- summer_save_scene
229
+ summer_write_file(path="res://addons/vfx/lightning/lightning.gdshader", content="<section 2>", create_only=true)
230
+ summer_write_file(path="res://addons/vfx/lightning/lightning_caster.gd", content="<section 3>", create_only=true)
231
+
232
+ summer_add_node(scenePath="res://main.tscn", parent="./World", type="Node3D", name="LightningCaster")
233
+ summer_set_prop(scenePath="res://main.tscn", path="./World/LightningCaster", key="script", value="res://addons/vfx/lightning/lightning_caster.gd")
234
+ summer_set_prop(scenePath="res://main.tscn", path="./World/LightningCaster", key="bolt_color", value="Color(0.55, 0.75, 1.0)")
235
+ summer_set_prop(scenePath="res://main.tscn", path="./World/LightningCaster", key="trauma_amount", value=0.6)
236
+ summer_save_scene(scenePath="res://main.tscn")
237
+ ```
238
+
239
+ Then verify — the caster `preload`s its own shader, so a shader that failed to write
240
+ takes the whole script down at parse time:
241
+
242
+ ```
243
+ summer_get_script_errors # lightning_caster.gd parsed?
244
+ summer_play
245
+ summer_get_debugger_errors # shader compile errors surface here at runtime
246
+ summer_stop
225
247
  ```
226
248
 
227
249
  Then from the spell code:
@@ -319,18 +341,21 @@ trauma_amount = 0.0 # no shake on a held beam
319
341
 
320
342
  ## Anti-patterns
321
343
 
322
- - **Drawing the bolt as a `Line3D`.** Line3D doesn't billboard or accept the additive shader cleanly. Use `ImmediateMesh` triangle strip with the billboard vertex shader.
344
+ - **Reaching for a `Line3D`.** The current Summer build has no such class
345
+ (`ClassDB.class_exists("Line3D")` is `false`); `Line2D` is 2D-only. Use an
346
+ `ImmediateMesh` triangle strip with the billboard vertex shader.
323
347
  - **Animating the path frame-by-frame.** A bolt is one shape, on screen for 150 ms. You don't see the inside detail moving. One generation per cast.
324
348
  - **Forgetting `blend_add`.** A bolt that doesn't bloom looks like a curved metal stick.
325
- - **No camera shake.** Lightning without screen shake feels weightless. Always call `CameraShake.add_trauma`.
349
+ - **No camera shake.** Lightning without screen shake feels weightless. Always call `add_trauma` on the `/root/CameraShake` autoload.
350
+ - **Gating the shake on `Engine.has_singleton("CameraShake")`.** That is always `false` for an autoload (measured) — the shake would never fire. Autoloads are nodes at `/root/<Name>`; look them up with `get_node_or_null`.
326
351
  - **Spawning lightning every frame for a "continuous" beam.** Use the `plasma-laser` variant pattern instead — one mesh, updated per-frame transform, never re-instantiated.
327
352
  - **`SEGMENTS` too low (< 6).** Bolt looks like a zigzag triangle. Default 18 is right.
328
353
  - **`DISPLACEMENT` proportional to total length without falloff.** Endpoints drift away from `from`/`to`. The included code uses `sin(t * PI)` falloff so endpoints meet cleanly.
329
354
 
330
355
  ## Performance notes
331
356
 
332
- - One bolt: ~36 verts, one draw call, freed after 150 ms. Effectively free.
333
- - Chain lightning with 5 segments: still under 0.05 ms.
357
+ - One bolt at `SEGMENTS = 18`: 19 path points × 2 verts = 38 verts for the trunk, plus 2 × (`SEGMENTS / 3` + 1) = 14 verts per forked branch. One draw call, freed after 150 ms. Effectively free.
358
+ - Chain lightning with 5 segments stays in the same negligible band — that is an order-of-magnitude expectation, not a measurement.
334
359
  - Endpoint sparks (instantiated `hit-spark` scenes) are the bigger cost — 64 particles × 2 = 128 particles per cast. Throttle for storms (see edge cases).
335
360
  - The shader's flicker uses `floor(TIME * flicker_rate)`, so it ages with `TIME` not particle time — flicker is consistent across all bolts in flight.
336
361
 
@@ -341,16 +366,23 @@ trauma_amount = 0.0 # no shake on a held beam
341
366
  - **Bolt origin and target at the same point.** Falloff goes to zero; `_generate_path` returns a degenerate strip. Add a guard: if `(to - from).length() < 0.01`, skip.
342
367
  - **Underwater bolt.** Tint blue-green, drop `emission_boost` to 5.0; underwater bloom is muted.
343
368
  - **First-person caster (player's hand).** The bolt starts inside the player's view and reads as a flash. Use a `held-beam` variant or shorten `LIFETIME` to 0.08.
344
- - **No `CameraShake` autoload registered.** The script's `_has_autoload` guard prevents a crash; the bolt fires without shake. Suggest `_building-blocks/trauma-shake-snippet.md` to wire it.
369
+ - **No `CameraShake` autoload registered.** `get_node_or_null("/root/CameraShake")` returns null and the `if cs` check skips the call; the bolt fires without shake. Suggest `_building-blocks/trauma-shake-snippet.md` to wire it.
345
370
 
346
371
  ## Fallback (no MCP)
347
372
 
348
- VFX is code, no MCP required:
373
+ Section 5 is fully automatable — `summer_write_file` writes the shader and the
374
+ caster, `summer_add_node` + `summer_set_prop` + `summer_save_scene` place it, and
375
+ `summer_project_setting` registers the autoload. Do not hand these steps to the user
376
+ when the MCP tools are available.
377
+
378
+ Without the MCP connection there is no engine to drive, so the user does it
379
+ manually in Summer Engine:
349
380
 
350
381
  1. Create `addons/vfx/lightning/` and write the three files above.
351
- 2. In Godot, add a `Node3D`, attach `lightning_caster.gd`. Optionally autoload as `Lightning`.
382
+ 2. Add a `Node3D`, attach `lightning_caster.gd`. Optionally autoload as `Lightning`.
352
383
  3. From any spell script, call `Lightning.cast_lightning(from_pos, to_pos)`.
353
384
  4. Wire the `CameraShake` autoload from `_building-blocks/trauma-shake-snippet.md` for the punch.
385
+ 5. Check the Errors dock — `lightning_caster.gd` `preload`s the shader, so a broken shader path fails the script too.
354
386
 
355
387
  ## Handoff
356
388
 
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: visual-effects
7
7
  user-invocable: true
8
- allowed-tools: Read Write Edit summer_add_node summer_set_prop summer_set_resource_property summer_inspect_node summer_save_scene
8
+ allowed-tools: Read Write Edit summer_write_file summer_read_file summer_create_scene summer_add_node summer_set_prop summer_set_resource_property summer_inspect_node summer_save_scene summer_get_script_errors summer_get_debugger_errors summer_play summer_stop
9
9
  paths: ["**/*.tscn", "**/*.gd", "**/*.gdshader", "addons/vfx/**"]
10
10
  ---
11
11
 
@@ -85,6 +85,7 @@ var _motes: GPUParticles3D
85
85
  var _source_mesh: MeshInstance3D
86
86
  var _source_mat: BaseMaterial3D
87
87
  var _t: float = 0.0
88
+ var _intensity: float = 1.0
88
89
 
89
90
  func _ready() -> void:
90
91
  _build_light()
@@ -127,7 +128,9 @@ func _build_motes() -> void:
127
128
 
128
129
  var mesh := QuadMesh.new()
129
130
  mesh.size = Vector2.ONE
130
- var bm := BaseMaterial3D.new()
131
+ # BaseMaterial3D is abstract ("Native class "BaseMaterial3D" cannot be
132
+ # constructed as it is abstract"); StandardMaterial3D is the concrete subclass.
133
+ var bm := StandardMaterial3D.new()
131
134
  bm.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
132
135
  bm.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
133
136
  bm.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
@@ -160,14 +163,17 @@ func _process(delta: float) -> void:
160
163
  phase = pulse_easing.sample(phase)
161
164
 
162
165
  if _light:
163
- _light.light_energy = light_energy_base + light_energy_amplitude * phase
166
+ _light.light_energy = (light_energy_base + light_energy_amplitude * phase) * _intensity
164
167
  if _source_mat:
165
- _source_mat.emission_energy_multiplier = source_emission_base + source_emission_amplitude * phase
168
+ _source_mat.emission_energy_multiplier = (source_emission_base + source_emission_amplitude * phase) * _intensity
166
169
 
167
170
  func set_intensity(scale: float) -> void:
168
171
  ## Scale all visible cues 0..1+ (e.g., charge-up: tween from 0 to 1 over a second).
169
- light_energy_base *= scale
170
- light_energy_amplitude *= scale
172
+ ## Held as a separate factor rather than multiplied into light_energy_base /
173
+ ## light_energy_amplitude: a multiplying version (`light_energy_base *= scale`)
174
+ ## pins both fields to 0.0 forever after set_intensity(0.0), so the charge-up
175
+ ## tween from 0 to 1 never brings the glow back.
176
+ _intensity = scale
171
177
  if _motes:
172
178
  _motes.amount_ratio = scale
173
179
 
@@ -191,26 +197,40 @@ Node3D ("MagicGlow") [script: magic_glow.gd]
191
197
 
192
198
  For an enchanted sword:
193
199
 
200
+ Every scene-mutating call takes an explicit `scenePath`. `summer_set_prop` uses `key`
201
+ (not `property`) and only accepts a string, number, or boolean.
202
+
194
203
  ```
195
- summer_add_node(parent="./Player/Hand/Sword", type="Node3D", name="Glow")
196
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="script", value="res://addons/vfx/magic-glow/magic_glow.gd")
197
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="glow_color", value="Color(0.55, 0.85, 1.0)")
198
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="light_energy_base", value=1.2)
199
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="light_energy_amplitude", value=0.6)
200
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="mote_count", value=16)
201
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="mote_radius", value=0.20)
202
- summer_set_prop(path="./Player/Hand/Sword/Glow", property="pulse_source_mesh", value="../Blade")
203
- summer_save_scene
204
+ summer_write_file(path="res://addons/vfx/magic-glow/magic_glow.gd", content="<section 2>", create_only=true)
205
+
206
+ summer_add_node(scenePath="res://main.tscn", parent="./Player/Hand/Sword", type="Node3D", name="Glow")
207
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="script", value="res://addons/vfx/magic-glow/magic_glow.gd")
208
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="glow_color", value="Color(0.55, 0.85, 1.0)")
209
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="light_energy_base", value=1.2)
210
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="light_energy_amplitude", value=0.6)
211
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="mote_count", value=16)
212
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="mote_radius", value=0.20)
213
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Hand/Sword/Glow", key="pulse_source_mesh", value="../Blade")
214
+ summer_save_scene(scenePath="res://main.tscn")
204
215
  ```
205
216
 
206
217
  For a soul gem on a pedestal:
207
218
 
208
219
  ```
209
- summer_add_node(parent="./World/Pedestal/SoulGem", type="Node3D", name="Glow")
210
- summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="glow_color", value="Color(0.85, 0.45, 1.0)")
211
- summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="pulse_hz", value=0.4)
212
- summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="mote_count", value=32)
213
- summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="mote_radius", value=0.30)
220
+ summer_add_node(scenePath="res://main.tscn", parent="./World/Pedestal/SoulGem", type="Node3D", name="Glow")
221
+ summer_set_prop(scenePath="res://main.tscn", path="./World/Pedestal/SoulGem/Glow", key="glow_color", value="Color(0.85, 0.45, 1.0)")
222
+ summer_set_prop(scenePath="res://main.tscn", path="./World/Pedestal/SoulGem/Glow", key="pulse_hz", value=0.4)
223
+ summer_set_prop(scenePath="res://main.tscn", path="./World/Pedestal/SoulGem/Glow", key="mote_count", value=32)
224
+ summer_set_prop(scenePath="res://main.tscn", path="./World/Pedestal/SoulGem/Glow", key="mote_radius", value=0.30)
225
+ ```
226
+
227
+ Then verify — this recipe is one script, so a parse error is the whole effect:
228
+
229
+ ```
230
+ summer_get_script_errors # magic_glow.gd parsed?
231
+ summer_play
232
+ summer_get_debugger_errors
233
+ summer_stop
214
234
  ```
215
235
 
216
236
  For a charge-up before casting:
@@ -313,11 +333,12 @@ mote_drift_up = 0.0
313
333
 
314
334
  ### charge-up (paired with lightning/muzzle-flash)
315
335
 
316
- Starts at 0 intensity, ramped via `set_intensity`. Set initial values for fully-charged state.
336
+ Author the **fully-charged** values here; `set_intensity(0.0 → 1.0)` scales them at
337
+ runtime. Leaving `light_energy_base` at 0.0 means the fully-charged glow is also 0.0.
317
338
 
318
339
  ```
319
340
  glow_color = Color(0.55, 0.75, 1.0)
320
- light_energy_base = 0.0 # script tweens this up
341
+ light_energy_base = 1.5 # fully-charged value; set_intensity scales it
321
342
  light_energy_amplitude = 2.0
322
343
  pulse_hz = 2.5 # buzzing
323
344
  mote_count = 24
@@ -334,11 +355,16 @@ mote_drift_up = 0.5
334
355
  - **Forgetting to enable the source mesh's emission before assigning the glow color.** The shader's emission stays black. The controller's `_resolve_source_mesh` enables it.
335
356
  - **`pulse_hz` too high.** Above 3 Hz it strobes — accessibility issue (photosensitivity). Default 0.7 is calm.
336
357
  - **Generating an "aura" PNG and using as a billboard sprite.** The misroute. The procedural light + motes are alive and scale to any item.
358
+ - **`BaseMaterial3D.new()` for the mote material.** `BaseMaterial3D` is abstract — that line is a parse error and takes the whole script down. Instantiate `StandardMaterial3D`; keep using the `BaseMaterial3D.*` enum constants.
359
+ - **A multiplicative `set_intensity` (`light_energy_base *= scale`).** One `set_intensity(0.0)` pins the field to 0.0 permanently, so the charge-up tween from 0 to 1 does nothing. Hold the scale in its own variable and apply it where the energy is written.
337
360
 
338
361
  ## Performance notes
339
362
 
340
- - One glow = 1 light + 16–32 particles. ~0.05 ms total. Cheap.
341
- - N glowing items in a scene: budget ~12 active `OmniLight3D`s before you should consider `LightOccluder3D` culling. Motes scale linearly to ~512 across all glows.
363
+ - One glow = 1 light + 16–32 particles. Sub-millisecond — an order-of-magnitude expectation, not a measurement.
364
+ - N glowing items in a scene: budget ~12 active `OmniLight3D`s before you start
365
+ culling. The current Summer build has no `LightOccluder3D`
366
+ (`ClassDB.class_exists` is `false`; `LightOccluder2D` is 2D-only). Cull with
367
+ `distance_fade_enabled` / `distance_fade_begin` or a smaller `omni_range`.
342
368
  - Mobile: drop `mote_count` to 8, `light_range` × 0.5, `light_shadow_enabled = false` always.
343
369
  - LOD: in `_process`, scale `_motes.amount_ratio` and `_light.light_energy` by `clamp(1.5 - dist_to_camera / 20.0, 0.10, 1.0)`. Past 20 m, drop motes entirely; keep light.
344
370
 
@@ -352,11 +378,18 @@ mote_drift_up = 0.5
352
378
 
353
379
  ## Fallback (no MCP)
354
380
 
355
- VFX is code, no MCP required:
381
+ Section 4 is fully automatable — `summer_write_file` writes the controller,
382
+ `summer_add_node` + `summer_set_prop` attach and tune it, and `summer_create_scene` +
383
+ `summer_save_scene` produce `magic_glow.tscn`. Do not hand these steps to the user
384
+ when the MCP tools are available.
385
+
386
+ Without the MCP connection there is no engine to drive, so the user does it
387
+ manually in Summer Engine:
356
388
 
357
389
  1. Create `addons/vfx/magic-glow/` with the two files above.
358
- 2. In Godot, add a `Node3D` child to the glowing object, attach `magic_glow.gd`. Save as `magic_glow.tscn` if you want a scene to instantiate.
390
+ 2. Add a `Node3D` child to the glowing object, attach `magic_glow.gd`. Save as `magic_glow.tscn` if you want a scene to instantiate.
359
391
  3. Tune the exported parameters in the inspector or via script.
392
+ 4. Check the Errors dock before tuning anything.
360
393
 
361
394
  ## Handoff
362
395
 
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: visual-effects
7
7
  user-invocable: true
8
- allowed-tools: Read Write Edit summer_add_node summer_set_prop summer_set_resource_property summer_inspect_node summer_save_scene
8
+ allowed-tools: Read Write Edit summer_write_file summer_read_file summer_create_scene summer_add_node summer_set_prop summer_set_resource_property summer_connect_signal summer_inspect_node summer_save_scene summer_get_script_errors summer_get_debugger_errors summer_play summer_stop
9
9
  paths: ["**/*.tscn", "**/*.gd", "**/*.gdshader", "addons/vfx/**"]
10
10
  ---
11
11
 
@@ -25,7 +25,9 @@ A muzzle flash is on screen for 60–100 ms. Long enough to read, too short to a
25
25
 
26
26
  - The user wants a *continuous* flame (flamethrower) — use `fire`, not muzzle-flash.
27
27
  - The user wants the impact spark on the wall the bullet hit — that's `hit-spark`.
28
- - The user wants a tracer line from barrel to target — use a `Line3D`/`ImmediateMesh` recipe, not this. (Or pair this with `lightning` for an energy weapon.)
28
+ - The user wants a tracer line from barrel to target — use an `ImmediateMesh`
29
+ recipe, not this. The current Summer build has no `Line3D`
30
+ (`ClassDB.class_exists("Line3D")` is `false`).
29
31
  - The user wants a slow-glowing spell charge-up — use `magic-glow` for the buildup, then this for the release frame.
30
32
 
31
33
  ## Recipe
@@ -55,12 +57,19 @@ uniform float ray_sharpness : hint_range(0.5, 32.0) = 8.0;
55
57
  uniform float random_seed : hint_range(0.0, 6.2831) = 0.0; // rotates the star per shot
56
58
 
57
59
  void vertex() {
60
+ // Billboard toward the camera. The bare mat4(INV_VIEW_MATRIX[0..2], MODEL_MATRIX[3])
61
+ // form discards the node's scale, so the per-shot `flash_size` randomisation in
62
+ // fire() would never reach the screen — normalize the basis and re-apply scale.
58
63
  MODELVIEW_MATRIX = VIEW_MATRIX * mat4(
59
- INV_VIEW_MATRIX[0],
60
- INV_VIEW_MATRIX[1],
61
- INV_VIEW_MATRIX[2],
62
- MODEL_MATRIX[3]
63
- );
64
+ normalize(INV_VIEW_MATRIX[0]),
65
+ normalize(INV_VIEW_MATRIX[1]),
66
+ normalize(INV_VIEW_MATRIX[2]),
67
+ MODEL_MATRIX[3]) * mat4(
68
+ vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0),
69
+ vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0),
70
+ vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0),
71
+ vec4(0.0, 0.0, 0.0, 1.0));
72
+ MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);
64
73
  }
65
74
 
66
75
  void fragment() {
@@ -81,9 +90,11 @@ void fragment() {
81
90
  // Mix core color (white-hot) into flash color at the very center.
82
91
  vec3 col = mix(flash_color.rgb, core_color.rgb, pow(core, 4.0));
83
92
 
84
- ALBEDO = col;
85
- EMISSION = col * emission_boost * intensity;
86
- ALPHA = mask * intensity;
93
+ // `render_mode unshaded` outputs ALBEDO and discards EMISSION entirely
94
+ // (scene_forward_clustered.glsl:2962 — `frag_color = vec4(albedo, alpha)`
95
+ // under MODE_UNSHADED), so the bloom boost has to live in ALBEDO.
96
+ ALBEDO = col * emission_boost * intensity;
97
+ ALPHA = mask * intensity;
87
98
  }
88
99
  ```
89
100
 
@@ -98,6 +109,8 @@ extends Node3D
98
109
 
99
110
  ## Spawn one of these as a child of the gun's barrel tip and call `fire()` on each shot.
100
111
 
112
+ const SPARK_SCENE := "res://addons/vfx/hit-spark/hit_spark.tscn"
113
+
101
114
  @export var flash_color: Color = Color(1.0, 0.85, 0.45)
102
115
  @export_range(0.02, 0.30) var flash_duration: float = 0.08 ## seconds visible
103
116
  @export_range(0.10, 4.0) var flash_size: float = 0.6 ## meters across
@@ -134,8 +147,12 @@ func _ready() -> void:
134
147
  _light.shadow_enabled = false
135
148
  add_child(_light)
136
149
 
137
- if spawn_sparks:
138
- _sparks = preload("res://addons/vfx/hit-spark/hit_spark.tscn").instantiate() if ResourceLoader.exists("res://addons/vfx/hit-spark/hit_spark.tscn") else null
150
+ # preload() resolves at PARSE time, so a runtime ResourceLoader.exists() check
151
+ # cannot guard it — "Parse Error: Preload file ... does not exist" takes the
152
+ # whole script down when hit-spark hasn't been installed. Use load().
153
+ if spawn_sparks and ResourceLoader.exists(SPARK_SCENE):
154
+ var packed: PackedScene = load(SPARK_SCENE)
155
+ _sparks = packed.instantiate() as GPUParticles3D
139
156
  if _sparks:
140
157
  _sparks.one_shot = true
141
158
  _sparks.emitting = false
@@ -184,13 +201,19 @@ Node3D ("MuzzleFlash") [script: muzzle_flash.gd]
184
201
 
185
202
  Place the node at the **barrel tip** of the weapon. If your gun model has a `BarrelTip` Marker3D, parent there directly.
186
203
 
204
+ Every scene-mutating call takes an explicit `scenePath`. `summer_set_prop` uses `key`
205
+ (not `property`) and only accepts a string, number, or boolean.
206
+
187
207
  ```
188
- summer_add_node(parent="./Player/Weapon/BarrelTip", type="Node3D", name="MuzzleFlash")
189
- summer_set_prop(path="./Player/Weapon/BarrelTip/MuzzleFlash", property="script", value="res://addons/vfx/muzzle-flash/muzzle_flash.gd")
190
- summer_set_prop(path="./Player/Weapon/BarrelTip/MuzzleFlash", property="flash_size", value=0.6)
191
- summer_set_prop(path="./Player/Weapon/BarrelTip/MuzzleFlash", property="flash_duration", value=0.08)
192
- summer_set_prop(path="./Player/Weapon/BarrelTip/MuzzleFlash", property="light_energy", value=4.0)
193
- summer_save_scene
208
+ summer_write_file(path="res://addons/vfx/muzzle-flash/muzzle_flash.gdshader", content="<section 2>", create_only=true)
209
+ summer_write_file(path="res://addons/vfx/muzzle-flash/muzzle_flash.gd", content="<section 3>", create_only=true)
210
+
211
+ summer_add_node(scenePath="res://main.tscn", parent="./Player/Weapon/BarrelTip", type="Node3D", name="MuzzleFlash")
212
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Weapon/BarrelTip/MuzzleFlash", key="script", value="res://addons/vfx/muzzle-flash/muzzle_flash.gd")
213
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Weapon/BarrelTip/MuzzleFlash", key="flash_size", value=0.6)
214
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Weapon/BarrelTip/MuzzleFlash", key="flash_duration", value=0.08)
215
+ summer_set_prop(scenePath="res://main.tscn", path="./Player/Weapon/BarrelTip/MuzzleFlash", key="light_energy", value=4.0)
216
+ summer_save_scene(scenePath="res://main.tscn")
194
217
  ```
195
218
 
196
219
  Then in your weapon script, on `fire()`:
@@ -199,10 +222,22 @@ Then in your weapon script, on `fire()`:
199
222
  $BarrelTip/MuzzleFlash.fire()
200
223
  ```
201
224
 
202
- Or connect a signal:
225
+ Or connect the weapon's `fired` signal in the scene:
226
+
227
+ ```
228
+ summer_connect_signal(scenePath="res://main.tscn", emitter="./Player/Weapon", signal="fired", receiver="./Player/Weapon/BarrelTip/MuzzleFlash", method="fire")
229
+ ```
230
+
231
+ ### 5b. Verify
232
+
233
+ `muzzle_flash.gd` `preload`s its own shader, so a shader that failed to write takes
234
+ the script down at parse time. Check before firing a shot.
203
235
 
204
- ```gdscript
205
- weapon.fired.connect($BarrelTip/MuzzleFlash.fire)
236
+ ```
237
+ summer_get_script_errors # muzzle_flash.gd parsed?
238
+ summer_play
239
+ summer_get_debugger_errors # shader compile errors surface here at runtime
240
+ summer_stop
206
241
  ```
207
242
 
208
243
  ### 6. Parameters to tune
@@ -283,11 +318,12 @@ ray_sharpness = 18.0
283
318
  - **OmniLight3D with shadows enabled.** A 60 ms shadow pass costs more than the flash itself. `shadow_enabled = false`.
284
319
  - **Flash duration > 150 ms.** Reads as a flamethrower or a stuck animation. Stay under 100 ms unless intentional.
285
320
  - **Spawning a new MuzzleFlash each shot and freeing it.** Rapid-fire weapons leak. Reuse one anchored to the barrel and call `fire()`.
321
+ - **`preload`ing the optional hit-spark scene.** `preload()` resolves at parse time, so wrapping it in a runtime `ResourceLoader.exists()` check does nothing — the script fails to load entirely when hit-spark isn't installed. Use `load()` behind the check.
286
322
  - **Generating an animated flash sprite.** That's the misroute this skill exists to prevent. The procedural shader rotates per shot for free, and you can recolor without regenerating.
287
323
 
288
324
  ## Performance notes
289
325
 
290
- - One quad + one light + ~8 sparks = under 0.02 ms. Safe at any rate of fire.
326
+ - One quad + one light + ~8 sparks is negligible — safe at any rate of fire. That is an order-of-magnitude expectation, not a measurement.
291
327
  - The `OmniLight3D` snapping on/off per shot will trigger a shadow re-bake on lights with `shadow_enabled = true` — keep it off, always.
292
328
  - For full-auto weapons (10+ shots/sec), consider raising `flash_duration` to 0.10 and dropping `intensity` to 0.6 so consecutive flashes blend instead of strobe.
293
329
 
@@ -297,16 +333,23 @@ ray_sharpness = 18.0
297
333
  - **Weapon visible in a mirror / scope.** The additive quad will appear in reflections only if the reflection probe captures it; for scope ADS, render the flash into the scope's separate viewport.
298
334
  - **Underwater shooting.** Tint the flash blue-green via `flash_color`, drop `light_range` to 30%.
299
335
  - **Silenced weapon.** `flash_size = 0.10`, `light_energy = 0.5`, `flash_duration = 0.04`. Just a wink at the barrel.
300
- - **Suppressed flash with a tracer.** Pair this with a `Line3D` tracer from barrel to hit point, fired in the same call.
336
+ - **Suppressed flash with a tracer.** Pair this with an `ImmediateMesh` tracer from barrel to hit point, fired in the same call.
301
337
 
302
338
  ## Fallback (no MCP)
303
339
 
304
- VFX is code, no MCP required:
340
+ Section 5 is fully automatable — `summer_write_file` writes the shader and the
341
+ controller, `summer_add_node` + `summer_set_prop` place it, and
342
+ `summer_connect_signal` wires `fired` → `fire`. Do not hand these steps to the user
343
+ when the MCP tools are available.
344
+
345
+ Without the MCP connection there is no engine to drive, so the user does it
346
+ manually in Summer Engine:
305
347
 
306
348
  1. Create `addons/vfx/muzzle-flash/` and write the three files above.
307
- 2. In Godot, add a `Node3D` child to your weapon's barrel tip Marker3D.
349
+ 2. Add a `Node3D` child to your weapon's barrel tip Marker3D.
308
350
  3. Attach `muzzle_flash.gd` as the script. The runtime builds the quad + light.
309
351
  4. In your weapon's fire code: `$BarrelTip/MuzzleFlash.fire()`.
352
+ 5. Check the Errors dock — `muzzle_flash.gd` `preload`s the shader, so a broken shader path fails the script too.
310
353
 
311
354
  ## Handoff
312
355