summer-engine 1.3.0 → 1.3.2

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 (48) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +7 -2
  3. package/dist/bin/summer.js +24 -1
  4. package/dist/commands/doctor.js +44 -20
  5. package/dist/commands/setup.js +77 -6
  6. package/dist/commands/skills.js +5 -3
  7. package/dist/lib/format.d.ts +23 -0
  8. package/dist/lib/format.js +60 -0
  9. package/dist/lib/format.test.d.ts +1 -0
  10. package/dist/lib/format.test.js +36 -0
  11. package/dist/lib/skills-registry.d.ts +16 -0
  12. package/dist/lib/skills-registry.js +35 -0
  13. package/package.json +5 -3
  14. package/skills/_meta/skill-test/SKILL.md +1 -1
  15. package/skills/_shared/summer-folder.md +44 -0
  16. package/skills/_tests/specs/art-direction.md +115 -0
  17. package/skills/_tests/specs/audio-direction.md +123 -0
  18. package/skills/_tests/specs/brainstorm-game.md +99 -0
  19. package/skills/_tests/specs/debug.md +94 -0
  20. package/skills/_tests/specs/design-level.md +113 -0
  21. package/skills/_tests/specs/design-mechanic.md +112 -0
  22. package/skills/_tests/specs/design-npc.md +112 -0
  23. package/skills/_tests/specs/export-and-ship.md +114 -0
  24. package/skills/_tests/specs/fps-controller.md +5 -5
  25. package/skills/_tests/specs/play.md +88 -0
  26. package/skills/_tests/specs/setup-multiplayer.md +106 -0
  27. package/skills/_tests/specs/tune-performance.md +104 -0
  28. package/skills/_tests/specs/vfx.md +98 -0
  29. package/skills/ai-and-npcs/design-npc/SKILL.md +343 -0
  30. package/skills/asset-pipeline/asset-strategy/SKILL.md +1 -0
  31. package/skills/audio/audio-direction/SKILL.md +258 -0
  32. package/skills/catalog.yaml +13 -3
  33. package/skills/character-controllers/fps-controller/SKILL.md +1 -0
  34. package/skills/debugging/debug/SKILL.md +137 -0
  35. package/skills/deployment/export-and-ship/SKILL.md +300 -0
  36. package/skills/gameplay-mechanics/design-mechanic/SKILL.md +265 -0
  37. package/skills/level-design/design-level/SKILL.md +266 -0
  38. package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +328 -0
  39. package/skills/performance/tune-performance/SKILL.md +219 -0
  40. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -0
  41. package/skills/rendering-and-lighting/art-direction/SKILL.md +284 -0
  42. package/skills/scene-and-project/brainstorm-game/SKILL.md +210 -0
  43. package/skills/scene-and-project/make-game/SKILL.md +1 -0
  44. package/skills/scene-and-project/play/SKILL.md +108 -0
  45. package/skills/scene-and-project/scene-composition/SKILL.md +1 -0
  46. package/skills/scripting-patterns/gdscript-patterns/SKILL.md +1 -0
  47. package/skills/ui-and-ux/ui-basics/SKILL.md +1 -0
  48. package/skills/visual-effects/vfx/SKILL.md +280 -0
@@ -0,0 +1,98 @@
1
+ # Skill Spec: /vfx
2
+
3
+ ## Fixture
4
+
5
+ - Godot 4.5 / Summer Engine project, main scene with `World/Player` (CharacterBody3D + Camera3D) and `World/Enemy` (CharacterBody3D + MeshInstance3D + a `damaged` signal on a script).
6
+ - Summer MCP tools available (engine running on localhost:6550).
7
+ - Host file tools available (Read, Edit, Write).
8
+
9
+ ## Case 1: Happy Path — combat hits feel flat
10
+
11
+ **Input:** "My combat feels flat. Add some juice."
12
+
13
+ **Expected behavior:**
14
+
15
+ 1. Skill asks one focused question: "What feels flat? combat hits / movement / pickups / damage / explosions / spell casts."
16
+ 2. User says "combat hits".
17
+ 3. Skill calls `summer_get_scene_tree`, then `summer_inspect_node "./World/Enemy"` to confirm a MeshInstance3D and a `damaged` signal exist.
18
+ 4. Skill proposes hit flash + camera shake bundle: "I'm about to add HitFlash on `./World/Enemy`, attach `scripts/hit_flash.gd`, attach `scripts/camera_shake.gd` to `./World/Player/Camera3D`, and connect the `damaged` signal. OK?"
19
+ 5. After user OK:
20
+ - `summer_add_node(parent="./World/Enemy", type="Node", name="HitFlash")`
21
+ - `Write scripts/hit_flash.gd`
22
+ - `Write scripts/camera_shake.gd`
23
+ - `summer_connect_signal(from="./World/Enemy", signal="damaged", to="./World/Enemy/HitFlash", method="flash")`
24
+ - `summer_save_scene`
25
+ - `summer_get_script_errors`
26
+
27
+ **Assertions:**
28
+
29
+ - [ ] Skill asks the "what feels flat?" question before any tool call.
30
+ - [ ] Skill reads the scene tree and inspects the relevant node before adding nodes.
31
+ - [ ] Skill explicitly states the bundle of changes and asks "May I" / "OK?" before mutation.
32
+ - [ ] HitFlash script uses Tween + `set_surface_override_material`, NOT a polled timer in `_process`.
33
+ - [ ] CameraShake uses quadratic falloff (`_trauma * _trauma`), NOT linear.
34
+ - [ ] Skill never calls `summer_set_resource_property` on an inline sub_resource (e.g. ParticleProcessMaterial).
35
+ - [ ] Skill ends with `summer_save_scene` then `summer_get_script_errors`.
36
+ - [ ] Final hit flash duration is 0.06–0.12 sec (NOT 0.3 sec).
37
+
38
+ ## Case 2: Failure Path — no Camera3D in scene
39
+
40
+ **Fixture:** `World/Player` exists but has no Camera3D child. User asks for screen shake.
41
+
42
+ **Input:** "Add screen shake when I take damage."
43
+
44
+ **Expected behavior:**
45
+
46
+ - Skill calls `summer_get_scene_tree` and `summer_inspect_node "./World/Player"`, sees no Camera3D.
47
+ - Skill does NOT silently spawn a Camera3D — it asks: "I don't see a Camera3D under `./World/Player`. Should I add one (and risk conflicting with an existing camera elsewhere), or do you have a camera node I should target?"
48
+ - Waits for the answer.
49
+
50
+ **Assertions:**
51
+
52
+ - [ ] Skill detects the missing Camera3D and stops to ask.
53
+ - [ ] Skill does NOT add a Camera3D unilaterally.
54
+ - [ ] Skill names a concrete next-step path (point to existing camera OR add new one explicitly).
55
+
56
+ ## Case 3: Edge Case — user wants "all the juice"
57
+
58
+ **Fixture:** Same as Case 1.
59
+
60
+ **Input:** "Add all the VFX. Make it feel like Hades."
61
+
62
+ **Expected behavior:**
63
+
64
+ - Skill resists the temptation to stack 5 recipes at once.
65
+ - Anchors on combat hits first (per skill's own guidance: "loudest perceived improvement per minute spent").
66
+ - Says: "I'd rather land hit-flash + screen-shake first so we can see them work, then stack pickups/explosions next. Sound good?"
67
+ - Does NOT propose a single bundle of 5 recipes.
68
+
69
+ **Assertions:**
70
+
71
+ - [ ] Skill explicitly chooses ONE recipe to start with, not all.
72
+ - [ ] Skill names "combat hits" as the anchor.
73
+ - [ ] Skill defers other recipes to a follow-up pass.
74
+
75
+ ## Case 4: No Summer MCP — file-edit fallback
76
+
77
+ **Fixture:** Same as Case 1, but Summer MCP is unavailable (Cursor in a project where the engine is not connected).
78
+
79
+ **Input:** "Add a hit flash to my enemy."
80
+
81
+ **Expected behavior:**
82
+
83
+ - Skill detects MCP unavailable.
84
+ - Asks the user to paste the relevant `.tscn` snippet (the Enemy node + its children) so the skill can write the right node-path references.
85
+ - Writes `scripts/hit_flash.gd` directly via host file tools.
86
+ - Provides a `.tscn` patch (or step-by-step manual editor instructions) for adding the HitFlash node and connecting the signal — does NOT blindly call `summer_*` tools.
87
+ - Still asks "May I write `scripts/hit_flash.gd`?" before each file write.
88
+
89
+ **Assertions:**
90
+
91
+ - [ ] Skill identifies MCP is unavailable and adapts.
92
+ - [ ] Generated GDScript is syntactically valid Godot 4 GDScript with type hints.
93
+ - [ ] Skill asks the user to paste the `.tscn` snippet for accurate node paths.
94
+ - [ ] Skill still applies the "May I write …" protocol on file writes.
95
+
96
+ ---
97
+
98
+ This spec runs via `/skill-test vfx spec` (see `_meta/skill-test/SKILL.md`).
@@ -0,0 +1,343 @@
1
+ ---
2
+ name: design-npc
3
+ description: Use when the user wants to design an enemy, NPC, boss, companion, civilian, or wave-spawned mob's behavior. Walks perception, decision logic, escalation states, animation/audio tells, and defeat handling — outputs a state-machine GDScript stub plus the recommended node tree. Trigger on "enemy", "NPC", "boss", "companion", "AI", "behavior", "mob", "perception", "design enemy".
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: ai-and-npcs
7
+ user-invocable: true
8
+ allowed-tools: Read Grep Edit Write summer_get_scene_tree summer_inspect_node summer_add_node summer_set_prop summer_set_resource_property summer_connect_signal summer_save_scene summer_get_script_errors summer_input_map_bind
9
+ paths: ["**/*.gd", "**/*.tscn"]
10
+ ---
11
+
12
+ # /design-npc — Design an NPC's Behavior End-to-End
13
+
14
+ ## Overview
15
+
16
+ A good NPC is legible: the player can read its mood, predict its next move, and feel the consequence of theirs. This skill walks the design from the inside out — what does the NPC perceive, how does it decide, what tells signal each state, and how does it die or leave the scene. Output: a state-machine GDScript stub plus the scene tree.
17
+
18
+ **Core principle:** behavior is what the player *sees*, not what the code does. Every state needs a tell.
19
+
20
+ ## Steps
21
+
22
+ ### 1. Pick the NPC archetype
23
+
24
+ Open with the question. Wait.
25
+
26
+ > What's the NPC for? Pick the closest:
27
+ > - **Basic enemy** (melee/ranged grunt, dies in 1–3 hits)
28
+ > - **Boss** (named, scripted phases, 30–120 sec fight)
29
+ > - **Quest-giver** (talks, gives objectives, doesn't fight)
30
+ > - **Friendly companion** (follows player, helps in combat)
31
+ > - **Civilian / ambient** (reacts to events, no combat AI)
32
+ > - **Wave-spawned mob** (cheap, many at once, simple behavior)
33
+
34
+ Each archetype maps to a different complexity budget:
35
+
36
+ | Archetype | States | Perception | Decision logic | Tells per state |
37
+ |---|---|---|---|---|
38
+ | Basic enemy | 4 (idle / alert / attack / dead) | Sight cone | State machine | Animation + 1 sound |
39
+ | Boss | 6–10 (per phase) | Sight + hearing + scripted | Behavior tree or hand-coded FSM | Full anim suite + VO |
40
+ | Quest-giver | 2 (idle / talking) | Trigger area | None / dialogue tree | Idle anim + greet |
41
+ | Companion | 5 (follow / combat / wait / heal / dead) | Sight + group awareness | State machine + utility scoring | Anim + voice |
42
+ | Civilian | 3 (wander / panic / dead) | Hearing (loud events) | Simple FSM | Anim + scream |
43
+ | Wave mob | 2 (charge / dead) | Direct line to player | None | Anim + impact |
44
+
45
+ ### 2. Sketch the four design pillars
46
+
47
+ Walk them in order. Don't skip.
48
+
49
+ #### a) Perception — what does this NPC *sense*?
50
+
51
+ Pick the minimum. Over-perception = unfair AI. Common sensors:
52
+
53
+ | Sensor | Node | Cost | Use case |
54
+ |---|---|---|---|
55
+ | Sight cone | RayCast3D + dot product check | Cheap | Standard enemy |
56
+ | Sphere proximity | Area3D with sphere shape | Cheap | "Felt your presence" / aggro range |
57
+ | Hearing event | Signal from world events (gunshot, footstep) | Cheapest (event-driven) | Alert nearby NPCs |
58
+ | Group awareness | Shared signal bus (autoload) | Cheap | Pack tactics |
59
+ | Scripted scripted (boss) | Manual state changes | Free | Phase transitions |
60
+
61
+ Recommended setup for a basic enemy:
62
+
63
+ ```
64
+ Enemy (CharacterBody3D)
65
+ ├── MeshInstance3D
66
+ ├── CollisionShape3D
67
+ ├── Vision (Area3D) # sphere shape, radius = sight range
68
+ │ └── CollisionShape3D
69
+ └── Sight (RayCast3D) # confirms line-of-sight when target in Vision area
70
+ ```
71
+
72
+ The two-step (Area3D for proximity, RayCast3D for line-of-sight) is the right default. Don't raycast every frame to every potential target — first filter by Area3D overlap, then raycast only the candidates.
73
+
74
+ #### b) Decision logic — state machine vs behavior tree
75
+
76
+ **Strong recommendation:** start with a state machine. Always.
77
+
78
+ | | State machine | Behavior tree |
79
+ |---|---|---|
80
+ | Best for | < 10 states, scripted bosses, basic enemies | Reactive complex AI, branching priorities |
81
+ | Author cost | Low — read top to bottom | High — tree structure has its own learning curve |
82
+ | Debug | Print current state | Tree visualizer needed |
83
+ | Common failure | State explosion at ~15 states | Premature complexity |
84
+
85
+ Use a behavior tree when:
86
+ - You have 10+ states with priority interactions.
87
+ - You want designers (not coders) editing logic.
88
+ - The NPC has simultaneous goals (hunt + survive + cover).
89
+
90
+ For everything else: FSM. See `scripting-patterns/state-machine-patterns/SKILL.md`.
91
+
92
+ #### c) Escalation — tells per state
93
+
94
+ Every state must have at least one *tell* the player can read. List them up front:
95
+
96
+ | State | Animation tell | Audio tell | VFX tell |
97
+ |---|---|---|---|
98
+ | Idle | Breathing loop, occasional look | Ambient (snore, hum) | None |
99
+ | Alert | Head turn toward target, posture change | "Huh?" / radio chatter | Eye glow / aggro line |
100
+ | Aggressive | Combat pose, weapon raised | Battle cry | Muzzle flash / windup glow |
101
+ | Attacking | Strike anim with telegraph | Swing/shoot sound | Particle trail |
102
+ | Hurt | Stagger anim | Pain sound | Hit flash (see `vfx`) |
103
+ | Defeated | Death anim + ragdoll | Death sound | Loot drop / dissolve |
104
+
105
+ **Telegraph is non-negotiable for boss/heavy attacks:** 0.3–0.7 sec of windup the player can react to. Faster than 0.3 sec = "unfair"; slower than 0.7 sec = "boring".
106
+
107
+ #### d) Defeat handling — what happens when it dies?
108
+
109
+ Plan it. "It just disappears" is bad. Choices:
110
+
111
+ - **Death animation** plays, body stays for N sec, then `queue_free`.
112
+ - **Ragdoll**: switch CollisionShape from KinematicBody to RigidBody. Adds weight to the kill.
113
+ - **Loot drop**: spawn a pickup at death position.
114
+ - **Cleanup**: remove from group, disconnect signals, free children. Otherwise leak.
115
+ - **Score / quest hook**: emit a `died` signal the gameplay layer listens for.
116
+
117
+ ### 3. Read the scene to find an attach point
118
+
119
+ ```
120
+ summer_get_scene_tree
121
+ summer_inspect_node "./World"
122
+ ```
123
+
124
+ Identify where the NPC will live (a `Enemies` parent, or directly under World). Confirm the player exists for line-of-sight.
125
+
126
+ ### 4. Propose the bundle
127
+
128
+ > I'm about to design a basic patrol-and-chase enemy. Scene tree: Enemy (CharacterBody3D) + Vision Area3D + Sight RayCast3D + CollisionShape3D + MeshInstance3D. Script `scripts/enemy_ai.gd` with 4 states (idle / alert / attack / dead). Sight cone radius 12m, line-of-sight raycast. Telegraph: 0.5 sec windup before attack. Drop loot on death. May I create the scene + script?
129
+
130
+ ### 5. Build it
131
+
132
+ **Preferred (Summer MCP):**
133
+
134
+ ```
135
+ summer_add_node(parent="./World/Enemies", type="CharacterBody3D", name="Enemy")
136
+ summer_add_node(parent="./World/Enemies/Enemy", type="CollisionShape3D", name="Body")
137
+ summer_add_node(parent="./World/Enemies/Enemy", type="MeshInstance3D", name="Mesh")
138
+ summer_add_node(parent="./World/Enemies/Enemy", type="Area3D", name="Vision")
139
+ summer_add_node(parent="./World/Enemies/Enemy/Vision", type="CollisionShape3D", name="VisionShape")
140
+ summer_add_node(parent="./World/Enemies/Enemy", type="RayCast3D", name="Sight")
141
+ summer_set_prop(path="./World/Enemies/Enemy/Sight", key="enabled", value="true")
142
+ summer_set_prop(path="./World/Enemies/Enemy/Sight", key="target_position", value="Vector3(0, 0, -12)")
143
+ ```
144
+
145
+ Set the vision shape to a SphereShape3D with `radius = 12`. Save as standalone `.tres` (do NOT inline sub_resource — see `_shared/mcp-tools-reference.md` § "Trap"):
146
+
147
+ ```
148
+ # Save shape resource externally, then:
149
+ summer_set_prop(path="./World/Enemies/Enemy/Vision/VisionShape", key="shape", value="res://shapes/vision_sphere.tres")
150
+ ```
151
+
152
+ Connect the area signal:
153
+
154
+ ```
155
+ summer_connect_signal(from="./World/Enemies/Enemy/Vision", signal="body_entered", to="./World/Enemies/Enemy", method="_on_target_seen")
156
+ ```
157
+
158
+ **Fallback (no MCP):** write the scene as `.tscn` text. Ask user to paste their existing scene first, then propose a unified diff.
159
+
160
+ ### 6. Drop the script
161
+
162
+ `scripts/enemy_ai.gd` (FSM stub):
163
+
164
+ ```gdscript
165
+ class_name EnemyAI
166
+ extends CharacterBody3D
167
+
168
+ enum State { IDLE, ALERT, ATTACK, DEAD }
169
+
170
+ @export var move_speed: float = 3.5
171
+ @export var attack_range: float = 2.0
172
+ @export var attack_telegraph: float = 0.5
173
+ @export var attack_damage: int = 10
174
+ @export var max_health: int = 30
175
+
176
+ @onready var sight: RayCast3D = $Sight
177
+ @onready var vision: Area3D = $Vision
178
+ @onready var anim: AnimationPlayer = $AnimationPlayer
179
+
180
+ signal died(position: Vector3)
181
+
182
+ var _state: State = State.IDLE
183
+ var _target: Node3D = null
184
+ var _health: int = 30
185
+
186
+ func _ready() -> void:
187
+ _health = max_health
188
+ vision.body_entered.connect(_on_body_entered)
189
+
190
+ func _physics_process(delta: float) -> void:
191
+ match _state:
192
+ State.IDLE:
193
+ _process_idle(delta)
194
+ State.ALERT:
195
+ _process_alert(delta)
196
+ State.ATTACK:
197
+ _process_attack(delta)
198
+ State.DEAD:
199
+ pass
200
+ move_and_slide()
201
+
202
+ func _process_idle(_delta: float) -> void:
203
+ velocity = Vector3.ZERO
204
+ # transition handled by _on_body_entered
205
+
206
+ func _process_alert(_delta: float) -> void:
207
+ if _target == null:
208
+ _change_state(State.IDLE)
209
+ return
210
+ if not _has_line_of_sight(_target):
211
+ _change_state(State.IDLE)
212
+ _target = null
213
+ return
214
+ var dir := (_target.global_position - global_position).normalized()
215
+ velocity = dir * move_speed
216
+ look_at(_target.global_position, Vector3.UP)
217
+ if global_position.distance_squared_to(_target.global_position) < attack_range * attack_range:
218
+ _change_state(State.ATTACK)
219
+
220
+ func _process_attack(_delta: float) -> void:
221
+ velocity = Vector3.ZERO
222
+ # attack handled in _change_state via async telegraph
223
+
224
+ func _change_state(new_state: State) -> void:
225
+ if _state == new_state:
226
+ return
227
+ _state = new_state
228
+ match new_state:
229
+ State.IDLE:
230
+ anim.play("idle")
231
+ State.ALERT:
232
+ anim.play("alert")
233
+ State.ATTACK:
234
+ _execute_attack()
235
+ State.DEAD:
236
+ anim.play("death")
237
+ died.emit(global_position)
238
+ await get_tree().create_timer(2.0).timeout
239
+ queue_free()
240
+
241
+ func _execute_attack() -> void:
242
+ anim.play("attack_windup")
243
+ await get_tree().create_timer(attack_telegraph).timeout
244
+ if _state != State.ATTACK or _target == null:
245
+ return
246
+ if global_position.distance_squared_to(_target.global_position) < (attack_range * 1.2) ** 2:
247
+ if _target.has_method("take_damage"):
248
+ _target.take_damage(attack_damage)
249
+ anim.play("attack_recover")
250
+ await anim.animation_finished
251
+ _change_state(State.ALERT)
252
+
253
+ func _has_line_of_sight(target: Node3D) -> bool:
254
+ sight.target_position = sight.to_local(target.global_position)
255
+ sight.force_raycast_update()
256
+ if not sight.is_colliding():
257
+ return true
258
+ return sight.get_collider() == target
259
+
260
+ func _on_body_entered(body: Node3D) -> void:
261
+ if body.is_in_group("player") and _state == State.IDLE:
262
+ _target = body
263
+ _change_state(State.ALERT)
264
+
265
+ func take_damage(amount: int) -> void:
266
+ if _state == State.DEAD:
267
+ return
268
+ _health -= amount
269
+ if _health <= 0:
270
+ _change_state(State.DEAD)
271
+ ```
272
+
273
+ Key design notes embedded above:
274
+ - Telegraph (`attack_telegraph`) is a parameter — designers can tune feel.
275
+ - `take_damage` is the public hook the player's weapon calls.
276
+ - `died` signal is the gameplay-layer hook (loot, score, quest progress).
277
+ - `distance_squared_to` for comparisons (no sqrt cost).
278
+ - LOS check uses `force_raycast_update` for one-shot precision.
279
+
280
+ ### 7. Verify
281
+
282
+ ```
283
+ summer_save_scene
284
+ summer_get_script_errors
285
+ summer_play
286
+ # user tests
287
+ summer_stop
288
+ ```
289
+
290
+ If the enemy doesn't aggro, common causes:
291
+ - Player isn't in `player` group.
292
+ - Vision Area3D's sphere shape is inline sub_resource (silent fail).
293
+ - Sight RayCast3D `enabled = false` or `collide_with_areas = true` blocking on its own Vision area.
294
+
295
+ ## Common mistakes
296
+
297
+ | Don't | Do | Why |
298
+ |---|---|---|
299
+ | 360° perception | Sight cone or sphere with raycast LOS | "Sees through walls" feels unfair |
300
+ | Instant attack | 0.3–0.7 sec telegraph | Player needs to read the swing |
301
+ | Behavior tree for a grunt | State machine with 4 states | BT overhead unjustified at low complexity |
302
+ | Per-frame `get_tree().get_first_node_in_group("player")` | Cache target on aggro | Tree walks aren't free |
303
+ | `distance_to` in `_physics_process` | `distance_squared_to` for comparisons | Square root costs at scale |
304
+ | Same animation in all states | Distinct animations per state | The state machine is invisible; animations make it visible |
305
+ | `queue_free()` instantly on death | Death anim → 2 sec → free | Instant despawn breaks immersion + skips loot drops |
306
+ | Inline SphereShape3D for Vision Area3D | Standalone `.tres` | Inline sub_resources break SetResourceProperty |
307
+ | 100+ enemies with full FSM | Wave-mob archetype (2 states, no LOS) | Cheap mobs need cheap brains |
308
+ | No `died` signal | Emit on death | Gameplay layer needs the hook |
309
+
310
+ ## Anti-patterns
311
+
312
+ - **"AI that dodges everything."** Enemies that 100% dodge feel cheap. Random reaction time (50–250 ms) reads as skill, not aimbot.
313
+ - **Boss with 12 unique mechanics.** Cap at 4–6 distinct mechanics across phases. Players can't track more.
314
+ - **Companion that walks through doors.** NavigationAgent3D with proper navmesh. Don't fake it with raw `move_toward`.
315
+ - **Civilian that screams forever.** Add a calm-down state on a 5–10 sec timer.
316
+
317
+ ## Collaborative protocol
318
+
319
+ This skill writes scenes and scripts. Always ask before each step. Group related writes into one ask. See `_shared/collaborative-protocol.md`.
320
+
321
+ ## Want a working starter?
322
+
323
+ For a fully-rigged enemy with anim controller + loot drop:
324
+
325
+ → **template-id**: `template-3d-fps` — ships with a sample patrol enemy
326
+ → **Repo**: https://github.com/SummerEngine/FPS-template-Summer-Engine
327
+
328
+ For higher-complexity AI (boss with phases, behavior trees), defer to `ai-and-npcs/behavior-trees/SKILL.md`.
329
+
330
+ ## See also
331
+
332
+ - `_shared/mcp-tools-reference.md` — full MCP tool list
333
+ - `_shared/godot-version.md` — Godot 4.5 API notes
334
+ - `_shared/collaborative-protocol.md` — "May I write" pattern
335
+ - `_shared/gd-style.md` — typed GDScript conventions
336
+ - `ai-and-npcs/state-machine-npc/SKILL.md` — FSM pattern deep dive
337
+ - `ai-and-npcs/behavior-trees/SKILL.md` — when complexity demands a BT
338
+ - `ai-and-npcs/perception-sight-and-hearing/SKILL.md` — sensor patterns
339
+ - `ai-and-npcs/navmesh-pathfinding/SKILL.md` — NavigationAgent3D for movement
340
+ - `ai-and-npcs/llm-driven-dialogue/SKILL.md` — Summer's wedge for talking NPCs
341
+ - `ai-and-npcs/boss-patterns/SKILL.md` — phase transitions, mechanics
342
+ - `visual-effects/vfx/SKILL.md` — hit flash and death VFX recipes
343
+ - `scripting-patterns/state-machine-patterns/SKILL.md` — generic FSM patterns
@@ -4,6 +4,7 @@ description: Decision guide for how to create different game asset types — gen
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: asset-pipeline
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep summer_search_assets summer_import_asset summer_generate_image summer_generate_3d summer_generate_audio summer_import_from_url
8
9
  ---
9
10