@zigrivers/scaffold 3.4.1 → 3.5.1

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 (195) hide show
  1. package/README.md +91 -0
  2. package/content/knowledge/game/game-accessibility.md +328 -0
  3. package/content/knowledge/game/game-ai-patterns.md +567 -0
  4. package/content/knowledge/game/game-asset-pipeline.md +363 -0
  5. package/content/knowledge/game/game-audio-design.md +344 -0
  6. package/content/knowledge/game/game-binary-vcs-strategy.md +396 -0
  7. package/content/knowledge/game/game-design-document.md +269 -0
  8. package/content/knowledge/game/game-domain-patterns.md +299 -0
  9. package/content/knowledge/game/game-economy-design.md +355 -0
  10. package/content/knowledge/game/game-engine-selection.md +242 -0
  11. package/content/knowledge/game/game-input-systems.md +379 -0
  12. package/content/knowledge/game/game-level-content-design.md +483 -0
  13. package/content/knowledge/game/game-liveops-analytics.md +280 -0
  14. package/content/knowledge/game/game-localization.md +323 -0
  15. package/content/knowledge/game/game-milestone-definitions.md +337 -0
  16. package/content/knowledge/game/game-modding-ugc.md +390 -0
  17. package/content/knowledge/game/game-narrative-design.md +404 -0
  18. package/content/knowledge/game/game-networking.md +393 -0
  19. package/content/knowledge/game/game-performance-budgeting.md +389 -0
  20. package/content/knowledge/game/game-platform-certification.md +417 -0
  21. package/content/knowledge/game/game-project-structure.md +360 -0
  22. package/content/knowledge/game/game-save-systems.md +452 -0
  23. package/content/knowledge/game/game-testing-strategy.md +470 -0
  24. package/content/knowledge/game/game-ui-patterns.md +477 -0
  25. package/content/knowledge/game/game-vr-ar-design.md +313 -0
  26. package/content/knowledge/review/review-art-bible.md +305 -0
  27. package/content/knowledge/review/review-game-design.md +303 -0
  28. package/content/knowledge/review/review-game-economy.md +272 -0
  29. package/content/knowledge/review/review-game-ui.md +293 -0
  30. package/content/knowledge/review/review-netcode.md +280 -0
  31. package/content/knowledge/review/review-platform-cert.md +341 -0
  32. package/content/methodology/custom-defaults.yml +25 -0
  33. package/content/methodology/deep.yml +25 -0
  34. package/content/methodology/game-overlay.yml +145 -0
  35. package/content/methodology/mvp.yml +25 -0
  36. package/content/pipeline/architecture/ai-behavior-design.md +87 -0
  37. package/content/pipeline/architecture/netcode-spec.md +86 -0
  38. package/content/pipeline/architecture/review-netcode.md +78 -0
  39. package/content/pipeline/foundation/performance-budgets.md +91 -0
  40. package/content/pipeline/modeling/narrative-bible.md +84 -0
  41. package/content/pipeline/pre/game-design-document.md +90 -0
  42. package/content/pipeline/pre/review-gdd.md +74 -0
  43. package/content/pipeline/quality/analytics-telemetry.md +98 -0
  44. package/content/pipeline/quality/live-ops-plan.md +99 -0
  45. package/content/pipeline/quality/platform-cert-prep.md +129 -0
  46. package/content/pipeline/quality/playtest-plan.md +84 -0
  47. package/content/pipeline/specification/art-bible.md +87 -0
  48. package/content/pipeline/specification/audio-design.md +97 -0
  49. package/content/pipeline/specification/content-structure-design.md +142 -0
  50. package/content/pipeline/specification/economy-design.md +105 -0
  51. package/content/pipeline/specification/game-accessibility.md +82 -0
  52. package/content/pipeline/specification/game-ui-spec.md +97 -0
  53. package/content/pipeline/specification/input-controls-spec.md +81 -0
  54. package/content/pipeline/specification/localization-plan.md +113 -0
  55. package/content/pipeline/specification/modding-ugc-spec.md +116 -0
  56. package/content/pipeline/specification/online-services-spec.md +104 -0
  57. package/content/pipeline/specification/review-economy.md +87 -0
  58. package/content/pipeline/specification/review-game-ui.md +73 -0
  59. package/content/pipeline/specification/save-system-spec.md +116 -0
  60. package/dist/cli/commands/adopt.d.ts.map +1 -1
  61. package/dist/cli/commands/adopt.js +25 -0
  62. package/dist/cli/commands/adopt.js.map +1 -1
  63. package/dist/cli/commands/adopt.test.js +28 -1
  64. package/dist/cli/commands/adopt.test.js.map +1 -1
  65. package/dist/cli/commands/build.test.js +3 -0
  66. package/dist/cli/commands/build.test.js.map +1 -1
  67. package/dist/cli/commands/init.d.ts +1 -0
  68. package/dist/cli/commands/init.d.ts.map +1 -1
  69. package/dist/cli/commands/init.js +6 -0
  70. package/dist/cli/commands/init.js.map +1 -1
  71. package/dist/cli/commands/init.test.js +12 -1
  72. package/dist/cli/commands/init.test.js.map +1 -1
  73. package/dist/cli/commands/knowledge.test.js +8 -0
  74. package/dist/cli/commands/knowledge.test.js.map +1 -1
  75. package/dist/cli/commands/next.d.ts.map +1 -1
  76. package/dist/cli/commands/next.js +19 -5
  77. package/dist/cli/commands/next.js.map +1 -1
  78. package/dist/cli/commands/next.test.js +56 -0
  79. package/dist/cli/commands/next.test.js.map +1 -1
  80. package/dist/cli/commands/rework.d.ts.map +1 -1
  81. package/dist/cli/commands/rework.js +11 -2
  82. package/dist/cli/commands/rework.js.map +1 -1
  83. package/dist/cli/commands/rework.test.js +5 -0
  84. package/dist/cli/commands/rework.test.js.map +1 -1
  85. package/dist/cli/commands/run.d.ts.map +1 -1
  86. package/dist/cli/commands/run.js +54 -4
  87. package/dist/cli/commands/run.js.map +1 -1
  88. package/dist/cli/commands/run.test.js +384 -0
  89. package/dist/cli/commands/run.test.js.map +1 -1
  90. package/dist/cli/commands/skip.test.js +3 -0
  91. package/dist/cli/commands/skip.test.js.map +1 -1
  92. package/dist/cli/commands/status.d.ts.map +1 -1
  93. package/dist/cli/commands/status.js +16 -3
  94. package/dist/cli/commands/status.js.map +1 -1
  95. package/dist/cli/commands/status.test.js +55 -0
  96. package/dist/cli/commands/status.test.js.map +1 -1
  97. package/dist/cli/output/auto.d.ts +3 -0
  98. package/dist/cli/output/auto.d.ts.map +1 -1
  99. package/dist/cli/output/auto.js +9 -0
  100. package/dist/cli/output/auto.js.map +1 -1
  101. package/dist/cli/output/context.d.ts +6 -0
  102. package/dist/cli/output/context.d.ts.map +1 -1
  103. package/dist/cli/output/context.js.map +1 -1
  104. package/dist/cli/output/context.test.js +87 -0
  105. package/dist/cli/output/context.test.js.map +1 -1
  106. package/dist/cli/output/error-display.test.js +3 -0
  107. package/dist/cli/output/error-display.test.js.map +1 -1
  108. package/dist/cli/output/interactive.d.ts +3 -0
  109. package/dist/cli/output/interactive.d.ts.map +1 -1
  110. package/dist/cli/output/interactive.js +76 -0
  111. package/dist/cli/output/interactive.js.map +1 -1
  112. package/dist/cli/output/json.d.ts +3 -0
  113. package/dist/cli/output/json.d.ts.map +1 -1
  114. package/dist/cli/output/json.js +9 -0
  115. package/dist/cli/output/json.js.map +1 -1
  116. package/dist/config/loader.d.ts.map +1 -1
  117. package/dist/config/loader.js +3 -2
  118. package/dist/config/loader.js.map +1 -1
  119. package/dist/config/schema.d.ts +641 -15
  120. package/dist/config/schema.d.ts.map +1 -1
  121. package/dist/config/schema.js +26 -1
  122. package/dist/config/schema.js.map +1 -1
  123. package/dist/config/schema.test.js +192 -1
  124. package/dist/config/schema.test.js.map +1 -1
  125. package/dist/core/assembly/overlay-loader.d.ts +24 -0
  126. package/dist/core/assembly/overlay-loader.d.ts.map +1 -0
  127. package/dist/core/assembly/overlay-loader.js +190 -0
  128. package/dist/core/assembly/overlay-loader.js.map +1 -0
  129. package/dist/core/assembly/overlay-loader.test.d.ts +2 -0
  130. package/dist/core/assembly/overlay-loader.test.d.ts.map +1 -0
  131. package/dist/core/assembly/overlay-loader.test.js +106 -0
  132. package/dist/core/assembly/overlay-loader.test.js.map +1 -0
  133. package/dist/core/assembly/overlay-resolver.d.ts +15 -0
  134. package/dist/core/assembly/overlay-resolver.d.ts.map +1 -0
  135. package/dist/core/assembly/overlay-resolver.js +58 -0
  136. package/dist/core/assembly/overlay-resolver.js.map +1 -0
  137. package/dist/core/assembly/overlay-resolver.test.d.ts +2 -0
  138. package/dist/core/assembly/overlay-resolver.test.d.ts.map +1 -0
  139. package/dist/core/assembly/overlay-resolver.test.js +246 -0
  140. package/dist/core/assembly/overlay-resolver.test.js.map +1 -0
  141. package/dist/core/assembly/overlay-state-resolver.d.ts +26 -0
  142. package/dist/core/assembly/overlay-state-resolver.d.ts.map +1 -0
  143. package/dist/core/assembly/overlay-state-resolver.js +63 -0
  144. package/dist/core/assembly/overlay-state-resolver.js.map +1 -0
  145. package/dist/core/assembly/overlay-state-resolver.test.d.ts +2 -0
  146. package/dist/core/assembly/overlay-state-resolver.test.d.ts.map +1 -0
  147. package/dist/core/assembly/overlay-state-resolver.test.js +256 -0
  148. package/dist/core/assembly/overlay-state-resolver.test.js.map +1 -0
  149. package/dist/core/assembly/preset-loader.d.ts +1 -0
  150. package/dist/core/assembly/preset-loader.d.ts.map +1 -1
  151. package/dist/core/assembly/preset-loader.js +2 -0
  152. package/dist/core/assembly/preset-loader.js.map +1 -1
  153. package/dist/core/dependency/eligibility.test.js +3 -0
  154. package/dist/core/dependency/eligibility.test.js.map +1 -1
  155. package/dist/e2e/game-pipeline.test.d.ts +10 -0
  156. package/dist/e2e/game-pipeline.test.d.ts.map +1 -0
  157. package/dist/e2e/game-pipeline.test.js +298 -0
  158. package/dist/e2e/game-pipeline.test.js.map +1 -0
  159. package/dist/e2e/init.test.js +3 -0
  160. package/dist/e2e/init.test.js.map +1 -1
  161. package/dist/project/adopt.d.ts +3 -1
  162. package/dist/project/adopt.d.ts.map +1 -1
  163. package/dist/project/adopt.js +29 -1
  164. package/dist/project/adopt.js.map +1 -1
  165. package/dist/project/adopt.test.js +51 -1
  166. package/dist/project/adopt.test.js.map +1 -1
  167. package/dist/types/config.d.ts +50 -4
  168. package/dist/types/config.d.ts.map +1 -1
  169. package/dist/types/config.test.d.ts +2 -0
  170. package/dist/types/config.test.d.ts.map +1 -0
  171. package/dist/types/config.test.js +97 -0
  172. package/dist/types/config.test.js.map +1 -0
  173. package/dist/utils/eligible.d.ts +3 -2
  174. package/dist/utils/eligible.d.ts.map +1 -1
  175. package/dist/utils/eligible.js +18 -4
  176. package/dist/utils/eligible.js.map +1 -1
  177. package/dist/utils/errors.d.ts +4 -0
  178. package/dist/utils/errors.d.ts.map +1 -1
  179. package/dist/utils/errors.js +31 -0
  180. package/dist/utils/errors.js.map +1 -1
  181. package/dist/utils/errors.test.js +4 -1
  182. package/dist/utils/errors.test.js.map +1 -1
  183. package/dist/wizard/questions.d.ts +4 -0
  184. package/dist/wizard/questions.d.ts.map +1 -1
  185. package/dist/wizard/questions.js +59 -1
  186. package/dist/wizard/questions.js.map +1 -1
  187. package/dist/wizard/questions.test.js +178 -4
  188. package/dist/wizard/questions.test.js.map +1 -1
  189. package/dist/wizard/wizard.d.ts +1 -0
  190. package/dist/wizard/wizard.d.ts.map +1 -1
  191. package/dist/wizard/wizard.js +4 -1
  192. package/dist/wizard/wizard.js.map +1 -1
  193. package/dist/wizard/wizard.test.js +102 -4
  194. package/dist/wizard/wizard.test.js.map +1 -1
  195. package/package.json +1 -1
@@ -0,0 +1,567 @@
1
+ ---
2
+ name: game-ai-patterns
3
+ description: Behavior trees, GOAP, utility AI, finite state machines, NavMesh pathfinding, perception systems, and companion AI
4
+ topics: [game-dev, ai, behavior-trees, goap, pathfinding, npc]
5
+ ---
6
+
7
+ Game AI encompasses the systems that control non-player character behavior — from enemy combat tactics to companion pathfinding to ambient NPC routines. Unlike machine learning AI, game AI is deterministic and designed: every behavior is authored by a designer and executed by a runtime system. The goal is not intelligence but the appearance of intelligence — NPCs should behave in ways that feel believable, create interesting gameplay challenges, and respond to the player's actions in readable ways. The core trade-off in game AI is expressiveness vs. complexity: more sophisticated AI systems enable richer behavior but are harder to design, debug, and performance-tune.
8
+
9
+ ## Summary
10
+
11
+ ### AI Architecture Spectrum
12
+
13
+ Game AI systems exist on a spectrum from simple to complex:
14
+
15
+ - **Finite State Machines (FSM)**: States with explicit transitions. Simple, predictable, easy to debug. Falls apart when state count grows large. Best for simple enemies, UI, and game state management.
16
+ - **Behavior Trees (BT)**: Hierarchical task decomposition. Moderate complexity, highly readable, industry standard for action game AI. Used in Halo, Unreal Engine's default AI, most AAA combat AI.
17
+ - **Goal-Oriented Action Planning (GOAP)**: Agents define goals, planner finds action sequences to achieve them. More autonomous, harder to control. Used in F.E.A.R., Tomb Raider (2013 reboot).
18
+ - **Utility AI**: Score every possible action and pick the highest-scoring one. Extremely flexible, non-linear priority. Used in The Sims, Infinite Axis Utility System. Harder to predict and debug.
19
+
20
+ Each system has a sweet spot. Do not use GOAP for a platformer enemy that runs left and right. Do not use an FSM for an open-world companion that must react to hundreds of situations.
21
+
22
+ ### NavMesh Pathfinding
23
+
24
+ Navigation meshes (NavMesh) are the standard solution for pathfinding in 3D games. A NavMesh is a simplified polygon mesh covering walkable surfaces. Agents find paths on this mesh using A* or similar algorithms. The mesh is typically baked offline by the engine (Unity, Unreal, and Godot all provide NavMesh baking tools) and modified at runtime for dynamic obstacles.
25
+
26
+ ### Perception Systems
27
+
28
+ Perception systems model what an NPC can see, hear, and remember. Without a perception system, AI has perfect information — it knows where the player is at all times, which feels unfair and breaks stealth gameplay. Perception adds sight cones, hearing ranges, memory decay, and investigation behavior.
29
+
30
+ ### Encounter Design and Difficulty Scaling
31
+
32
+ AI behavior must be tunable per encounter. A trash mob should not fight like a boss. Difficulty scaling adjusts AI parameters (reaction time, accuracy, aggression) rather than just health/damage numbers.
33
+
34
+ ## Deep Guidance
35
+
36
+ ### Finite State Machines for Game AI
37
+
38
+ FSMs are the simplest AI architecture. An NPC is in one state at a time and transitions to another state when conditions are met.
39
+
40
+ ```typescript
41
+ // Finite State Machine for a guard NPC
42
+
43
+ type GuardState = "patrol" | "alert" | "chase" | "attack" | "search" | "return";
44
+
45
+ interface StateTransition {
46
+ from: GuardState;
47
+ to: GuardState;
48
+ condition: (npc: GuardNPC) => boolean;
49
+ priority: number; // Higher priority transitions are checked first
50
+ }
51
+
52
+ interface GuardNPC {
53
+ currentState: GuardState;
54
+ position: Vector3;
55
+ patrolRoute: Vector3[];
56
+ patrolIndex: number;
57
+ lastKnownPlayerPos: Vector3 | null;
58
+ alertLevel: number; // 0-100
59
+ searchTimer: number;
60
+ attackRange: number;
61
+ sightRange: number;
62
+ hearingRange: number;
63
+ }
64
+
65
+ const guardTransitions: StateTransition[] = [
66
+ // Chase → Attack (highest priority when in range)
67
+ {
68
+ from: "chase",
69
+ to: "attack",
70
+ condition: (npc) => distToPlayer(npc) < npc.attackRange,
71
+ priority: 10,
72
+ },
73
+ // Any state → Chase (player spotted)
74
+ {
75
+ from: "patrol",
76
+ to: "chase",
77
+ condition: (npc) => canSeePlayer(npc) && distToPlayer(npc) < npc.sightRange,
78
+ priority: 9,
79
+ },
80
+ {
81
+ from: "alert",
82
+ to: "chase",
83
+ condition: (npc) => canSeePlayer(npc),
84
+ priority: 9,
85
+ },
86
+ {
87
+ from: "search",
88
+ to: "chase",
89
+ condition: (npc) => canSeePlayer(npc),
90
+ priority: 9,
91
+ },
92
+ // Patrol → Alert (heard something)
93
+ {
94
+ from: "patrol",
95
+ to: "alert",
96
+ condition: (npc) => canHearPlayer(npc) && npc.alertLevel > 30,
97
+ priority: 5,
98
+ },
99
+ // Chase → Search (lost sight)
100
+ {
101
+ from: "chase",
102
+ to: "search",
103
+ condition: (npc) => !canSeePlayer(npc),
104
+ priority: 4,
105
+ },
106
+ // Attack → Chase (player left range)
107
+ {
108
+ from: "attack",
109
+ to: "chase",
110
+ condition: (npc) => distToPlayer(npc) > npc.attackRange * 1.5,
111
+ priority: 4,
112
+ },
113
+ // Search → Return (search timer expired)
114
+ {
115
+ from: "search",
116
+ to: "return",
117
+ condition: (npc) => npc.searchTimer <= 0,
118
+ priority: 3,
119
+ },
120
+ // Return → Patrol (back at patrol route)
121
+ {
122
+ from: "return",
123
+ to: "patrol",
124
+ condition: (npc) => distToPatrolRoute(npc) < 2.0,
125
+ priority: 2,
126
+ },
127
+ ];
128
+
129
+ function updateGuardFSM(npc: GuardNPC, dt: number): void {
130
+ // Check transitions sorted by priority
131
+ const applicable = guardTransitions
132
+ .filter(t => t.from === npc.currentState)
133
+ .sort((a, b) => b.priority - a.priority);
134
+
135
+ for (const transition of applicable) {
136
+ if (transition.condition(npc)) {
137
+ exitState(npc, npc.currentState);
138
+ npc.currentState = transition.to;
139
+ enterState(npc, npc.currentState);
140
+ break;
141
+ }
142
+ }
143
+
144
+ // Execute current state behavior
145
+ updateState(npc, npc.currentState, dt);
146
+ }
147
+
148
+ function enterState(npc: GuardNPC, state: GuardState): void {
149
+ switch (state) {
150
+ case "alert":
151
+ npc.alertLevel = 50;
152
+ playAnimation(npc, "alert_idle");
153
+ break;
154
+ case "chase":
155
+ playAnimation(npc, "run");
156
+ alertNearbyGuards(npc); // Call for backup
157
+ break;
158
+ case "search":
159
+ npc.searchTimer = 10.0; // Search for 10 seconds
160
+ playAnimation(npc, "search_look_around");
161
+ break;
162
+ case "attack":
163
+ playAnimation(npc, "attack_ready");
164
+ break;
165
+ case "return":
166
+ playAnimation(npc, "walk");
167
+ break;
168
+ case "patrol":
169
+ playAnimation(npc, "walk");
170
+ break;
171
+ }
172
+ }
173
+
174
+ function exitState(npc: GuardNPC, _state: GuardState): void {
175
+ // Clean up state-specific resources
176
+ }
177
+
178
+ function updateState(npc: GuardNPC, state: GuardState, dt: number): void {
179
+ switch (state) {
180
+ case "patrol":
181
+ moveToward(npc, npc.patrolRoute[npc.patrolIndex], 3.0);
182
+ if (distTo(npc.position, npc.patrolRoute[npc.patrolIndex]) < 1.0) {
183
+ npc.patrolIndex = (npc.patrolIndex + 1) % npc.patrolRoute.length;
184
+ }
185
+ break;
186
+ case "chase":
187
+ moveToward(npc, getPlayerPosition(), 6.0);
188
+ npc.lastKnownPlayerPos = getPlayerPosition();
189
+ break;
190
+ case "search":
191
+ npc.searchTimer -= dt;
192
+ moveToward(npc, npc.lastKnownPlayerPos!, 3.0);
193
+ break;
194
+ case "attack":
195
+ facePlayer(npc);
196
+ performAttack(npc);
197
+ break;
198
+ case "return":
199
+ moveToward(npc, npc.patrolRoute[npc.patrolIndex], 3.0);
200
+ break;
201
+ }
202
+ }
203
+
204
+ // Helper stubs
205
+ interface Vector3 { x: number; y: number; z: number; }
206
+ function distToPlayer(_npc: GuardNPC): number { return 0; }
207
+ function canSeePlayer(_npc: GuardNPC): boolean { return false; }
208
+ function canHearPlayer(_npc: GuardNPC): boolean { return false; }
209
+ function distToPatrolRoute(_npc: GuardNPC): number { return 0; }
210
+ function distTo(_a: Vector3, _b: Vector3): number { return 0; }
211
+ function moveToward(_npc: GuardNPC, _target: Vector3, _speed: number): void {}
212
+ function getPlayerPosition(): Vector3 { return { x: 0, y: 0, z: 0 }; }
213
+ function facePlayer(_npc: GuardNPC): void {}
214
+ function performAttack(_npc: GuardNPC): void {}
215
+ function playAnimation(_npc: GuardNPC, _anim: string): void {}
216
+ function alertNearbyGuards(_npc: GuardNPC): void {}
217
+ ```
218
+
219
+ **FSM limitations:**
220
+ - State explosion: N states can have N*(N-1) transitions. Adding "crouching" variants of every state doubles the state count.
221
+ - No concurrency: An FSM can only be in one state. An NPC that needs to patrol AND talk requires either a hierarchical FSM or a different architecture.
222
+ - Rigid behavior: FSM NPCs feel mechanical because transitions are binary — they switch abruptly from one behavior to another.
223
+
224
+ ### Behavior Trees
225
+
226
+ Behavior trees decompose AI behavior into a hierarchy of tasks. The tree is evaluated top-to-bottom, left-to-right every tick. Each node returns Success, Failure, or Running.
227
+
228
+ **Node types:**
229
+ - **Sequence**: Runs children left-to-right. Fails if any child fails. Succeeds when all children succeed. (AND logic)
230
+ - **Selector/Fallback**: Runs children left-to-right. Succeeds if any child succeeds. Fails when all children fail. (OR logic)
231
+ - **Decorator**: Wraps a single child and modifies its behavior (Inverter, Repeater, Timeout, Cooldown)
232
+ - **Leaf/Action**: Performs an actual game action (MoveTo, Attack, PlayAnimation, Wait)
233
+ - **Condition**: Checks a predicate (CanSeePlayer, IsHealthLow, HasAmmo)
234
+
235
+ ```yaml
236
+ # Behavior tree for a combat enemy (YAML representation)
237
+ # Read top-to-bottom: Selector tries each branch until one succeeds
238
+
239
+ root:
240
+ type: Selector
241
+ children:
242
+ # Branch 1: Critical health — flee
243
+ - type: Sequence
244
+ children:
245
+ - type: Condition
246
+ check: "health_below"
247
+ threshold: 20
248
+ - type: Action
249
+ name: "flee_to_cover"
250
+ - type: Action
251
+ name: "call_for_help"
252
+
253
+ # Branch 2: In combat range — fight
254
+ - type: Sequence
255
+ children:
256
+ - type: Condition
257
+ check: "can_see_player"
258
+ - type: Selector
259
+ children:
260
+ # Sub-branch 2a: Use grenade if available and player clustered
261
+ - type: Sequence
262
+ children:
263
+ - type: Condition
264
+ check: "has_grenade"
265
+ - type: Condition
266
+ check: "player_near_allies"
267
+ radius: 5.0
268
+ - type: Decorator
269
+ kind: Cooldown
270
+ duration: 8.0
271
+ child:
272
+ type: Action
273
+ name: "throw_grenade"
274
+
275
+ # Sub-branch 2b: Ranged attack from cover
276
+ - type: Sequence
277
+ children:
278
+ - type: Condition
279
+ check: "in_cover"
280
+ - type: Action
281
+ name: "peek_and_shoot"
282
+ - type: Action
283
+ name: "duck_into_cover"
284
+
285
+ # Sub-branch 2c: Close range — melee
286
+ - type: Sequence
287
+ children:
288
+ - type: Condition
289
+ check: "player_within"
290
+ range: 3.0
291
+ - type: Action
292
+ name: "melee_attack"
293
+
294
+ # Sub-branch 2d: Advance to cover
295
+ - type: Action
296
+ name: "move_to_nearest_cover"
297
+
298
+ # Branch 3: Alert — investigate
299
+ - type: Sequence
300
+ children:
301
+ - type: Condition
302
+ check: "alert_level_above"
303
+ threshold: 30
304
+ - type: Action
305
+ name: "investigate_last_known_position"
306
+
307
+ # Branch 4: Default — patrol
308
+ - type: Action
309
+ name: "patrol"
310
+ ```
311
+
312
+ **Why behavior trees dominate game AI:**
313
+ - Readable by designers (visual node graphs in most engines)
314
+ - Modular — subtrees can be reused across NPC types
315
+ - Priority is implicit in left-to-right ordering (no explicit priority values)
316
+ - Running state allows long-running actions (MoveTo that takes many frames)
317
+ - Decorators handle cooldowns, retries, and timeouts cleanly
318
+
319
+ ### Goal-Oriented Action Planning (GOAP)
320
+
321
+ GOAP inverts the control flow: instead of authoring behavior directly, you define actions with preconditions and effects, then a planner finds sequences of actions that satisfy a goal.
322
+
323
+ **GOAP components:**
324
+ - **Goals**: Desired world states (e.g., "player is dead", "at full health", "has ammo")
325
+ - **Actions**: Things the agent can do, with preconditions and effects
326
+ - Attack: precondition {can_see_player, has_ammo}, effect {player_damaged}
327
+ - Reload: precondition {ammo_in_pocket}, effect {has_ammo}
328
+ - FindAmmo: precondition {knows_ammo_location}, effect {ammo_in_pocket}
329
+ - Heal: precondition {has_medkit, health_below_50}, effect {health_above_50}
330
+ - FlankPlayer: precondition {can_see_player, has_cover_nearby}, effect {in_flanking_position}
331
+ - **Planner**: A* search through action space to find a plan (sequence of actions) that transforms the current world state into the goal state
332
+
333
+ GOAP produces emergent behavior: NPCs chain actions in ways the designer did not explicitly author. A GOAP enemy might retreat to find ammo, then flank the player, then attack — a sequence that was never hand-coded but emerged from action preconditions and effects.
334
+
335
+ **GOAP trade-offs:**
336
+ - Emergent behavior can be surprising (good for players, challenging for designers)
337
+ - Harder to debug than behavior trees (why did the planner choose that plan?)
338
+ - Planning has CPU cost — cache plans and only re-plan when world state changes
339
+ - Requires careful action design — badly defined preconditions lead to degenerate plans
340
+
341
+ ### Utility AI
342
+
343
+ Utility AI scores every available action on a 0-1 scale using response curves, then picks the highest-scoring action. This creates smooth, non-binary decision-making.
344
+
345
+ **Response curves:**
346
+ - **Linear**: Score increases linearly with input (hunger score rises linearly as food decreases)
347
+ - **Quadratic**: Score increases slowly at first, then rapidly (urgency builds exponentially)
348
+ - **Logistic**: S-curve — slow start, rapid middle, saturates at high values (most natural for biological needs)
349
+ - **Inverse**: Score decreases as input increases (interest in exploring decreases near already-explored areas)
350
+
351
+ Each action has multiple input axes, each with its own response curve. The final score is the product (or weighted average) of all axes.
352
+
353
+ **Example:** A Sims-style NPC choosing between Eat, Sleep, Socialize, and Work:
354
+ - Eat score = hunger_curve(hunger_level) * food_available_curve(nearby_food)
355
+ - Sleep score = tired_curve(energy_level) * bed_available_curve(bed_distance) * time_curve(hour_of_day)
356
+ - Socialize score = lonely_curve(social_need) * person_nearby_curve(nearest_npc) * relationship_curve(relationship_quality)
357
+ - Work score = duty_curve(work_urgency) * energy_curve(energy_level) * time_curve(hour_of_day)
358
+
359
+ The NPC picks whichever scores highest. This creates naturalistic behavior: an NPC does not abruptly switch from "working" to "eating" at exactly 50% hunger. Instead, the eat score gradually rises and eventually overtakes work.
360
+
361
+ ### NavMesh Configuration
362
+
363
+ ```typescript
364
+ // NavMesh configuration for different agent types
365
+
366
+ interface NavMeshConfig {
367
+ agentRadius: number; // Agent collision radius in world units
368
+ agentHeight: number; // Agent height for clearance checks
369
+ maxSlope: number; // Maximum walkable slope in degrees
370
+ stepHeight: number; // Maximum step-up height (stairs, curbs)
371
+ dropHeight: number; // Maximum drop-down height before needing a jump
372
+ jumpDistance: number; // Maximum horizontal gap traversable
373
+ cellSize: number; // NavMesh voxel resolution (smaller = more precise, slower to bake)
374
+ cellHeight: number; // Vertical voxel resolution
375
+ }
376
+
377
+ // Different agent types need different NavMesh layers
378
+ const agentConfigs: Record<string, NavMeshConfig> = {
379
+ humanoid: {
380
+ agentRadius: 0.4,
381
+ agentHeight: 1.8,
382
+ maxSlope: 45,
383
+ stepHeight: 0.4,
384
+ dropHeight: 2.5,
385
+ jumpDistance: 0, // Humans don't auto-jump on NavMesh
386
+ cellSize: 0.15,
387
+ cellHeight: 0.2,
388
+ },
389
+ large_creature: {
390
+ agentRadius: 1.5,
391
+ agentHeight: 3.0,
392
+ maxSlope: 35,
393
+ stepHeight: 0.8,
394
+ dropHeight: 4.0,
395
+ jumpDistance: 0,
396
+ cellSize: 0.3, // Coarser resolution — large agents don't need tight spaces
397
+ cellHeight: 0.3,
398
+ },
399
+ flying: {
400
+ // Flying agents often skip NavMesh entirely and use 3D pathfinding
401
+ // or simple steering behaviors in open airspace
402
+ agentRadius: 0.5,
403
+ agentHeight: 0.5,
404
+ maxSlope: 90, // Can fly over anything
405
+ stepHeight: 100,
406
+ dropHeight: 100,
407
+ jumpDistance: 100,
408
+ cellSize: 0.5,
409
+ cellHeight: 0.5,
410
+ },
411
+ };
412
+
413
+ // NavMesh areas for cost-based pathfinding
414
+ // Agents prefer lower-cost areas, enabling tactical routing
415
+ const navMeshAreas = {
416
+ walkable: { cost: 1.0 }, // Default terrain
417
+ road: { cost: 0.5 }, // Prefer roads (faster movement)
418
+ mud: { cost: 3.0 }, // Avoid mud (slower movement)
419
+ dangerous: { cost: 10.0 }, // Heavily penalize dangerous zones
420
+ water_shallow: { cost: 2.0 }, // Passable but slow
421
+ water_deep: { cost: Infinity }, // Impassable for non-swimming agents
422
+ };
423
+
424
+ // Dynamic obstacle handling
425
+ // When obstacles spawn/move at runtime, NavMesh needs updating
426
+ interface DynamicObstacle {
427
+ shape: "box" | "cylinder";
428
+ size: Vector3;
429
+ carveNavMesh: boolean; // true = cut a hole in NavMesh; false = use avoidance only
430
+ // Carving is expensive — use only for large, infrequent changes (bridges destroyed, doors closed)
431
+ // For small/frequent obstacles (other NPCs, vehicles), use avoidance steering instead
432
+ }
433
+ ```
434
+
435
+ ### Perception Systems
436
+
437
+ **Sight:**
438
+ - Defined by a cone: direction, angle (field of view, typically 90-120 degrees), and range
439
+ - Blocked by geometry (raycasts from NPC eyes to target)
440
+ - Detection is not instant: awareness builds over time based on distance, movement, and lighting
441
+ - Peripheral vision has longer detection time than center vision
442
+
443
+ **Hearing:**
444
+ - Defined by a radius around the NPC
445
+ - Sounds have volume that attenuates with distance (inverse square or simpler linear falloff)
446
+ - Different actions have different noise levels: walking (quiet), running (medium), gunfire (loud), explosion (very loud)
447
+ - Blocked partially by walls (sound travels around corners but is attenuated)
448
+
449
+ **Memory and investigation:**
450
+ - When perception is lost (target leaves sight/hearing range), the NPC remembers the last known position
451
+ - Memory decays over time — an NPC who lost sight of the player 30 seconds ago should not walk directly to the player's current position
452
+ - Investigation behavior: move to last known position, search nearby areas, then give up
453
+
454
+ **Aggro radius:**
455
+ - Enemies within aggro radius enter combat when the player approaches
456
+ - Aggro radius should be tuned per encounter: open fields use larger radius, corridors use shorter
457
+ - Social aggro: nearby allies within a radius also aggro when one NPC engages
458
+
459
+ ### Encounter Scripting
460
+
461
+ Encounters are hand-designed combat or challenge scenarios. Effective encounter scripting layers authored triggers on top of AI systems.
462
+
463
+ **Trigger types:**
464
+ - **Proximity triggers**: Player enters a zone; enemies spawn or activate
465
+ - **Event triggers**: Player picks up an item or interacts with an object; combat begins
466
+ - **Kill triggers**: A certain number of enemies killed triggers the next wave
467
+ - **Timer triggers**: After N seconds, reinforcements arrive
468
+ - **Health triggers**: Boss reaches 50% health; phase 2 begins
469
+
470
+ ### Difficulty Scaling Through AI Parameters
471
+
472
+ Rather than scaling only health and damage (the "bullet sponge" approach), scale AI behavior parameters:
473
+
474
+ ```yaml
475
+ # AI difficulty scaling — tune behavior, not just numbers
476
+ difficulty_scaling:
477
+ easy:
478
+ reaction_time_ms: 800 # Slow to react to player
479
+ accuracy_base: 0.3 # 30% shots hit at medium range
480
+ accuracy_moving: 0.1 # Very inaccurate while moving
481
+ aggression: 0.3 # Rarely pushes forward
482
+ flank_probability: 0.1 # Almost never flanks
483
+ grenade_frequency: 0.05 # Rarely uses grenades
484
+ cover_seek_priority: 0.9 # Strongly prefers cover
485
+ group_coordination: false # Enemies act independently
486
+ perception_range_mult: 0.7 # Shorter sight/hearing range
487
+ aim_sway_degrees: 15 # Very inaccurate aim
488
+
489
+ normal:
490
+ reaction_time_ms: 400
491
+ accuracy_base: 0.5
492
+ accuracy_moving: 0.25
493
+ aggression: 0.5
494
+ flank_probability: 0.3
495
+ grenade_frequency: 0.15
496
+ cover_seek_priority: 0.7
497
+ group_coordination: true # Enemies coordinate
498
+ perception_range_mult: 1.0
499
+ aim_sway_degrees: 8
500
+
501
+ hard:
502
+ reaction_time_ms: 200
503
+ accuracy_base: 0.7
504
+ accuracy_moving: 0.4
505
+ aggression: 0.7
506
+ flank_probability: 0.6 # Frequently flanks
507
+ grenade_frequency: 0.3 # Regular grenades
508
+ cover_seek_priority: 0.5 # Willing to trade cover for aggression
509
+ group_coordination: true
510
+ perception_range_mult: 1.3 # Extended awareness
511
+ aim_sway_degrees: 4
512
+
513
+ # Dynamic difficulty: adjust parameters based on player performance
514
+ dynamic:
515
+ metric: "deaths_per_encounter"
516
+ # If player dies more than 2x per encounter, ease toward easy
517
+ # If player takes less than 10% damage per encounter, push toward hard
518
+ adjustment_rate: 0.1 # Blend 10% toward target per encounter
519
+ floor: "easy"
520
+ ceiling: "hard"
521
+ ```
522
+
523
+ ### Companion AI Patterns
524
+
525
+ Companion AI has unique requirements compared to enemy AI. The companion must help without stealing the player's agency, stay out of the player's way, and feel like a character rather than a utility.
526
+
527
+ **Core companion behaviors:**
528
+ - **Following**: Stay near the player without blocking movement. Use path offset (slightly behind and to the side). Teleport to player if distance exceeds a threshold (prevents companions getting stuck).
529
+ - **Combat assist**: Attack enemies the player is fighting, but deal less damage than the player. Do not kill enemies the player is clearly targeting (steal kills feel bad). Prioritize enemies the player is not focused on.
530
+ - **Callouts**: Bark when they see an enemy, find a resource, or notice a point of interest. Barks should be informative ("Enemy on the roof!") not annoying (no constant chatter).
531
+ - **Navigation**: Companions must never block doorways, narrow passages, or line of fire. Push aside if the player bumps into them. Pathfind independently but prefer the player's path.
532
+ - **Invulnerability decision**: Many games make companions invulnerable (or very durable) because the player cannot control companion positioning. A companion that dies constantly frustrates players. If the companion can die, provide a revive mechanic.
533
+
534
+ ### AI Debugging
535
+
536
+ AI bugs are hard to reproduce because they depend on runtime state. Build debugging tools early:
537
+
538
+ - **Visual debug drawing**: Sight cones, hearing radii, NavMesh paths, current behavior tree node, current state
539
+ - **AI log**: Per-NPC decision log showing what was evaluated, what scored highest, what was chosen, and why alternatives were rejected
540
+ - **Freeze and inspect**: Ability to pause the game and inspect any NPC's full AI state (current BT node, GOAP plan, utility scores, perception targets)
541
+ - **Record and replay**: Capture AI inputs (world state each frame) and replay to reproduce bugs deterministically
542
+ - **Slow motion**: Run the game at 0.25x speed to observe AI decision-making in real time
543
+
544
+ ## Genre-Specific AI Patterns
545
+
546
+ ### Strategy Game AI
547
+
548
+ Strategy AI operates on longer time horizons than action AI. Key patterns:
549
+
550
+ - **Influence Maps**: 2D grid overlays tracking resource density, threat level, territory control. Update per turn or every N seconds. Used for build placement decisions, army movement, and resource prioritization.
551
+ - **Build Order Planning**: Decision trees or scripted sequences for early-game economy. At higher difficulty, use Monte Carlo Tree Search (MCTS) to evaluate build paths 10-20 turns ahead.
552
+ - **Opponent Modeling**: Track player tendencies (aggressive/defensive/economic) and adapt strategy. Simple approach: weighted counter-strategy table. Advanced: maintain belief state of opponent's hidden information.
553
+
554
+ ### Turn-Based AI
555
+
556
+ - **Minimax with Alpha-Beta Pruning**: Standard for two-player zero-sum games (chess, checkers). Search depth 4-8 plies for real-time-constrained turns. Evaluation function must be fast (<1ms per position).
557
+ - **Monte Carlo Tree Search (MCTS)**: Preferred for games with large branching factors (Go, complex card games). Run 1,000-10,000 simulations per decision within the time budget. UCB1 exploration constant: typically 1.0-1.4.
558
+
559
+ ### Racing AI
560
+
561
+ - **Racing Line Following**: Precompute optimal racing line as spline control points. AI follows line with rubber-banding: if too far ahead, reduce throttle by 5-15%; if behind, increase by 10-20%. Expose difficulty parameter controlling how closely AI follows the optimal line.
562
+ - **Overtaking Decision**: Distance-to-corner, speed differential, and track width determine whether to attempt pass. Use simple cost function: `overtake_score = speed_advantage * track_width / distance_to_corner`.
563
+
564
+ ### Simulation/Management AI
565
+
566
+ - **Need-Based Scheduling**: NPCs maintain need queues (hunger, rest, social). Highest-urgency need drives behavior selection. Satisfaction decay rates define personality: fast hunger decay = always eating. Based on Sims-style utility curves.
567
+ - **Agent Scheduling**: For city-builder NPCs: pathfind to workplace at shift start, return home at shift end. Use job queue with priority: emergency > assigned work > idle tasks. Budget 0.5-1ms per agent per tick for 100+ simultaneous agents.