summer-engine 1.3.1 → 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.
@@ -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
@@ -0,0 +1,258 @@
1
+ ---
2
+ name: audio-direction
3
+ description: Use when defining the sonic identity — music style, instruments, SFX vocabulary, dynamic music plan. Outputs an audio bible at `.summer/audio-bible.md`. Trigger on "audio direction", "audio bible", "music style", "sound design", "what should it sound like", "dynamic music".
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: audio
7
+ user-invocable: true
8
+ allowed-tools: Read Grep Glob Write Edit summer_get_project_context summer_get_scene_tree summer_inspect_node summer_add_node summer_project_setting summer_generate_audio
9
+ paths: [".summer/**", "project.godot", "**/*.tscn", "audio/**"]
10
+ ---
11
+
12
+ # /audio-direction — Define the Sonic Identity
13
+
14
+ ## Overview
15
+
16
+ Most indie games sound like a single library used flat. The same generic "swoosh" for menu, jump, pickup, hit. That's not audio direction — that's audio absence. This skill builds an audio bible that names the music style, the SFX vocabulary, the dynamic music plan, and the spatial / mix rules. Writes to `.summer/audio-bible.md` and configures the audio bus layout in the project so the rest of the work has a real foundation.
17
+
18
+ **Core principle:** SFX needs a *vocabulary*, not a *library*. Eight named SFX classes with consistent character beat 80 random library hits.
19
+
20
+ ## Steps
21
+
22
+ ### 1. Open with three references
23
+
24
+ Ask exactly this:
25
+
26
+ > Three reference tracks, games, or films for the music. What instruments matter? And what's the SFX vibe — chunky, clean, synthetic, or organic?
27
+
28
+ Three is the minimum. One or two collapses into a clone. References are the highest-signal input — listen carefully.
29
+
30
+ If they freeze, offer four axes (not titles):
31
+
32
+ > Pick the closest: a) orchestral + cinematic (Hollow Knight, Journey), b) lo-fi electronic + ambient (Disco Elysium, Outer Wilds), c) chiptune / synth (Hyper Light Drifter, Celeste), d) acoustic / folk (Sable, A Short Hike).
33
+
34
+ ### 2. Read the brief and art bible if they exist
35
+
36
+ ```
37
+ Read .summer/GameSoul.md
38
+ Read .summer/art-bible.md
39
+ ```
40
+
41
+ Audio must rhyme with the visuals. Quote it back:
42
+
43
+ > Art bible says "soft, hand-warmed, golden-hour, painterly". That rhymes with acoustic / folk / lo-fi over orchestral or aggressive synth. Confirming.
44
+
45
+ If the brief says "horror", that constrains the music style harder than the user's references will. Name the constraint.
46
+
47
+ ### 3. Pin the music style
48
+
49
+ Force one primary style. Sub-styles can blend, but a primary is required.
50
+
51
+ | Style | Anchor instruments | Pairs with | Avoid |
52
+ |---|---|---|---|
53
+ | Orchestral cinematic | Strings, brass, choir | Story-led, fantasy | Casual / cozy |
54
+ | Acoustic / folk | Guitar, piano, hand percussion | Cozy, narrative | Action / horror |
55
+ | Lo-fi electronic | Vintage synth pad, tape sat, light beat | Atmospheric, indie | Combat-heavy |
56
+ | Chiptune / synthwave | 8-bit / 16-bit chips, FM synth | Retro, platformer | Realism |
57
+ | Industrial / metal | Distorted bass, percussion-led | Horror, action | Cozy |
58
+ | Ambient drone | Pads, no drums, slow movement | Walking sim, horror | Action |
59
+ | Jazz / fusion | Live instruments, improv | Stylish, narrative | Generic action |
60
+ | Tracker / hyperpop | Glitchy, dense, modern | Esoteric, indie | Mass appeal |
61
+
62
+ Ask:
63
+
64
+ > One primary style. Which?
65
+
66
+ ### 4. Define tempo + key range
67
+
68
+ Concrete numbers, not "varies".
69
+
70
+ ```
71
+ Tempo range: 80–110 BPM (slow side; combat would push 120+)
72
+ Key center: D minor (warm but melancholic)
73
+ Modulation rule: combat tracks shift to D dorian, calm tracks to D natural minor
74
+ Time signature: 4/4 default; 6/8 for emotional cues
75
+ ```
76
+
77
+ ### 5. Build the SFX vocabulary
78
+
79
+ Eight named classes. Each class has a *character*, not a single sound.
80
+
81
+ | Class | Character | Examples in this game | DON'T |
82
+ |---|---|---|---|
83
+ | **UI** | Soft, woody, short (60-100ms) | Menu hover, button click, tab switch | No bell tones, no synth blips |
84
+ | **Pickup positive** | Warm, ascending interval (major third), 200-400ms | Coin, key, heal | No pure sine "ding" |
85
+ | **Pickup neutral** | Brief tactile, no melodic component | Note, breadcrumb, lore item | No ascending pitch (reads as reward) |
86
+ | **Negative / damage** | Punchy low-mid, slight detune | Player hit, enemy hit, fall damage | No long reverb tail |
87
+ | **Movement / footstep** | Material-aware (wood / stone / grass) | Walk, run, jump, land | No same sample for all surfaces |
88
+ | **Attack / action** | Front-loaded transient, short body | Swing, parry, dash | No layered orchestral hit |
89
+ | **Ambient layer** | Continuous, low presence | Wind, room tone, distant waves | No looping melody |
90
+ | **Stinger / event** | Distinctive, rare, melodic | Boss intro, level clear, story beat | No reuse for normal events |
91
+
92
+ Ask:
93
+
94
+ > Eight classes above. Adjust any character / examples / DON'Ts?
95
+
96
+ ### 6. Define the dynamic music plan
97
+
98
+ Static music = one track loops forever in a level. Boring. Dynamic music = layers swell with player state. Pick a model.
99
+
100
+ | Model | How it works | Cost | Best for |
101
+ |---|---|---|---|
102
+ | **Layered stems** | One bar / multiple instrument layers; combat adds drum stem, calm removes it | Medium (need stems authored) | Most games |
103
+ | **Vertical re-orchestration** | Same melody, different instrumentation per state | High (composer-led) | Story-led / boss-heavy |
104
+ | **Horizontal sequencing** | Different tracks for different states, crossfade | Low (just author 3-5 full tracks) | Vertical slice scope |
105
+ | **Procedural / generative** | Generate from rules in real-time | High dev cost | Special-case |
106
+
107
+ Pick one. State the state machine:
108
+
109
+ ```
110
+ States:
111
+ CALM — exploration, low intensity
112
+ TENSION — enemies aware but not engaged
113
+ COMBAT — engaged
114
+ VICTORY — encounter cleared (4-second sting → return to CALM)
115
+
116
+ Transitions:
117
+ CALM → TENSION when an enemy detects the player (2s crossfade)
118
+ TENSION → COMBAT when first damage exchanged (instant drum stem)
119
+ COMBAT → VICTORY when last enemy dies (drum stem cuts, brass swell)
120
+ VICTORY → CALM after 4s
121
+
122
+ Rules:
123
+ No state stays > 90s without variation (loop with bar-1 randomization)
124
+ Volume ducking: dialogue active → music -6dB
125
+ Boss music is its own track; ignores normal state machine
126
+ ```
127
+
128
+ ### 7. Define the spatial + mix rules
129
+
130
+ | Rule | Value |
131
+ |---|---|
132
+ | 3D attenuation curve | Inverse-square (default), max distance 25m |
133
+ | Reverb buses | 1 small (interior), 1 large (cave / hall), 1 outdoor |
134
+ | HRTF for player | ON (headphone-friendly directional cues) |
135
+ | Master ceiling | -1 dBTP (true peak, no clipping on consoles) |
136
+ | Music vs SFX vs UI mix | -8 dB / 0 dB / -3 dB (SFX is loudest) |
137
+ | Footstep rolloff | Cuts at 4m for player; cuts at 10m for NPC (player needs them, distant NPCs are noise) |
138
+ | Compression | Light bus compression on master (2:1, slow attack, 100ms release) |
139
+
140
+ ### 8. Configure the audio bus layout
141
+
142
+ Real audio in Godot needs a bus layout. Default Godot has Master only — configure the buses now.
143
+
144
+ **Preferred (Summer MCP):** Buses are typically in `default_bus_layout.tres`. Modify project setting:
145
+
146
+ ```
147
+ summer_project_setting(key="audio/buses/default_bus_layout", value="res://audio/default_bus_layout.tres")
148
+ ```
149
+
150
+ Ask the user:
151
+
152
+ > May I create `audio/default_bus_layout.tres` with these buses: Master / Music / SFX / UI / Voice / Ambient / Reverb_Small / Reverb_Large / Reverb_Outdoor?
153
+
154
+ On yes, write the `.tres` directly via `Write` (it's plain Godot resource format the host can produce):
155
+
156
+ ```
157
+ Write audio/default_bus_layout.tres
158
+ ```
159
+
160
+ **Fallback (no MCP):** Print the `.tres` for the user to paste, plus the `project.godot` line.
161
+
162
+ ### 9. Save the audio bible
163
+
164
+ Always write the bible. Path: `.summer/audio-bible.md`.
165
+
166
+ ```markdown
167
+ # Audio Bible — <Game Name>
168
+
169
+ **References:** <3 specific tracks / games / films>
170
+ **Style:** <one of the 8 above>
171
+ **Tempo / key:** 80–110 BPM, D minor, 4/4
172
+
173
+ ## SFX Vocabulary
174
+ | Class | Character | Examples | DON'T |
175
+ |---|---|---|---|
176
+ | UI | Soft, woody, 60-100ms | hover, click | bell tones |
177
+ | Pickup positive | Warm major-third, 200-400ms | coin, heal | pure sine |
178
+ | ... | ... | ... | ... |
179
+
180
+ ## Dynamic Music Plan
181
+ **Model:** Layered stems
182
+ **States:** CALM / TENSION / COMBAT / VICTORY
183
+ **Transitions:** ...
184
+ **Rules:** ...
185
+
186
+ ## Spatial + Mix
187
+ - 3D attenuation: inverse-square, 25m
188
+ - Reverb buses: small / large / outdoor
189
+ - HRTF: ON
190
+ - Master ceiling: -1 dBTP
191
+ - Mix bus: Music -8 dB, SFX 0 dB, UI -3 dB
192
+ - Footstep rolloff: 4m player / 10m NPC
193
+
194
+ ## Bus Layout
195
+ Master → Music, SFX (→ Reverb_Small / Large / Outdoor), UI, Voice, Ambient
196
+ ```
197
+
198
+ Ask:
199
+
200
+ > May I create `.summer/audio-bible.md` with this bible?
201
+
202
+ ### 10. Optional — generate first SFX seeds
203
+
204
+ If the user wants to start generating, propose 3-5 SFX seeds aligned with the vocabulary. These are starting points, not finals.
205
+
206
+ ```
207
+ summer_generate_audio(prompt="soft woody UI click, 80ms, no reverb, neutral pitch", duration=0.1)
208
+ summer_generate_audio(prompt="warm major-third coin pickup, 300ms, light reverb", duration=0.3)
209
+ summer_generate_audio(prompt="punchy low-mid player damage hit, slight detune, 200ms, no tail", duration=0.2)
210
+ ```
211
+
212
+ Ask first:
213
+
214
+ > May I generate first-pass SFX seeds for UI, pickup, and damage classes? They're starting points, not finals.
215
+
216
+ ### 11. Route to the next step
217
+
218
+ End with:
219
+
220
+ > Bible saved. Next:
221
+ > - `/summer:design-mechanic` to wire SFX cues into specific mechanics.
222
+ > - `/summer:vfx` if you haven't paired audio with visual effect cues yet.
223
+ > - Author or generate the calm music track first; it's the longest-running and sets the room tone.
224
+
225
+ ## Common mistakes
226
+
227
+ | Mistake | Fix |
228
+ |---|---|
229
+ | Single reference for music | Three minimum. Listen for the *common* element across them. |
230
+ | "Library" instead of "vocabulary" | A bag of free SFX with no shared character feels random. Eight named classes with character notes. |
231
+ | Same SFX for all events | If pickup-coin and pickup-key sound identical, the player learns nothing from audio. |
232
+ | No dynamic music plan | Static loops bore in 90s. Pick a model (layered / vertical / horizontal / procedural). |
233
+ | Music louder than SFX | Players need SFX for gameplay; music is mood. Default mix is -8 dB music vs 0 dB SFX. |
234
+ | No reverb buses | "Send" architecture (rooms route to a small/large/outdoor reverb bus) is mandatory or your mix sounds dry-then-wet inconsistently. |
235
+ | Pure sine "ding" for pickup | Reads as cheap mobile game. Use a major-third interval with character. |
236
+ | Same footstep sample for all surfaces | Material-aware footsteps are 80% of the perceived audio quality. |
237
+ | No master ceiling / true peak | Audio clips on console builds without `-1 dBTP` ceiling. Always set it. |
238
+ | Calling SetResourceProperty on inline sub_resources | Silent fail. See `_shared/mcp-tools-reference.md` § Trap. |
239
+
240
+ ## Collaborative protocol
241
+
242
+ This skill writes one bible (`.summer/audio-bible.md`), creates a bus layout (`audio/default_bus_layout.tres`), modifies `project.godot`, and may generate audio (metered). Always ask before writing or generating. See `_shared/collaborative-protocol.md`.
243
+
244
+ ## Want a working starter?
245
+
246
+ No template — this is a workflow that produces the bible the rest of the project obeys. After the bible exists, the bus layout is set up and any subsequent audio work (footstep system, dynamic music) reads from it.
247
+
248
+ ## See also
249
+
250
+ - `_shared/collaborative-protocol.md`
251
+ - `_shared/godot-version.md` — Godot 4.5 audio API notes
252
+ - `_shared/mcp-tools-reference.md`
253
+ - `scene-and-project/brainstorm-game/SKILL.md` — produces the brief that anchors the bible
254
+ - `rendering-and-lighting/art-direction/SKILL.md` — the visual counterpart; audio must rhyme with it
255
+ - `audio/audio-bus-setup/SKILL.md` (NEXT) — implements the bus layout in detail
256
+ - `audio/3d-spatial-audio/SKILL.md` (NEXT) — spatial attenuation patterns
257
+ - `audio/footstep-systems/SKILL.md` (NEXT) — material-aware footstep impl
258
+ - `audio/dynamic-music-stingers/SKILL.md` (LATER) — implements the dynamic music plan