summer-engine 1.5.0 → 2.2.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 (101) hide show
  1. package/{marketplace.json → .claude-plugin/marketplace.json} +3 -3
  2. package/.claude-plugin/plugin.json +52 -11
  3. package/.codex-plugin/plugin.json +69 -8
  4. package/.cursor-plugin/plugin.json +53 -12
  5. package/.factory-plugin/plugin.json +20 -0
  6. package/.opencode/INSTALL.md +75 -0
  7. package/.opencode/plugins/summer.js +9 -6
  8. package/AGENTS.md +51 -0
  9. package/CHANGELOG.md +31 -1
  10. package/CLAUDE.md +5 -0
  11. package/GEMINI.md +56 -0
  12. package/README.md +409 -80
  13. package/_persona/summer/SKILL.md +113 -0
  14. package/dist/bin/summer.js +0 -0
  15. package/dist/commands/assets.d.ts +2 -0
  16. package/dist/commands/assets.js +189 -0
  17. package/dist/commands/create.js +55 -17
  18. package/dist/commands/debug.d.ts +2 -0
  19. package/dist/commands/debug.js +127 -0
  20. package/dist/commands/install.js +2 -2
  21. package/dist/commands/list.js +24 -5
  22. package/dist/commands/project.d.ts +2 -0
  23. package/dist/commands/project.js +170 -0
  24. package/dist/commands/scene.d.ts +2 -0
  25. package/dist/commands/scene.js +290 -0
  26. package/dist/lib/banner.js +2 -2
  27. package/dist/lib/log-filters.d.ts +53 -0
  28. package/dist/lib/log-filters.js +130 -0
  29. package/dist/lib/plugin-manifests.test.d.ts +1 -0
  30. package/dist/lib/plugin-manifests.test.js +134 -0
  31. package/dist/lib/remote-templates.d.ts +25 -0
  32. package/dist/lib/remote-templates.js +86 -0
  33. package/dist/lib/skills-registry.d.ts +283 -3
  34. package/dist/lib/skills-registry.js +522 -2
  35. package/dist/lib/telemetry.d.ts +13 -0
  36. package/dist/lib/telemetry.js +63 -0
  37. package/dist/lib/with-engine.d.ts +14 -0
  38. package/dist/lib/with-engine.js +109 -0
  39. package/dist/mcp/tools/asset-tools.js +12 -13
  40. package/dist/mcp/tools/debug-tools.js +32 -9
  41. package/dist/mcp/tools/generate-tools.js +101 -0
  42. package/dist/mcp/tools/with-engine.js +4 -0
  43. package/gemini-extension.json +2 -2
  44. package/hooks/pre-commit-doctor.sh +0 -0
  45. package/hooks/run-hook.cmd +0 -0
  46. package/hooks/session-start.sh +0 -0
  47. package/package.json +11 -5
  48. package/skills/2d-assets/character-portrait/SKILL.md +196 -0
  49. package/skills/2d-assets/concept-art/SKILL.md +152 -0
  50. package/skills/2d-assets/pixel-art/SKILL.md +180 -0
  51. package/skills/2d-assets/skybox-panorama/SKILL.md +207 -0
  52. package/skills/2d-assets/sprite-sheet/SKILL.md +239 -0
  53. package/skills/2d-assets/tileable-texture/SKILL.md +205 -0
  54. package/skills/2d-assets/ui-graphics/SKILL.md +210 -0
  55. package/skills/3d-assets/character-model/SKILL.md +225 -0
  56. package/skills/3d-assets/environment-kit/SKILL.md +164 -0
  57. package/skills/3d-assets/organic-model/SKILL.md +192 -0
  58. package/skills/3d-assets/prop-model/SKILL.md +133 -0
  59. package/skills/3d-assets/vehicle-model/SKILL.md +202 -0
  60. package/skills/README.md +1 -1
  61. package/skills/_meta/skill-create/SKILL.md +1 -1
  62. package/skills/_meta/skill-improve/SKILL.md +1 -1
  63. package/skills/_meta/skill-test/SKILL.md +1 -1
  64. package/skills/_meta/using-summer/SKILL.md +130 -0
  65. package/skills/ai-and-npcs/design-npc/SKILL.md +3 -1
  66. package/skills/animation/animation-tree/SKILL.md +260 -0
  67. package/skills/animation/facial-and-lipsync/SKILL.md +282 -0
  68. package/skills/animation/generate-motion/SKILL.md +129 -0
  69. package/skills/animation/procedural-animation/SKILL.md +234 -0
  70. package/skills/animation/retarget/SKILL.md +172 -0
  71. package/skills/asset-pipeline/asset-strategy/SKILL.md +102 -212
  72. package/skills/audio/adaptive-music/SKILL.md +326 -0
  73. package/skills/audio/ambient-bed/SKILL.md +278 -0
  74. package/skills/audio/music-track/SKILL.md +268 -0
  75. package/skills/audio/sound-effect/SKILL.md +237 -0
  76. package/skills/audio/voice-line/SKILL.md +253 -0
  77. package/skills/catalog.yaml +266 -5
  78. package/skills/character-controllers/fps-controller/SKILL.md +1 -1
  79. package/skills/debugging/debug/SKILL.md +102 -82
  80. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -1
  81. package/skills/scene-and-project/browse-templates/SKILL.md +105 -0
  82. package/skills/scene-and-project/make-game/SKILL.md +159 -346
  83. package/skills/scene-and-project/new-project/SKILL.md +106 -0
  84. package/skills/scene-and-project/scene-composition/SKILL.md +1 -1
  85. package/skills/scripting-patterns/gdscript-patterns/SKILL.md +1 -1
  86. package/skills/ui-and-ux/ui-basics/SKILL.md +1 -1
  87. package/skills/video/animated-loop/SKILL.md +200 -0
  88. package/skills/video/cinematic-cutscene/SKILL.md +204 -0
  89. package/skills/video/trailer-shot/SKILL.md +203 -0
  90. package/skills/visual-effects/_building-blocks/additive-billboard-particles.md +28 -0
  91. package/skills/visual-effects/_building-blocks/noise-3d-fbm.gdshaderinc +45 -0
  92. package/skills/visual-effects/_building-blocks/trauma-shake-snippet.md +51 -0
  93. package/skills/visual-effects/{vfx → game-feel}/SKILL.md +1 -1
  94. package/skills/visual-effects/recipes/dissolve/SKILL.md +349 -0
  95. package/skills/visual-effects/recipes/fire/SKILL.md +387 -0
  96. package/skills/visual-effects/recipes/hit-spark/SKILL.md +330 -0
  97. package/skills/visual-effects/recipes/lightning/SKILL.md +372 -0
  98. package/skills/visual-effects/recipes/magic-glow/SKILL.md +377 -0
  99. package/skills/visual-effects/recipes/muzzle-flash/SKILL.md +326 -0
  100. package/skills/visual-effects/recipes/smoke/SKILL.md +381 -0
  101. package/skills/visual-effects/recipes/water-ripple/SKILL.md +340 -0
@@ -0,0 +1,377 @@
1
+ ---
2
+ name: magic-glow
3
+ description: Use when authoring a magic-glow visual effect — a pulsing OmniLight3D plus drifting additive motes plus optional emission shader on the source mesh. Trigger on "magic glow", "enchanted item", "soul gem", "pulsing aura", "rune glow", "magical orb", "summon circle glow", "fairy", "wisp".
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: visual-effects
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
9
+ paths: ["**/*.tscn", "**/*.gd", "**/*.gdshader", "addons/vfx/**"]
10
+ ---
11
+
12
+ # magic-glow — Pulsing Light + Drifting Motes
13
+
14
+ Three layered cues that together read as "magical": a pulsing `OmniLight3D` (sin-wave energy), drifting additive billboard motes orbiting the source, and optional emission on the source mesh itself. Used for: enchanted items, soul gems, runes, summoning circles, wisps, fairies, magical waypoints. The recipe writes a single controller that builds and animates all three layers from one node.
15
+
16
+ ## When to use
17
+
18
+ - "This sword is enchanted — add a glow."
19
+ - "Make the soul gem pulse with light."
20
+ - "Magic rune on the floor glowing."
21
+ - "Wisp / fairy / firefly companion."
22
+ - "The summoning circle is active."
23
+ - "Quest item glows so the player notices it."
24
+ - Pre-charge phase before casting `lightning` or `muzzle-flash` (charge-up beam).
25
+
26
+ ## When NOT to use
27
+
28
+ - The user wants a static, non-animated glow — just enable emission on the mesh's material; this recipe is overkill.
29
+ - The user wants flames, not a glow — use `fire` (or `magic-fire` variant of `fire`).
30
+ - The user wants a beam shooting out, not an aura — use `lightning` with the `plasma-laser` variant.
31
+ - The user wants a screen-space bloom on the whole scene — that's `WorldEnvironment.glow_*`, not a per-object recipe.
32
+ - The user wants a 2D glow on a UI sprite — use `canvas_item` shader with a radial gradient, not this 3D recipe.
33
+
34
+ ## Recipe
35
+
36
+ ### 1. Files to create
37
+
38
+ ```
39
+ addons/vfx/magic-glow/magic_glow.gd
40
+ addons/vfx/magic-glow/magic_glow.tscn
41
+ ```
42
+
43
+ No custom shader — uses canonical additive billboard motes (see `_building-blocks/additive-billboard-particles.md`) plus an `OmniLight3D`. Optional: pair with the source mesh's existing material's emission.
44
+
45
+ ### 2. GDScript controller
46
+
47
+ `addons/vfx/magic-glow/magic_glow.gd`:
48
+
49
+ ```gdscript
50
+ @tool
51
+ class_name MagicGlow
52
+ extends Node3D
53
+
54
+ ## Builds: OmniLight3D (pulsing) + GPUParticles3D motes (drifting orbit) + optional mesh emission tween.
55
+ ## Add as a child of any glowing object.
56
+
57
+ @export_group("Color")
58
+ @export var glow_color: Color = Color(0.55, 0.85, 1.0)
59
+ @export_range(0.0, 12.0) var light_energy_base: float = 1.4
60
+ @export_range(0.0, 12.0) var light_energy_amplitude: float = 0.8
61
+
62
+ @export_group("Pulse")
63
+ @export_range(0.05, 4.0) var pulse_hz: float = 0.7
64
+ @export var pulse_easing: Curve ## leave null for sine; assign for custom
65
+
66
+ @export_group("Light")
67
+ @export_range(0.5, 30.0) var light_range: float = 4.0
68
+ @export var light_shadow_enabled: bool = false
69
+
70
+ @export_group("Motes")
71
+ @export_range(0, 256) var mote_count: int = 24
72
+ @export_range(0.05, 4.0) var mote_radius: float = 0.6
73
+ @export_range(0.5, 8.0) var mote_lifetime: float = 2.0
74
+ @export_range(0.01, 0.30) var mote_size: float = 0.06
75
+ @export_range(0.0, 8.0) var mote_emission_boost: float = 4.0
76
+ @export var mote_drift_up: float = 0.3 ## meters/sec mean upward drift
77
+
78
+ @export_group("Source mesh emission (optional)")
79
+ @export var pulse_source_mesh: NodePath ## point at a MeshInstance3D to tween its emission_energy_multiplier
80
+ @export_range(0.0, 12.0) var source_emission_base: float = 1.0
81
+ @export_range(0.0, 12.0) var source_emission_amplitude: float = 1.5
82
+
83
+ var _light: OmniLight3D
84
+ var _motes: GPUParticles3D
85
+ var _source_mesh: MeshInstance3D
86
+ var _source_mat: BaseMaterial3D
87
+ var _t: float = 0.0
88
+
89
+ func _ready() -> void:
90
+ _build_light()
91
+ _build_motes()
92
+ _resolve_source_mesh()
93
+ set_process(true)
94
+
95
+ func _build_light() -> void:
96
+ _light = OmniLight3D.new()
97
+ _light.light_color = glow_color
98
+ _light.light_energy = light_energy_base
99
+ _light.omni_range = light_range
100
+ _light.shadow_enabled = light_shadow_enabled
101
+ add_child(_light)
102
+
103
+ func _build_motes() -> void:
104
+ if mote_count <= 0:
105
+ return
106
+ _motes = GPUParticles3D.new()
107
+ _motes.amount = mote_count
108
+ _motes.lifetime = mote_lifetime
109
+ _motes.one_shot = false
110
+ _motes.explosiveness = 0.0
111
+ _motes.local_coords = true
112
+
113
+ var pm := ParticleProcessMaterial.new()
114
+ pm.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE
115
+ pm.emission_sphere_radius = mote_radius
116
+ pm.direction = Vector3.UP
117
+ pm.spread = 180.0
118
+ pm.initial_velocity_min = 0.05
119
+ pm.initial_velocity_max = 0.20
120
+ pm.gravity = Vector3(0, mote_drift_up * 0.5, 0)
121
+ pm.scale_min = mote_size * 0.7
122
+ pm.scale_max = mote_size * 1.3
123
+ pm.color = glow_color
124
+ pm.damping_min = 0.5
125
+ pm.damping_max = 1.2
126
+ _motes.process_material = pm
127
+
128
+ var mesh := QuadMesh.new()
129
+ mesh.size = Vector2.ONE
130
+ var bm := BaseMaterial3D.new()
131
+ bm.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
132
+ bm.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
133
+ bm.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
134
+ bm.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
135
+ bm.albedo_color = glow_color
136
+ bm.emission_enabled = true
137
+ bm.emission = glow_color
138
+ bm.emission_energy_multiplier = mote_emission_boost
139
+ mesh.material = bm
140
+ _motes.draw_pass_1 = mesh
141
+
142
+ add_child(_motes)
143
+
144
+ func _resolve_source_mesh() -> void:
145
+ if pulse_source_mesh.is_empty(): return
146
+ var n := get_node_or_null(pulse_source_mesh)
147
+ if n is MeshInstance3D:
148
+ _source_mesh = n
149
+ var m := _source_mesh.get_active_material(0)
150
+ if m is BaseMaterial3D:
151
+ _source_mat = m
152
+ _source_mat.emission_enabled = true
153
+ if _source_mat.emission == Color.BLACK:
154
+ _source_mat.emission = glow_color
155
+
156
+ func _process(delta: float) -> void:
157
+ _t += delta
158
+ var phase: float = (sin(_t * TAU * pulse_hz) + 1.0) * 0.5 # 0..1
159
+ if pulse_easing:
160
+ phase = pulse_easing.sample(phase)
161
+
162
+ if _light:
163
+ _light.light_energy = light_energy_base + light_energy_amplitude * phase
164
+ if _source_mat:
165
+ _source_mat.emission_energy_multiplier = source_emission_base + source_emission_amplitude * phase
166
+
167
+ func set_intensity(scale: float) -> void:
168
+ ## 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
171
+ if _motes:
172
+ _motes.amount_ratio = scale
173
+
174
+ func extinguish(fade_seconds: float = 0.6) -> void:
175
+ var t := create_tween().set_parallel(true)
176
+ t.tween_property(_light, "light_energy", 0.0, fade_seconds)
177
+ if _motes:
178
+ t.tween_property(_motes, "amount_ratio", 0.0, fade_seconds)
179
+ t.chain().tween_callback(queue_free)
180
+ ```
181
+
182
+ ### 3. Node tree
183
+
184
+ ```
185
+ Node3D ("MagicGlow") [script: magic_glow.gd]
186
+ ├── OmniLight3D (created at runtime, pulses)
187
+ └── GPUParticles3D (created at runtime, drifting motes)
188
+ ```
189
+
190
+ ### 4. Wire it in (MCP calls)
191
+
192
+ For an enchanted sword:
193
+
194
+ ```
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
+ ```
205
+
206
+ For a soul gem on a pedestal:
207
+
208
+ ```
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)
214
+ ```
215
+
216
+ For a charge-up before casting:
217
+
218
+ ```gdscript
219
+ # Spawn at the wizard's hand:
220
+ var glow: MagicGlow = preload("res://addons/vfx/magic-glow/magic_glow.tscn").instantiate()
221
+ $Wizard/HandTip.add_child(glow)
222
+ glow.set_intensity(0.0)
223
+ var t := create_tween()
224
+ t.tween_method(glow.set_intensity, 0.0, 1.0, 0.8)
225
+ t.tween_callback(func() -> void:
226
+ Lightning.cast_lightning($Wizard/HandTip.global_position, target.global_position)
227
+ glow.extinguish(0.3))
228
+ ```
229
+
230
+ ### 5. Parameters to tune
231
+
232
+ | Parameter | Range | Effect |
233
+ |---|---|---|
234
+ | `glow_color` | Color | controls light, motes, and (if linked) source emission |
235
+ | `light_energy_base` | 0.0–12.0 | resting light brightness |
236
+ | `light_energy_amplitude` | 0.0–12.0 | pulse swing on top of base |
237
+ | `pulse_hz` | 0.05–4.0 | beats per second; <1 for slow breathing, >2 for buzzing |
238
+ | `light_range` | 0.5–30.0 m | reach of the light cast |
239
+ | `mote_count` | 0–256 | number of motes orbiting (0 = light only) |
240
+ | `mote_radius` | 0.05–4.0 m | spawn sphere radius around the source |
241
+ | `mote_lifetime` | 0.5–8.0 s | how long each mote drifts |
242
+ | `mote_size` | 0.01–0.30 m | per-mote billboard size |
243
+ | `mote_emission_boost` | 0.0–8.0 | bloom strength of motes |
244
+ | `mote_drift_up` | -1.0–3.0 | mean upward drift (negative = sink) |
245
+ | `pulse_source_mesh` | NodePath | optional: pulse a mesh's emission with the same phase |
246
+
247
+ ## Cookbook — named variants
248
+
249
+ ### enchanted-sword (default)
250
+
251
+ Cool blue, gentle pulse, few tight motes.
252
+
253
+ ```
254
+ glow_color = Color(0.55, 0.85, 1.0)
255
+ light_energy_base = 1.2
256
+ light_energy_amplitude = 0.5
257
+ pulse_hz = 0.7
258
+ light_range = 3.0
259
+ mote_count = 16
260
+ mote_radius = 0.15
261
+ mote_size = 0.04
262
+ pulse_source_mesh = "../Blade" # blade emission pulses too
263
+ ```
264
+
265
+ ### soul-gem
266
+
267
+ Deeper purple, slow heartbeat pulse, many motes.
268
+
269
+ ```
270
+ glow_color = Color(0.75, 0.30, 1.0)
271
+ light_energy_base = 1.0
272
+ light_energy_amplitude = 0.8
273
+ pulse_hz = 0.4
274
+ light_range = 4.0
275
+ mote_count = 32
276
+ mote_radius = 0.25
277
+ mote_size = 0.05
278
+ mote_drift_up = 0.4
279
+ ```
280
+
281
+ ### summoning-circle
282
+
283
+ Strong, broad, many motes drifting upward.
284
+
285
+ ```
286
+ glow_color = Color(0.45, 0.80, 1.0)
287
+ light_energy_base = 0.8
288
+ light_energy_amplitude = 1.6
289
+ pulse_hz = 0.3
290
+ light_range = 8.0
291
+ mote_count = 96
292
+ mote_radius = 1.5
293
+ mote_size = 0.08
294
+ mote_drift_up = 1.0
295
+ mote_lifetime = 3.0
296
+ ```
297
+
298
+ ### wisp-companion
299
+
300
+ Tiny floating orb. Attach to a `PathFollow3D` for the wandering motion.
301
+
302
+ ```
303
+ glow_color = Color(0.85, 1.0, 0.65)
304
+ light_energy_base = 0.6
305
+ light_energy_amplitude = 0.3
306
+ pulse_hz = 1.2
307
+ light_range = 2.5
308
+ mote_count = 8
309
+ mote_radius = 0.10
310
+ mote_size = 0.03
311
+ mote_drift_up = 0.0
312
+ ```
313
+
314
+ ### charge-up (paired with lightning/muzzle-flash)
315
+
316
+ Starts at 0 intensity, ramped via `set_intensity`. Set initial values for fully-charged state.
317
+
318
+ ```
319
+ glow_color = Color(0.55, 0.75, 1.0)
320
+ light_energy_base = 0.0 # script tweens this up
321
+ light_energy_amplitude = 2.0
322
+ pulse_hz = 2.5 # buzzing
323
+ mote_count = 24
324
+ mote_radius = 0.20
325
+ mote_drift_up = 0.5
326
+ ```
327
+
328
+ ## Anti-patterns
329
+
330
+ - **`light_shadow_enabled = true` on every magical item.** A player carrying a glowing sword adds a fast-moving shadow caster; expensive. Default off.
331
+ - **Light range too large** (10+ m for a small item). The glow lights up an entire room and the player loses sense of location. Match `light_range` to the item's importance.
332
+ - **Mote material set to `BLEND_MODE_MIX`.** Motes look like opaque dots, not glowing dust. Always `BLEND_MODE_ADD`.
333
+ - **Motes parented in world space (`local_coords = false`) for an item the player carries.** Motes trail behind as the player walks. Default `local_coords = true`.
334
+ - **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
+ - **`pulse_hz` too high.** Above 3 Hz it strobes — accessibility issue (photosensitivity). Default 0.7 is calm.
336
+ - **Generating an "aura" PNG and using as a billboard sprite.** The misroute. The procedural light + motes are alive and scale to any item.
337
+
338
+ ## Performance notes
339
+
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.
342
+ - Mobile: drop `mote_count` to 8, `light_range` × 0.5, `light_shadow_enabled = false` always.
343
+ - 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
+
345
+ ## Edge cases
346
+
347
+ - **Glow on a moving NPC's head (wisp follower).** Set `local_coords = true` (default) and parent under the NPC. The light moves with the character.
348
+ - **Glow on a particle system** (e.g., the wisp is a particle itself). Don't — combine into a single Node3D with the glow attached, or the light won't track the particle's position.
349
+ - **Multiple glows on the same object** (sword pommel + blade). Two MagicGlow children, different `pulse_hz` and slightly offset phases → richer feel. Use `pulse_easing` curve to sync if you want them to beat together.
350
+ - **Glow visible underwater.** Reduce `light_range` 50% (water absorbs light); tint slightly green.
351
+ - **Pulse needs to sync with audio (heartbeat).** Drive `pulse_hz` from your audio system's BPM, or replace `_process` with an `AnimationPlayer` track timed to a music event.
352
+
353
+ ## Fallback (no MCP)
354
+
355
+ VFX is code, no MCP required:
356
+
357
+ 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.
359
+ 3. Tune the exported parameters in the inspector or via script.
360
+
361
+ ## Handoff
362
+
363
+ After firing this recipe, suggest:
364
+
365
+ - `summer:visual-effects/recipes/lightning` — perfect pre-cast charge-up. Use the `charge-up` variant and ramp `set_intensity(0→1)` then call `Lightning.cast_lightning`.
366
+ - `summer:visual-effects/recipes/dissolve` — pair `summon-arrival` (materialize_object) with the `summoning-circle` variant.
367
+ - `summer:visual-effects/recipes/fire` (the `magic-fire` variant) — for items that should glow AND flame.
368
+ - `summer:visual-effects/game-feel` — slight time-dilation when picking up a pulsing quest item adds weight.
369
+ - `summer:audio/sound-effect` — generate `magical hum, ethereal shimmer, 2s loop` and play on the same anchor.
370
+
371
+ ## See also
372
+
373
+ - `_building-blocks/additive-billboard-particles.md` — canonical additive material settings (this recipe uses them for motes)
374
+ - `summer:visual-effects/recipes/lightning` — pre-cast charge-up partner
375
+ - `summer:visual-effects/recipes/dissolve` — for summon arrival pairing
376
+ - `summer:visual-effects/recipes/fire` — for the `magic-fire` variant
377
+ - `summer:gdscript-patterns` — for the controller idioms (signals, exports, `@tool`)