@aura3d/engine 1.0.2 → 1.0.5

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 (296) hide show
  1. package/README.md +331 -16
  2. package/dist/animation/AnimationClipEvents.d.ts +57 -0
  3. package/dist/animation/AnimationClipEvents.d.ts.map +1 -0
  4. package/dist/animation/AnimationClipEvents.js +171 -0
  5. package/dist/animation/AnimationClipEvents.js.map +1 -0
  6. package/dist/animation/AnimationClipRegistry.d.ts +76 -0
  7. package/dist/animation/AnimationClipRegistry.d.ts.map +1 -0
  8. package/dist/animation/AnimationClipRegistry.js +130 -0
  9. package/dist/animation/AnimationClipRegistry.js.map +1 -0
  10. package/dist/animation/AnimationController.d.ts +168 -0
  11. package/dist/animation/AnimationController.d.ts.map +1 -0
  12. package/dist/animation/AnimationController.js +619 -0
  13. package/dist/animation/AnimationController.js.map +1 -0
  14. package/dist/animation/HumanoidRetargeting.d.ts +76 -0
  15. package/dist/animation/HumanoidRetargeting.d.ts.map +1 -0
  16. package/dist/animation/HumanoidRetargeting.js +331 -0
  17. package/dist/animation/HumanoidRetargeting.js.map +1 -0
  18. package/dist/animation/browser-index.d.ts +18 -0
  19. package/dist/animation/browser-index.d.ts.map +1 -1
  20. package/dist/animation/browser-index.js +13 -0
  21. package/dist/animation/browser-index.js.map +1 -1
  22. package/dist/animation/index.d.ts +16 -1
  23. package/dist/animation/index.d.ts.map +1 -1
  24. package/dist/animation/index.js +11 -1
  25. package/dist/animation/index.js.map +1 -1
  26. package/dist/animation/threejs-compatibility/AnimationDiagnostics.d.ts.map +1 -1
  27. package/dist/animation/threejs-compatibility/AnimationDiagnostics.js +3 -5
  28. package/dist/animation/threejs-compatibility/AnimationDiagnostics.js.map +1 -1
  29. package/dist/assets/GLTFAnimationRuntime.js +1 -1
  30. package/dist/assets/GLTFLoader.js +1 -1
  31. package/dist/aura3d-cli/cli.js +194 -8
  32. package/dist/aura3d-cli/cli.js.map +1 -1
  33. package/dist/aura3d-cli/index.d.ts +280 -3
  34. package/dist/aura3d-cli/index.d.ts.map +1 -1
  35. package/dist/aura3d-cli/index.js +886 -4
  36. package/dist/aura3d-cli/index.js.map +1 -1
  37. package/dist/aura3d-cli/pull-bridge.d.ts +95 -0
  38. package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -0
  39. package/dist/aura3d-cli/pull-bridge.js +247 -0
  40. package/dist/aura3d-cli/pull-bridge.js.map +1 -0
  41. package/dist/create-aura3d/index.d.ts +1 -1
  42. package/dist/create-aura3d/index.d.ts.map +1 -1
  43. package/dist/create-aura3d/index.js +9 -2
  44. package/dist/create-aura3d/index.js.map +1 -1
  45. package/dist/editor-runtime/ProjectSerializer.d.ts +74 -1
  46. package/dist/editor-runtime/ProjectSerializer.d.ts.map +1 -1
  47. package/dist/editor-runtime/ProjectSerializer.js +123 -6
  48. package/dist/editor-runtime/ProjectSerializer.js.map +1 -1
  49. package/dist/editor-runtime/TimelineModel.d.ts +18 -0
  50. package/dist/editor-runtime/TimelineModel.d.ts.map +1 -1
  51. package/dist/editor-runtime/TimelineModel.js +67 -3
  52. package/dist/editor-runtime/TimelineModel.js.map +1 -1
  53. package/dist/editor-runtime/TimelineRuntimeBridge.d.ts +98 -0
  54. package/dist/editor-runtime/TimelineRuntimeBridge.d.ts.map +1 -0
  55. package/dist/editor-runtime/TimelineRuntimeBridge.js +186 -0
  56. package/dist/editor-runtime/TimelineRuntimeBridge.js.map +1 -0
  57. package/dist/editor-runtime/index.d.ts +3 -1
  58. package/dist/editor-runtime/index.d.ts.map +1 -1
  59. package/dist/editor-runtime/index.js +1 -0
  60. package/dist/editor-runtime/index.js.map +1 -1
  61. package/dist/engine/agent-api/AnimationController.d.ts +607 -0
  62. package/dist/engine/agent-api/AnimationController.d.ts.map +1 -0
  63. package/dist/engine/agent-api/AnimationController.js +2192 -0
  64. package/dist/engine/agent-api/AnimationController.js.map +1 -0
  65. package/dist/engine/agent-api/AssetEvidence.d.ts +88 -0
  66. package/dist/engine/agent-api/AssetEvidence.d.ts.map +1 -0
  67. package/dist/engine/agent-api/AssetEvidence.js +157 -0
  68. package/dist/engine/agent-api/AssetEvidence.js.map +1 -0
  69. package/dist/engine/agent-api/AuraAppHandle.d.ts +55 -0
  70. package/dist/engine/agent-api/AuraAppHandle.d.ts.map +1 -0
  71. package/dist/engine/agent-api/AuraAppHandle.js +15 -0
  72. package/dist/engine/agent-api/AuraAppHandle.js.map +1 -0
  73. package/dist/engine/agent-api/AuraVoiceBridge.d.ts +96 -0
  74. package/dist/engine/agent-api/AuraVoiceBridge.d.ts.map +1 -0
  75. package/dist/engine/agent-api/AuraVoiceBridge.js +370 -0
  76. package/dist/engine/agent-api/AuraVoiceBridge.js.map +1 -0
  77. package/dist/engine/agent-api/CartoonDirector.d.ts +95 -0
  78. package/dist/engine/agent-api/CartoonDirector.d.ts.map +1 -0
  79. package/dist/engine/agent-api/CartoonDirector.js +342 -0
  80. package/dist/engine/agent-api/CartoonDirector.js.map +1 -0
  81. package/dist/engine/agent-api/CartoonPerformance.d.ts +149 -0
  82. package/dist/engine/agent-api/CartoonPerformance.d.ts.map +1 -0
  83. package/dist/engine/agent-api/CartoonPerformance.js +317 -0
  84. package/dist/engine/agent-api/CartoonPerformance.js.map +1 -0
  85. package/dist/engine/agent-api/CartoonRenderQueue.d.ts +132 -0
  86. package/dist/engine/agent-api/CartoonRenderQueue.d.ts.map +1 -0
  87. package/dist/engine/agent-api/CartoonRenderQueue.js +385 -0
  88. package/dist/engine/agent-api/CartoonRenderQueue.js.map +1 -0
  89. package/dist/engine/agent-api/CharacterAssembly.d.ts +126 -0
  90. package/dist/engine/agent-api/CharacterAssembly.d.ts.map +1 -0
  91. package/dist/engine/agent-api/CharacterAssembly.js +280 -0
  92. package/dist/engine/agent-api/CharacterAssembly.js.map +1 -0
  93. package/dist/engine/agent-api/DialoguePerformance.d.ts +150 -0
  94. package/dist/engine/agent-api/DialoguePerformance.d.ts.map +1 -0
  95. package/dist/engine/agent-api/DialoguePerformance.js +335 -0
  96. package/dist/engine/agent-api/DialoguePerformance.js.map +1 -0
  97. package/dist/engine/agent-api/FrameLoop.d.ts +70 -0
  98. package/dist/engine/agent-api/FrameLoop.d.ts.map +1 -0
  99. package/dist/engine/agent-api/FrameLoop.js +165 -0
  100. package/dist/engine/agent-api/FrameLoop.js.map +1 -0
  101. package/dist/engine/agent-api/GameAssetValidation.d.ts +279 -0
  102. package/dist/engine/agent-api/GameAssetValidation.d.ts.map +1 -0
  103. package/dist/engine/agent-api/GameAssetValidation.js +719 -0
  104. package/dist/engine/agent-api/GameAssetValidation.js.map +1 -0
  105. package/dist/engine/agent-api/GameEvidence.d.ts +148 -0
  106. package/dist/engine/agent-api/GameEvidence.d.ts.map +1 -0
  107. package/dist/engine/agent-api/GameEvidence.js +269 -0
  108. package/dist/engine/agent-api/GameEvidence.js.map +1 -0
  109. package/dist/engine/agent-api/GameRuntime.d.ts +931 -0
  110. package/dist/engine/agent-api/GameRuntime.d.ts.map +1 -0
  111. package/dist/engine/agent-api/GameRuntime.js +2229 -0
  112. package/dist/engine/agent-api/GameRuntime.js.map +1 -0
  113. package/dist/engine/agent-api/GameSceneBridge.d.ts +54 -0
  114. package/dist/engine/agent-api/GameSceneBridge.d.ts.map +1 -0
  115. package/dist/engine/agent-api/GameSceneBridge.js +110 -0
  116. package/dist/engine/agent-api/GameSceneBridge.js.map +1 -0
  117. package/dist/engine/agent-api/PromptAnimationContract.d.ts +278 -0
  118. package/dist/engine/agent-api/PromptAnimationContract.d.ts.map +1 -0
  119. package/dist/engine/agent-api/PromptAnimationContract.js +238 -0
  120. package/dist/engine/agent-api/PromptAnimationContract.js.map +1 -0
  121. package/dist/engine/agent-api/PromptAnimationEvidence.d.ts +183 -0
  122. package/dist/engine/agent-api/PromptAnimationEvidence.d.ts.map +1 -0
  123. package/dist/engine/agent-api/PromptAnimationEvidence.js +454 -0
  124. package/dist/engine/agent-api/PromptAnimationEvidence.js.map +1 -0
  125. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts +100 -0
  126. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts.map +1 -0
  127. package/dist/engine/agent-api/RuntimeNodeHandle.js +36 -0
  128. package/dist/engine/agent-api/RuntimeNodeHandle.js.map +1 -0
  129. package/dist/engine/agent-api/ShotTimeline.d.ts +179 -0
  130. package/dist/engine/agent-api/ShotTimeline.d.ts.map +1 -0
  131. package/dist/engine/agent-api/ShotTimeline.js +264 -0
  132. package/dist/engine/agent-api/ShotTimeline.js.map +1 -0
  133. package/dist/engine/agent-api/VisemeController.d.ts +89 -0
  134. package/dist/engine/agent-api/VisemeController.d.ts.map +1 -0
  135. package/dist/engine/agent-api/VisemeController.js +207 -0
  136. package/dist/engine/agent-api/VisemeController.js.map +1 -0
  137. package/dist/engine/agent-api/game-kits/fighting.d.ts +123 -0
  138. package/dist/engine/agent-api/game-kits/fighting.d.ts.map +1 -0
  139. package/dist/engine/agent-api/game-kits/fighting.js +483 -0
  140. package/dist/engine/agent-api/game-kits/fighting.js.map +1 -0
  141. package/dist/engine/agent-api/game-kits/index.d.ts +15 -0
  142. package/dist/engine/agent-api/game-kits/index.d.ts.map +1 -0
  143. package/dist/engine/agent-api/game-kits/index.js +6 -0
  144. package/dist/engine/agent-api/game-kits/index.js.map +1 -0
  145. package/dist/engine/agent-api/humanoid-walk-runtime.d.ts +18 -81
  146. package/dist/engine/agent-api/humanoid-walk-runtime.d.ts.map +1 -1
  147. package/dist/engine/agent-api/humanoid-walk-runtime.js +4 -279
  148. package/dist/engine/agent-api/humanoid-walk-runtime.js.map +1 -1
  149. package/dist/engine/agent-api/index.d.ts +490 -4
  150. package/dist/engine/agent-api/index.d.ts.map +1 -1
  151. package/dist/engine/agent-api/index.js +759 -1802
  152. package/dist/engine/agent-api/index.js.map +1 -1
  153. package/dist/engine/agent-api/particle-fountain-runtime.d.ts +5 -80
  154. package/dist/engine/agent-api/particle-fountain-runtime.d.ts.map +1 -1
  155. package/dist/engine/agent-api/particle-fountain-runtime.js +7 -291
  156. package/dist/engine/agent-api/particle-fountain-runtime.js.map +1 -1
  157. package/dist/engine/agent-api/product-viewer-runtime.d.ts +17 -107
  158. package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
  159. package/dist/engine/agent-api/product-viewer-runtime.js +4 -330
  160. package/dist/engine/agent-api/product-viewer-runtime.js.map +1 -1
  161. package/dist/index.d.ts +1 -0
  162. package/dist/index.js +1 -0
  163. package/dist/physics/CollisionVolumes.d.ts +57 -0
  164. package/dist/physics/CollisionVolumes.d.ts.map +1 -0
  165. package/dist/physics/CollisionVolumes.js +159 -0
  166. package/dist/physics/CollisionVolumes.js.map +1 -0
  167. package/dist/physics/HitboxWorld.d.ts +229 -0
  168. package/dist/physics/HitboxWorld.d.ts.map +1 -0
  169. package/dist/physics/HitboxWorld.js +640 -0
  170. package/dist/physics/HitboxWorld.js.map +1 -0
  171. package/dist/physics/KinematicBody.d.ts +157 -0
  172. package/dist/physics/KinematicBody.d.ts.map +1 -0
  173. package/dist/physics/KinematicBody.js +405 -0
  174. package/dist/physics/KinematicBody.js.map +1 -0
  175. package/dist/physics/KinematicWorld.d.ts +58 -0
  176. package/dist/physics/KinematicWorld.d.ts.map +1 -0
  177. package/dist/physics/KinematicWorld.js +246 -0
  178. package/dist/physics/KinematicWorld.js.map +1 -0
  179. package/dist/physics/index.d.ts +4 -0
  180. package/dist/physics/index.d.ts.map +1 -1
  181. package/dist/physics/index.js +4 -0
  182. package/dist/physics/index.js.map +1 -1
  183. package/dist/rendering/ForwardPass.js +2 -2
  184. package/dist/rendering/ShaderLibrary.js +2 -2
  185. package/dist/rendering/SkinnedLitMaterial.js +3 -3
  186. package/dist/rendering/SkinnedUnlitMaterial.js +3 -3
  187. package/dist/scene/Renderable.js +2 -2
  188. package/dist/scripting/VisualGraph.d.ts +2 -1
  189. package/dist/scripting/VisualGraph.d.ts.map +1 -1
  190. package/dist/scripting/VisualGraph.js +118 -1
  191. package/dist/scripting/VisualGraph.js.map +1 -1
  192. package/dist/scripting/VisualGraphContext.d.ts +123 -0
  193. package/dist/scripting/VisualGraphContext.d.ts.map +1 -0
  194. package/dist/scripting/VisualGraphContext.js +2 -0
  195. package/dist/scripting/VisualGraphContext.js.map +1 -0
  196. package/dist/scripting/VisualGraphExecutor.d.ts +6 -1
  197. package/dist/scripting/VisualGraphExecutor.d.ts.map +1 -1
  198. package/dist/scripting/VisualGraphExecutor.js +364 -7
  199. package/dist/scripting/VisualGraphExecutor.js.map +1 -1
  200. package/dist/scripting/VisualNodeCatalog.d.ts +1 -1
  201. package/dist/scripting/VisualNodeCatalog.d.ts.map +1 -1
  202. package/dist/scripting/VisualNodeCatalog.js +61 -1
  203. package/dist/scripting/VisualNodeCatalog.js.map +1 -1
  204. package/dist/scripting/index.d.ts +1 -0
  205. package/dist/scripting/index.d.ts.map +1 -1
  206. package/dist/scripting/index.js.map +1 -1
  207. package/package.json +193 -121
  208. package/templates/product-viewer/src/main.ts +1 -0
  209. package/dist/three-compat/ThreeApiInventory.d.ts +0 -18
  210. package/dist/three-compat/ThreeApiInventory.d.ts.map +0 -1
  211. package/dist/three-compat/ThreeApiInventory.js +0 -99
  212. package/dist/three-compat/ThreeApiInventory.js.map +0 -1
  213. package/dist/three-compat/ThreeCompatibilityMatrix.d.ts +0 -30
  214. package/dist/three-compat/ThreeCompatibilityMatrix.d.ts.map +0 -1
  215. package/dist/three-compat/ThreeCompatibilityMatrix.js +0 -69
  216. package/dist/three-compat/ThreeCompatibilityMatrix.js.map +0 -1
  217. package/dist/three-compat/animation/index.d.ts +0 -3
  218. package/dist/three-compat/animation/index.d.ts.map +0 -1
  219. package/dist/three-compat/animation/index.js +0 -2
  220. package/dist/three-compat/animation/index.js.map +0 -1
  221. package/dist/three-compat/cameras/index.d.ts +0 -24
  222. package/dist/three-compat/cameras/index.d.ts.map +0 -1
  223. package/dist/three-compat/cameras/index.js +0 -44
  224. package/dist/three-compat/cameras/index.js.map +0 -1
  225. package/dist/three-compat/controls/index.d.ts +0 -3
  226. package/dist/three-compat/controls/index.d.ts.map +0 -1
  227. package/dist/three-compat/controls/index.js +0 -2
  228. package/dist/three-compat/controls/index.js.map +0 -1
  229. package/dist/three-compat/core/Object3DCompat.d.ts +0 -62
  230. package/dist/three-compat/core/Object3DCompat.d.ts.map +0 -1
  231. package/dist/three-compat/core/Object3DCompat.js +0 -112
  232. package/dist/three-compat/core/Object3DCompat.js.map +0 -1
  233. package/dist/three-compat/core/RaycasterCompat.d.ts +0 -18
  234. package/dist/three-compat/core/RaycasterCompat.d.ts.map +0 -1
  235. package/dist/three-compat/core/RaycasterCompat.js +0 -28
  236. package/dist/three-compat/core/RaycasterCompat.js.map +0 -1
  237. package/dist/three-compat/core/SceneCompat.d.ts +0 -8
  238. package/dist/three-compat/core/SceneCompat.d.ts.map +0 -1
  239. package/dist/three-compat/core/SceneCompat.js +0 -8
  240. package/dist/three-compat/core/SceneCompat.js.map +0 -1
  241. package/dist/three-compat/geometries/index.d.ts +0 -67
  242. package/dist/three-compat/geometries/index.d.ts.map +0 -1
  243. package/dist/three-compat/geometries/index.js +0 -114
  244. package/dist/three-compat/geometries/index.js.map +0 -1
  245. package/dist/three-compat/helpers/index.d.ts +0 -40
  246. package/dist/three-compat/helpers/index.d.ts.map +0 -1
  247. package/dist/three-compat/helpers/index.js +0 -129
  248. package/dist/three-compat/helpers/index.js.map +0 -1
  249. package/dist/three-compat/index.d.ts +0 -32
  250. package/dist/three-compat/index.d.ts.map +0 -1
  251. package/dist/three-compat/index.js +0 -22
  252. package/dist/three-compat/index.js.map +0 -1
  253. package/dist/three-compat/lights/index.d.ts +0 -35
  254. package/dist/three-compat/lights/index.d.ts.map +0 -1
  255. package/dist/three-compat/lights/index.js +0 -39
  256. package/dist/three-compat/lights/index.js.map +0 -1
  257. package/dist/three-compat/loaders/index.d.ts +0 -54
  258. package/dist/three-compat/loaders/index.d.ts.map +0 -1
  259. package/dist/three-compat/loaders/index.js +0 -79
  260. package/dist/three-compat/loaders/index.js.map +0 -1
  261. package/dist/three-compat/materials/index.d.ts +0 -70
  262. package/dist/three-compat/materials/index.d.ts.map +0 -1
  263. package/dist/three-compat/materials/index.js +0 -85
  264. package/dist/three-compat/materials/index.js.map +0 -1
  265. package/dist/three-compat/math/index.d.ts +0 -34
  266. package/dist/three-compat/math/index.d.ts.map +0 -1
  267. package/dist/three-compat/math/index.js +0 -90
  268. package/dist/three-compat/math/index.js.map +0 -1
  269. package/dist/three-compat/migration/CompatibilityWarnings.d.ts +0 -6
  270. package/dist/three-compat/migration/CompatibilityWarnings.d.ts.map +0 -1
  271. package/dist/three-compat/migration/CompatibilityWarnings.js +0 -8
  272. package/dist/three-compat/migration/CompatibilityWarnings.js.map +0 -1
  273. package/dist/three-compat/migration/ImportMap.d.ts +0 -2
  274. package/dist/three-compat/migration/ImportMap.d.ts.map +0 -1
  275. package/dist/three-compat/migration/ImportMap.js +0 -10
  276. package/dist/three-compat/migration/ImportMap.js.map +0 -1
  277. package/dist/three-compat/migration/ThreeToA3DAdapter.d.ts +0 -8
  278. package/dist/three-compat/migration/ThreeToA3DAdapter.d.ts.map +0 -1
  279. package/dist/three-compat/migration/ThreeToA3DAdapter.js +0 -20
  280. package/dist/three-compat/migration/ThreeToA3DAdapter.js.map +0 -1
  281. package/dist/three-compat/postprocessing/index.d.ts +0 -2
  282. package/dist/three-compat/postprocessing/index.d.ts.map +0 -1
  283. package/dist/three-compat/postprocessing/index.js +0 -2
  284. package/dist/three-compat/postprocessing/index.js.map +0 -1
  285. package/dist/three-compat/render-targets/index.d.ts +0 -17
  286. package/dist/three-compat/render-targets/index.d.ts.map +0 -1
  287. package/dist/three-compat/render-targets/index.js +0 -29
  288. package/dist/three-compat/render-targets/index.js.map +0 -1
  289. package/dist/three-compat/shaders/index.d.ts +0 -3
  290. package/dist/three-compat/shaders/index.d.ts.map +0 -1
  291. package/dist/three-compat/shaders/index.js +0 -3
  292. package/dist/three-compat/shaders/index.js.map +0 -1
  293. package/dist/three-compat/textures/index.d.ts +0 -19
  294. package/dist/three-compat/textures/index.d.ts.map +0 -1
  295. package/dist/three-compat/textures/index.js +0 -24
  296. package/dist/three-compat/textures/index.js.map +0 -1
@@ -0,0 +1,2229 @@
1
+ export function createGameLoopPlan(options = {}) {
2
+ return {
3
+ kind: "aura-game-loop-plan",
4
+ fixedDt: options.fixedDt ?? 1 / 60,
5
+ maxSubSteps: options.maxSubSteps ?? 5,
6
+ timeScale: options.timeScale ?? 1
7
+ };
8
+ }
9
+ export function createGameJumpAssist(options = {}) {
10
+ const coyoteMs = Math.max(0, options.coyoteMs ?? 100);
11
+ const bufferMs = Math.max(0, options.bufferMs ?? 120);
12
+ const coyoteSeconds = coyoteMs / 1000;
13
+ const bufferSeconds = bufferMs / 1000;
14
+ let time = 0;
15
+ let grounded = false;
16
+ let lastGroundedAt = Number.NEGATIVE_INFINITY;
17
+ let lastJumpRequestedAt = Number.NEGATIVE_INFINITY;
18
+ let consumed = false;
19
+ const coyoteAvailable = () => grounded || time - lastGroundedAt <= coyoteSeconds;
20
+ const jumpBuffered = () => time - lastJumpRequestedAt <= bufferSeconds;
21
+ const snapshot = () => ({
22
+ kind: "aura-game-jump-assist",
23
+ time,
24
+ grounded,
25
+ coyoteMs,
26
+ bufferMs,
27
+ lastGroundedAt,
28
+ lastJumpRequestedAt,
29
+ coyoteAvailable: coyoteAvailable(),
30
+ jumpBuffered: jumpBuffered(),
31
+ consumed,
32
+ canJump: coyoteAvailable() && jumpBuffered() && !consumed
33
+ });
34
+ return {
35
+ kind: "aura-game-jump-assist",
36
+ update(dt, state) {
37
+ time += Math.max(0, dt);
38
+ grounded = state.grounded;
39
+ if (grounded) {
40
+ lastGroundedAt = time;
41
+ consumed = false;
42
+ }
43
+ if (state.jumpPressed || state.jumpRequested)
44
+ lastJumpRequestedAt = time;
45
+ return snapshot();
46
+ },
47
+ requestJump() {
48
+ lastJumpRequestedAt = time;
49
+ },
50
+ canJump() {
51
+ return coyoteAvailable() && jumpBuffered() && !consumed;
52
+ },
53
+ consume() {
54
+ if (!this.canJump())
55
+ return false;
56
+ consumed = true;
57
+ lastJumpRequestedAt = Number.NEGATIVE_INFINITY;
58
+ return true;
59
+ },
60
+ reset(state = {}) {
61
+ time = 0;
62
+ grounded = state.grounded ?? false;
63
+ lastGroundedAt = grounded ? 0 : Number.NEGATIVE_INFINITY;
64
+ lastJumpRequestedAt = state.jumpPressed || state.jumpRequested ? 0 : Number.NEGATIVE_INFINITY;
65
+ consumed = false;
66
+ return snapshot();
67
+ },
68
+ snapshot
69
+ };
70
+ }
71
+ export function createGameInputReplay(events, options = {}) {
72
+ const fps = Math.max(1, options.fps ?? 60);
73
+ const seed = options.seed ?? 0;
74
+ const normalized = normalizeReplayEvents(events);
75
+ const frameCount = normalized.reduce((max, event) => Math.max(max, event.frame), 0);
76
+ const duration = normalized.reduce((max, event) => Math.max(max, event.time), frameCount / fps);
77
+ const checksum = replayChecksum(normalized, seed, fps);
78
+ return {
79
+ kind: "aura-game-input-replay",
80
+ label: options.label,
81
+ fps,
82
+ seed,
83
+ frameCount,
84
+ duration,
85
+ checksum,
86
+ events: normalized
87
+ };
88
+ }
89
+ export function gameInputReplayEventsAt(replay, frame) {
90
+ return replay.events.filter((event) => event.frame === frame);
91
+ }
92
+ export function createGameInputReplayDriver(input, replay) {
93
+ let frame = 0;
94
+ let time = 0;
95
+ const seek = (targetFrame, dt = 1 / replay.fps) => {
96
+ frame = Math.max(0, Math.floor(targetFrame));
97
+ time = frame / replay.fps;
98
+ return input.replay(replay.events.filter((event) => event.frame <= frame), dt);
99
+ };
100
+ return {
101
+ kind: "aura-game-input-replay-driver",
102
+ replay,
103
+ step(dt = 1 / replay.fps) {
104
+ frame += 1;
105
+ time += Math.max(0, dt);
106
+ return input.replay(replay.events.filter((event) => event.frame <= frame), dt);
107
+ },
108
+ seek,
109
+ reset() {
110
+ frame = 0;
111
+ time = 0;
112
+ input.replay([], 0);
113
+ },
114
+ snapshot() {
115
+ return {
116
+ kind: "aura-game-input-replay-driver",
117
+ frame,
118
+ time,
119
+ checksum: replay.checksum,
120
+ complete: frame >= replay.frameCount
121
+ };
122
+ }
123
+ };
124
+ }
125
+ export function createGameTouchControlLayout(options) {
126
+ const width = Math.max(1, options.width);
127
+ const height = Math.max(1, options.height);
128
+ const safeArea = {
129
+ top: options.safeArea?.top ?? 0,
130
+ right: options.safeArea?.right ?? 0,
131
+ bottom: options.safeArea?.bottom ?? 0,
132
+ left: options.safeArea?.left ?? 0
133
+ };
134
+ const scale = options.scale ?? clamp(Math.min(width, height) / 720, 0.72, 1.35);
135
+ const gap = options.gap ?? 16 * scale;
136
+ const margin = 32 * scale;
137
+ const controls = [];
138
+ const bindings = {};
139
+ const addControl = (request, center, anchor, zIndex) => {
140
+ const kind = request.kind ?? "button";
141
+ const radius = request.radius ?? (request.size ?? (kind === "button" ? 58 : 72)) * scale;
142
+ const id = request.id ?? request.action ?? `touch-${controls.length + 1}`;
143
+ const binding = request.binding ?? `Touch:${id}`;
144
+ const label = request.label ?? request.action ?? id;
145
+ const region = {
146
+ id,
147
+ kind,
148
+ action: request.action,
149
+ label,
150
+ binding,
151
+ anchor,
152
+ center,
153
+ radius,
154
+ rect: [center[0] - radius, center[1] - radius, radius * 2, radius * 2],
155
+ zIndex
156
+ };
157
+ controls.push(region);
158
+ if (request.action)
159
+ bindings[request.action] = binding;
160
+ };
161
+ if (options.stick !== false) {
162
+ const stick = options.stick ?? {};
163
+ const radius = stick.radius ?? (stick.size ?? 76) * scale;
164
+ addControl({ id: "move", kind: "stick", label: "Move", binding: "TouchStickMove", ...stick }, [safeArea.left + margin + radius, height - safeArea.bottom - margin - radius], "bottom-left", 10);
165
+ }
166
+ const buttons = options.buttons ?? [
167
+ { action: "jump", label: "Jump", binding: "TouchJump" },
168
+ { action: "light", label: "Light", binding: "TouchLight" },
169
+ { action: "special", label: "Special", binding: "TouchSpecial" }
170
+ ];
171
+ buttons.forEach((button, index) => {
172
+ const radius = button.radius ?? (button.size ?? 54) * scale;
173
+ const column = index % 2;
174
+ const row = Math.floor(index / 2);
175
+ const x = width - safeArea.right - margin - radius - column * (radius * 2 + gap);
176
+ const y = height - safeArea.bottom - margin - radius - row * (radius * 2 + gap);
177
+ addControl(button, [x, y], "bottom-right", 20 + index);
178
+ });
179
+ return {
180
+ kind: "aura-game-touch-layout",
181
+ width,
182
+ height,
183
+ safeArea,
184
+ controls,
185
+ bindings
186
+ };
187
+ }
188
+ export function createGameInput(options) {
189
+ const actions = options.actions;
190
+ const axes = options.axes ?? {};
191
+ const axisDefaults = options.axisDefaults ?? {};
192
+ const bufferMs = options.bufferMs ?? 120;
193
+ const activeBindings = new Set();
194
+ const activeActionOverrides = new Set();
195
+ const previousHeld = new Map();
196
+ const currentHeld = new Map();
197
+ const pressedEdges = new Set();
198
+ const releasedEdges = new Set();
199
+ const lastPressedAt = new Map();
200
+ const actionPressHistory = [];
201
+ const replayEvents = [];
202
+ const gamepadBindings = new Set();
203
+ const axisValues = new Map();
204
+ let frame = 0;
205
+ let time = 0;
206
+ let pointer = { active: false, x: 0, y: 0, dx: 0, dy: 0, buttons: [] };
207
+ let latestGamepads = [];
208
+ let latestSnapshot = {
209
+ kind: "aura-game-input-snapshot",
210
+ frame,
211
+ time,
212
+ activeBindings: [],
213
+ actions: {},
214
+ axes: {},
215
+ pointer,
216
+ gamepads: []
217
+ };
218
+ const resolveHeld = (action) => {
219
+ if (activeActionOverrides.has(action))
220
+ return true;
221
+ const bindings = actions[action] ?? [];
222
+ return bindings.some((binding) => activeBindings.has(binding));
223
+ };
224
+ const isActionHeld = (action) => currentHeld.get(action) ?? resolveHeld(action);
225
+ const record = (type, binding) => {
226
+ replayEvents.push({ frame, time, type, binding });
227
+ };
228
+ const pressBinding = (binding, shouldRecord = true) => {
229
+ activeBindings.add(binding);
230
+ if (actions[binding])
231
+ activeActionOverrides.add(binding);
232
+ if (shouldRecord)
233
+ record("press", binding);
234
+ };
235
+ const releaseBinding = (binding, shouldRecord = true) => {
236
+ activeBindings.delete(binding);
237
+ activeActionOverrides.delete(binding);
238
+ if (shouldRecord)
239
+ record("release", binding);
240
+ };
241
+ const pollGamepads = () => {
242
+ for (const binding of gamepadBindings)
243
+ activeBindings.delete(binding);
244
+ gamepadBindings.clear();
245
+ latestGamepads = [];
246
+ if (options.gamepad === false || typeof navigator === "undefined" || typeof navigator.getGamepads !== "function")
247
+ return;
248
+ const requestedIndex = typeof options.gamepad === "object" ? options.gamepad.index : undefined;
249
+ const pads = navigator.getGamepads();
250
+ for (const pad of pads) {
251
+ if (!pad || (requestedIndex !== undefined && pad.index !== requestedIndex))
252
+ continue;
253
+ const buttonNames = [];
254
+ pad.buttons.forEach((button, index) => {
255
+ if (!button.pressed)
256
+ return;
257
+ const names = gamepadButtonNames(index, pad.index);
258
+ for (const name of names) {
259
+ activeBindings.add(name);
260
+ gamepadBindings.add(name);
261
+ buttonNames.push(name);
262
+ }
263
+ });
264
+ latestGamepads.push({
265
+ connected: true,
266
+ index: pad.index,
267
+ axes: [...pad.axes],
268
+ buttons: buttonNames
269
+ });
270
+ }
271
+ };
272
+ const resolveAxisRaw = (name, negativeAction, positiveAction, framePointer = latestSnapshot.pointer ?? pointer) => {
273
+ const binding = axes[name];
274
+ const negative = negativeAction ?? binding?.negative;
275
+ const positive = positiveAction ?? binding?.positive;
276
+ const digital = (positive && isActionHeld(positive) ? 1 : 0) - (negative && isActionHeld(negative) ? 1 : 0);
277
+ const gamepadAxis = binding?.gamepadAxis;
278
+ const pad = latestGamepads.find((snapshot) => binding?.gamepadIndex === undefined || snapshot.index === binding.gamepadIndex);
279
+ const deadzone = binding?.deadzone ?? axisDefaults.deadzone ?? 0.18;
280
+ const deadzoneMode = binding?.deadzoneMode ?? axisDefaults.deadzoneMode ?? "axial";
281
+ const analog = gamepadAxis === undefined ? 0 : applyDeadzone(pad?.axes[gamepadAxis] ?? 0, deadzone, deadzoneMode);
282
+ const pointerAxis = binding?.pointerDelta === "x" ? framePointer.dx : binding?.pointerDelta === "y" ? framePointer.dy : 0;
283
+ if (!binding && !negative && !positive)
284
+ return isActionHeld(name) ? 1 : 0;
285
+ const invert = binding?.invert ?? axisDefaults.invert ?? false;
286
+ const scale = binding?.scale ?? axisDefaults.scale ?? 1;
287
+ return clamp((digital || analog || pointerAxis / 96) * scale * (invert ? -1 : 1), -1, 1);
288
+ };
289
+ const smoothAxis = (name, raw, dt) => {
290
+ const binding = axes[name];
291
+ const smoothing = binding?.smoothing ?? axisDefaults.smoothing ?? 0;
292
+ const previous = axisValues.get(name) ?? 0;
293
+ const snap = binding?.snap ?? axisDefaults.snap ?? false;
294
+ if (smoothing <= 0 || dt <= 0 || (snap && Math.sign(previous) !== 0 && Math.sign(raw) !== 0 && Math.sign(previous) !== Math.sign(raw)))
295
+ return raw;
296
+ const t = 1 - Math.exp(-smoothing * dt);
297
+ return clamp(previous + (raw - previous) * t, -1, 1);
298
+ };
299
+ const toSnapshot = (framePointer, axesSnapshot) => {
300
+ const actionStates = {};
301
+ const nowMs = time * 1000;
302
+ for (const action of Object.keys(actions)) {
303
+ const held = currentHeld.get(action) ?? false;
304
+ actionStates[action] = {
305
+ pressed: pressedEdges.has(action),
306
+ held,
307
+ released: releasedEdges.has(action),
308
+ buffered: pressedEdges.has(action) || nowMs - (lastPressedAt.get(action) ?? Number.NEGATIVE_INFINITY) <= bufferMs,
309
+ value: held ? 1 : 0
310
+ };
311
+ }
312
+ return {
313
+ kind: "aura-game-input-snapshot",
314
+ frame,
315
+ time,
316
+ activeBindings: [...activeBindings].sort(),
317
+ actions: actionStates,
318
+ axes: axesSnapshot,
319
+ pointer: framePointer,
320
+ gamepads: latestGamepads
321
+ };
322
+ };
323
+ const update = (dt = 1 / 60) => {
324
+ const seconds = Math.max(0, dt);
325
+ frame += 1;
326
+ time += seconds;
327
+ const framePointer = pointer;
328
+ pollGamepads();
329
+ pressedEdges.clear();
330
+ releasedEdges.clear();
331
+ for (const action of Object.keys(actions)) {
332
+ const held = resolveHeld(action);
333
+ const wasHeld = previousHeld.get(action) ?? false;
334
+ currentHeld.set(action, held);
335
+ if (held && !wasHeld) {
336
+ pressedEdges.add(action);
337
+ const nowMs = time * 1000;
338
+ lastPressedAt.set(action, nowMs);
339
+ actionPressHistory.push({ action, timeMs: nowMs });
340
+ while (actionPressHistory.length > 64)
341
+ actionPressHistory.shift();
342
+ }
343
+ if (!held && wasHeld)
344
+ releasedEdges.add(action);
345
+ previousHeld.set(action, held);
346
+ }
347
+ const axesSnapshot = {};
348
+ for (const axisName of Object.keys(axes)) {
349
+ const raw = resolveAxisRaw(axisName, undefined, undefined, framePointer);
350
+ const value = smoothAxis(axisName, raw, seconds);
351
+ axisValues.set(axisName, value);
352
+ axesSnapshot[axisName] = value;
353
+ }
354
+ latestSnapshot = toSnapshot(framePointer, axesSnapshot);
355
+ pointer = { ...pointer, dx: 0, dy: 0 };
356
+ return latestSnapshot;
357
+ };
358
+ const target = options.target ?? (typeof window !== "undefined" ? window : undefined);
359
+ const onKeyDown = (event) => {
360
+ const keyboard = event;
361
+ if (keyboard.repeat)
362
+ return;
363
+ if (keyboard.code)
364
+ pressBinding(keyboard.code);
365
+ if (keyboard.key && keyboard.key !== keyboard.code)
366
+ pressBinding(keyboard.key);
367
+ };
368
+ const onKeyUp = (event) => {
369
+ const keyboard = event;
370
+ if (keyboard.code)
371
+ releaseBinding(keyboard.code);
372
+ if (keyboard.key && keyboard.key !== keyboard.code)
373
+ releaseBinding(keyboard.key);
374
+ };
375
+ const onPointerDown = (event) => {
376
+ if (options.pointer === false)
377
+ return;
378
+ const next = event;
379
+ pressBinding(next.button === 2 ? "PointerSecondary" : "PointerPrimary");
380
+ pressBinding(`PointerButton${next.button}`);
381
+ pointer = {
382
+ active: true,
383
+ x: next.clientX,
384
+ y: next.clientY,
385
+ dx: 0,
386
+ dy: 0,
387
+ buttons: [...new Set([...pointer.buttons, next.button])].sort()
388
+ };
389
+ };
390
+ const onPointerMove = (event) => {
391
+ if (options.pointer === false)
392
+ return;
393
+ const next = event;
394
+ pointer = {
395
+ ...pointer,
396
+ x: next.clientX,
397
+ y: next.clientY,
398
+ dx: pointer.dx + next.clientX - pointer.x,
399
+ dy: pointer.dy + next.clientY - pointer.y
400
+ };
401
+ };
402
+ const onPointerUp = (event) => {
403
+ if (options.pointer === false)
404
+ return;
405
+ const next = event;
406
+ releaseBinding(next.button === 2 ? "PointerSecondary" : "PointerPrimary");
407
+ releaseBinding(`PointerButton${next.button}`);
408
+ const buttons = pointer.buttons.filter((button) => button !== next.button);
409
+ pointer = {
410
+ active: buttons.length > 0,
411
+ x: next.clientX,
412
+ y: next.clientY,
413
+ dx: pointer.dx + next.clientX - pointer.x,
414
+ dy: pointer.dy + next.clientY - pointer.y,
415
+ buttons
416
+ };
417
+ };
418
+ const onTouchStart = (event) => {
419
+ if (options.touch === false)
420
+ return;
421
+ const touch = event.touches[0];
422
+ pressBinding("TouchPrimary");
423
+ if (touch)
424
+ pointer = { active: true, x: touch.clientX, y: touch.clientY, dx: 0, dy: 0, buttons: [0] };
425
+ };
426
+ const onTouchEnd = () => {
427
+ if (options.touch === false)
428
+ return;
429
+ releaseBinding("TouchPrimary");
430
+ pointer = { ...pointer, active: false, buttons: [] };
431
+ };
432
+ if (options.autoListen !== false && target?.addEventListener) {
433
+ target.addEventListener("keydown", onKeyDown);
434
+ target.addEventListener("keyup", onKeyUp);
435
+ target.addEventListener("pointerdown", onPointerDown);
436
+ target.addEventListener("pointermove", onPointerMove);
437
+ target.addEventListener("pointerup", onPointerUp);
438
+ target.addEventListener("touchstart", onTouchStart);
439
+ target.addEventListener("touchend", onTouchEnd);
440
+ target.addEventListener("touchcancel", onTouchEnd);
441
+ }
442
+ return {
443
+ kind: "aura-game-input-plan",
444
+ actions,
445
+ axes,
446
+ bufferMs,
447
+ update,
448
+ snapshot() {
449
+ return latestSnapshot;
450
+ },
451
+ pressed(action) {
452
+ return pressedEdges.has(action);
453
+ },
454
+ held(action) {
455
+ return currentHeld.get(action) ?? resolveHeld(action);
456
+ },
457
+ released(action) {
458
+ return releasedEdges.has(action);
459
+ },
460
+ buffered(action, windowMs = bufferMs) {
461
+ return pressedEdges.has(action) || time * 1000 - (lastPressedAt.get(action) ?? Number.NEGATIVE_INFINITY) <= windowMs;
462
+ },
463
+ combo(sequence, windowMs = bufferMs * Math.max(1, sequence.length)) {
464
+ if (sequence.length === 0)
465
+ return false;
466
+ const nowMs = time * 1000;
467
+ const recent = actionPressHistory.filter((entry) => nowMs - entry.timeMs <= windowMs);
468
+ let cursor = sequence.length - 1;
469
+ for (let index = recent.length - 1; index >= 0 && cursor >= 0; index -= 1) {
470
+ if (recent[index]?.action === sequence[cursor])
471
+ cursor -= 1;
472
+ }
473
+ return cursor < 0;
474
+ },
475
+ axis(name, negativeAction, positiveAction) {
476
+ if (!negativeAction && !positiveAction && axisValues.has(name))
477
+ return axisValues.get(name) ?? 0;
478
+ return resolveAxisRaw(name, negativeAction, positiveAction, latestSnapshot.pointer ?? pointer);
479
+ },
480
+ press(binding) {
481
+ pressBinding(binding);
482
+ },
483
+ release(binding) {
484
+ releaseBinding(binding);
485
+ },
486
+ setAction(action, held) {
487
+ if (held) {
488
+ activeActionOverrides.add(action);
489
+ record("press", action);
490
+ }
491
+ else {
492
+ activeActionOverrides.delete(action);
493
+ record("release", action);
494
+ }
495
+ },
496
+ recorded() {
497
+ return [...replayEvents];
498
+ },
499
+ replay(events, dt = 0) {
500
+ activeBindings.clear();
501
+ activeActionOverrides.clear();
502
+ for (const event of events) {
503
+ if (event.type === "press")
504
+ pressBinding(event.binding, false);
505
+ else
506
+ releaseBinding(event.binding, false);
507
+ }
508
+ return update(dt);
509
+ },
510
+ clearReplay() {
511
+ replayEvents.length = 0;
512
+ },
513
+ dispose() {
514
+ if (target?.removeEventListener) {
515
+ target.removeEventListener("keydown", onKeyDown);
516
+ target.removeEventListener("keyup", onKeyUp);
517
+ target.removeEventListener("pointerdown", onPointerDown);
518
+ target.removeEventListener("pointermove", onPointerMove);
519
+ target.removeEventListener("pointerup", onPointerUp);
520
+ target.removeEventListener("touchstart", onTouchStart);
521
+ target.removeEventListener("touchend", onTouchEnd);
522
+ target.removeEventListener("touchcancel", onTouchEnd);
523
+ }
524
+ activeBindings.clear();
525
+ activeActionOverrides.clear();
526
+ previousHeld.clear();
527
+ currentHeld.clear();
528
+ pressedEdges.clear();
529
+ releasedEdges.clear();
530
+ gamepadBindings.clear();
531
+ axisValues.clear();
532
+ actionPressHistory.length = 0;
533
+ }
534
+ };
535
+ }
536
+ export function createGameKinematicBody(options = {}) {
537
+ let position = vec3(options.position, [0, options.groundY ?? 0, 0]);
538
+ let velocity = vec3(options.velocity, [0, 0, 0]);
539
+ const size = vec3(options.size ?? kinematicSizeFromCollider(options.collider), [0.72, 1.7, 0.42]);
540
+ const gravity = typeof options.gravity === "boolean" ? (options.gravity ? -18 : 0) : options.gravity ?? -18;
541
+ const groundY = options.groundY ?? 0;
542
+ const friction = options.friction ?? 10;
543
+ const maxSpeed = options.maxSpeed ?? 5;
544
+ const jumpVelocity = options.jumpVelocity ?? 7.5;
545
+ const bounds = options.bounds ?? {};
546
+ const definedMoves = new Map();
547
+ let grounded = position[1] <= groundY + 0.0001;
548
+ let facing = 1;
549
+ let elapsed = 0;
550
+ const coyoteSeconds = Math.max(0, options.coyoteMs ?? 0) / 1000;
551
+ const jumpBufferSeconds = Math.max(0, options.jumpBufferMs ?? 0) / 1000;
552
+ let lastGroundedAt = grounded ? 0 : Number.NEGATIVE_INFINITY;
553
+ let lastJumpRequestedAt = Number.NEGATIVE_INFINITY;
554
+ let jumpConsumed = false;
555
+ const snapshot = () => ({
556
+ kind: "aura-game-kinematic-body",
557
+ id: options.id,
558
+ position,
559
+ velocity,
560
+ size,
561
+ collider: options.collider,
562
+ grounded,
563
+ facing,
564
+ coyoteAvailable: canUseGroundedJump(),
565
+ jumpBuffered: hasBufferedJump()
566
+ });
567
+ const hasBufferedJump = () => elapsed - lastJumpRequestedAt <= jumpBufferSeconds;
568
+ const canUseGroundedJump = () => !jumpConsumed && (grounded || elapsed - lastGroundedAt <= coyoteSeconds);
569
+ const startJump = (nextVelocity) => {
570
+ if (!canUseGroundedJump())
571
+ return false;
572
+ grounded = false;
573
+ jumpConsumed = true;
574
+ lastJumpRequestedAt = Number.NEGATIVE_INFINITY;
575
+ velocity = [velocity[0], nextVelocity, velocity[2]];
576
+ return true;
577
+ };
578
+ const clampPosition = () => {
579
+ position = [
580
+ clamp(position[0], bounds.minX ?? Number.NEGATIVE_INFINITY, bounds.maxX ?? Number.POSITIVE_INFINITY),
581
+ clamp(position[1], bounds.minY ?? Number.NEGATIVE_INFINITY, bounds.maxY ?? Number.POSITIVE_INFINITY),
582
+ clamp(position[2], bounds.minZ ?? Number.NEGATIVE_INFINITY, bounds.maxZ ?? Number.POSITIVE_INFINITY)
583
+ ];
584
+ };
585
+ const dashBody = (direction, speed = maxSpeed * 1.8) => {
586
+ const normalized = normalizeVec3(direction);
587
+ velocity = [normalized[0] * speed, velocity[1], normalized[2] * speed];
588
+ if (Math.abs(normalized[0]) > 0.01)
589
+ facing = normalized[0] >= 0 ? 1 : -1;
590
+ };
591
+ const applyBodyKnockback = (nextVelocity) => {
592
+ velocity = addVec3(velocity, nextVelocity);
593
+ if (Math.abs(nextVelocity[0]) > 0.01)
594
+ facing = nextVelocity[0] >= 0 ? -1 : 1;
595
+ grounded = false;
596
+ };
597
+ const updateBody = (dt) => {
598
+ const seconds = Math.max(0, dt);
599
+ elapsed += seconds;
600
+ if (!grounded || velocity[1] > 0)
601
+ velocity = [velocity[0], velocity[1] + gravity * seconds, velocity[2]];
602
+ position = addVec3(position, scaleVec3(velocity, seconds));
603
+ if (position[1] <= groundY) {
604
+ position = [position[0], groundY, position[2]];
605
+ velocity = [velocity[0], Math.max(0, velocity[1]), velocity[2]];
606
+ grounded = true;
607
+ lastGroundedAt = elapsed;
608
+ jumpConsumed = false;
609
+ }
610
+ else {
611
+ grounded = false;
612
+ }
613
+ if (grounded && friction > 0) {
614
+ const damping = Math.max(0, 1 - friction * seconds);
615
+ velocity = [velocity[0] * damping, velocity[1], velocity[2] * damping];
616
+ }
617
+ clampPosition();
618
+ return snapshot();
619
+ };
620
+ return {
621
+ id: options.id,
622
+ get position() {
623
+ return position;
624
+ },
625
+ set position(next) {
626
+ position = vec3(next, position);
627
+ grounded = position[1] <= groundY + 0.0001;
628
+ if (grounded) {
629
+ lastGroundedAt = elapsed;
630
+ jumpConsumed = false;
631
+ }
632
+ },
633
+ get velocity() {
634
+ return velocity;
635
+ },
636
+ set velocity(next) {
637
+ velocity = vec3(next, velocity);
638
+ },
639
+ size,
640
+ collider: options.collider,
641
+ get grounded() {
642
+ return grounded;
643
+ },
644
+ get facing() {
645
+ return facing;
646
+ },
647
+ move(axisOrCommand, speedOrDt) {
648
+ if (typeof axisOrCommand === "number") {
649
+ const next = clamp(axisOrCommand, -1, 1);
650
+ velocity = [next * (speedOrDt ?? maxSpeed), velocity[1], velocity[2]];
651
+ if (Math.abs(next) > 0.01)
652
+ facing = next >= 0 ? 1 : -1;
653
+ return undefined;
654
+ }
655
+ const command = axisOrCommand;
656
+ const horizontal = command.axis !== undefined
657
+ ? clamp(command.axis, -1, 1) * (command.speed ?? maxSpeed)
658
+ : command.x !== undefined
659
+ ? clamp(command.x, -maxSpeed, maxSpeed)
660
+ : velocity[0];
661
+ velocity = [
662
+ horizontal,
663
+ command.y !== undefined ? command.y : velocity[1],
664
+ command.z !== undefined ? clamp(command.z, -maxSpeed, maxSpeed) : velocity[2]
665
+ ];
666
+ if (Math.abs(horizontal) > 0.01)
667
+ facing = horizontal >= 0 ? 1 : -1;
668
+ if (command.jump) {
669
+ lastJumpRequestedAt = elapsed;
670
+ startJump(command.jumpVelocity ?? jumpVelocity);
671
+ }
672
+ if (command.dash)
673
+ dashBody(typeof command.dash === "boolean" ? [facing, 0, 0] : command.dash, command.dashSpeed);
674
+ if (command.knockback) {
675
+ const knockback = command.knockback.length === 2
676
+ ? [command.knockback[0], command.knockback[1], 0]
677
+ : command.knockback;
678
+ applyBodyKnockback(knockback);
679
+ }
680
+ return speedOrDt !== undefined ? updateBody(speedOrDt) : snapshot();
681
+ },
682
+ jump(nextVelocity = jumpVelocity) {
683
+ return startJump(nextVelocity);
684
+ },
685
+ requestJump() {
686
+ lastJumpRequestedAt = elapsed;
687
+ },
688
+ canJump() {
689
+ return canUseGroundedJump();
690
+ },
691
+ consumeJump(nextVelocity = jumpVelocity) {
692
+ if (!hasBufferedJump())
693
+ return false;
694
+ return startJump(nextVelocity);
695
+ },
696
+ dash(direction, speed = maxSpeed * 1.8) {
697
+ dashBody(direction, speed);
698
+ },
699
+ applyKnockback(nextVelocity) {
700
+ applyBodyKnockback(nextVelocity);
701
+ },
702
+ defineMove(id, move) {
703
+ const nextMove = { ...move, id: move.id ?? id };
704
+ definedMoves.set(id, nextMove);
705
+ return nextMove;
706
+ },
707
+ attack(moveId) {
708
+ return definedMoves.get(moveId);
709
+ },
710
+ moves() {
711
+ return [...definedMoves.values()];
712
+ },
713
+ update: updateBody,
714
+ snapToGround(nextGroundY = groundY) {
715
+ position = [position[0], nextGroundY, position[2]];
716
+ velocity = [velocity[0], Math.max(0, velocity[1]), velocity[2]];
717
+ grounded = true;
718
+ lastGroundedAt = elapsed;
719
+ jumpConsumed = false;
720
+ },
721
+ bounds() {
722
+ return aabb(position, size);
723
+ },
724
+ snapshot
725
+ };
726
+ }
727
+ export function createGameFighting2DRules(options = {}) {
728
+ return {
729
+ kind: "aura-game-fighting-2d-rules",
730
+ gravity: options.gravity ?? 24,
731
+ roundSeconds: options.roundSeconds ?? 90,
732
+ maxHealth: options.maxHealth ?? 100,
733
+ maxGuard: options.maxGuard ?? 100,
734
+ maxMeter: options.maxMeter ?? 100,
735
+ stageBounds: options.stageBounds ?? { minX: -4.5, maxX: 4.5, minZ: -0.72, maxZ: 0.72 },
736
+ fps: options.fps ?? 60,
737
+ pushboxSeparation: options.pushboxSeparation ?? true
738
+ };
739
+ }
740
+ export function createCombatWorld(options = {}) {
741
+ const actors = new Map();
742
+ const fighterBodies = new Map();
743
+ const moves = new Map();
744
+ const activeAttacks = [];
745
+ const rules = options.rules ?? createGameFighting2DRules();
746
+ const stageBounds = options.stageBounds ?? rules.stageBounds;
747
+ let events = [];
748
+ let frame = 0;
749
+ let time = 0;
750
+ const snapshot = () => ({
751
+ kind: "aura-game-combat-world",
752
+ frame,
753
+ time,
754
+ actors: [...actors.values()].map(actorSnapshot),
755
+ activeAttacks: activeAttacks.map((attack) => {
756
+ const activeFrames = moveActiveFrames(attack.move, rules.fps);
757
+ const durationFrames = moveDurationFrames(attack.move, activeFrames, rules.fps);
758
+ return {
759
+ attackerId: attack.attackerId,
760
+ moveId: attack.move.id,
761
+ frame: attack.frame,
762
+ activeFrames,
763
+ durationFrames,
764
+ active: attack.frame >= activeFrames[0] && attack.frame <= activeFrames[1],
765
+ hitboxes: moveHitboxes(attack.move),
766
+ hitTargets: [...attack.hitTargets].sort()
767
+ };
768
+ }),
769
+ events: [...events]
770
+ });
771
+ const controller = {
772
+ addActor(actor) {
773
+ const normalized = normalizeActor(actor);
774
+ actors.set(actor.id, {
775
+ ...normalized,
776
+ health: actor.health ?? rules.maxHealth,
777
+ guard: actor.guard ?? rules.maxGuard,
778
+ meter: actor.meter ?? 0
779
+ });
780
+ },
781
+ setActor(id, patch) {
782
+ const actor = actors.get(id);
783
+ if (!actor) {
784
+ actors.set(id, normalizeActor({ id, ...patch }));
785
+ return;
786
+ }
787
+ const stun = actor.stun;
788
+ const recovery = actor.recovery;
789
+ Object.assign(actor, normalizeActor({ ...actorSnapshot(actor), ...patch, id }));
790
+ actor.stun = stun;
791
+ actor.recovery = recovery;
792
+ },
793
+ removeActor(id) {
794
+ actors.delete(id);
795
+ fighterBodies.delete(id);
796
+ moves.delete(id);
797
+ },
798
+ defineMove(actorId, move) {
799
+ const actorMoves = moves.get(actorId) ?? new Map();
800
+ actorMoves.set(move.id, normalizeCombatMove(move));
801
+ moves.set(actorId, actorMoves);
802
+ },
803
+ attack(attackerId, move) {
804
+ const resolvedMove = typeof move === "string" ? moves.get(attackerId)?.get(move) ?? fighterBodies.get(attackerId)?.attack(move) : move;
805
+ if (resolvedMove)
806
+ this.beginAttack(attackerId, resolvedMove);
807
+ },
808
+ beginAttack(attackerId, move) {
809
+ if (!actors.has(attackerId))
810
+ return;
811
+ activeAttacks.push({ attackerId, move: normalizeCombatMove(move), frame: 0, hitTargets: new Set() });
812
+ },
813
+ update(dt) {
814
+ frame += 1;
815
+ time += Math.max(0, dt);
816
+ events = [];
817
+ syncActorsFromBodies(fighterBodies, actors);
818
+ for (const actor of actors.values()) {
819
+ actor.stun = Math.max(0, actor.stun - 1);
820
+ actor.recovery = Math.max(0, actor.recovery - 1);
821
+ }
822
+ if (rules.pushboxSeparation)
823
+ resolvePushboxSeparation(actors, stageBounds, frame, time, events);
824
+ for (let index = activeAttacks.length - 1; index >= 0; index -= 1) {
825
+ const attack = activeAttacks[index];
826
+ attack.frame += 1;
827
+ const attacker = actors.get(attack.attackerId);
828
+ if (!attacker) {
829
+ activeAttacks.splice(index, 1);
830
+ continue;
831
+ }
832
+ const [activeStart, activeEnd] = moveActiveFrames(attack.move, rules.fps);
833
+ if (attack.frame >= activeStart && attack.frame <= activeEnd) {
834
+ resolveAttack(attacker, attack, actors, stageBounds, frame, time, events);
835
+ }
836
+ if (attack.frame >= moveDurationFrames(attack.move, [activeStart, activeEnd], rules.fps)) {
837
+ if (attack.hitTargets.size === 0) {
838
+ events.push({
839
+ type: "whiff",
840
+ frame,
841
+ time,
842
+ attackerId: attacker.id,
843
+ moveId: attack.move.id,
844
+ position: attacker.position
845
+ });
846
+ }
847
+ activeAttacks.splice(index, 1);
848
+ }
849
+ }
850
+ syncBodiesFromActors(fighterBodies, actors);
851
+ return snapshot();
852
+ },
853
+ step(dt) {
854
+ return this.update(dt).events;
855
+ },
856
+ events() {
857
+ return [...events];
858
+ },
859
+ consumeEvents() {
860
+ const consumed = [...events];
861
+ events = [];
862
+ return consumed;
863
+ },
864
+ snapshot,
865
+ clear() {
866
+ actors.clear();
867
+ activeAttacks.length = 0;
868
+ events = [];
869
+ frame = 0;
870
+ time = 0;
871
+ }
872
+ };
873
+ for (const [index, fighter] of (options.fighters ?? []).entries()) {
874
+ if (isGameKinematicBody(fighter)) {
875
+ const id = fighter.id ?? `fighter-${index + 1}`;
876
+ fighterBodies.set(id, fighter);
877
+ controller.addActor({ id, position: fighter.position, facing: fighter.facing });
878
+ for (const move of fighter.moves())
879
+ controller.defineMove(id, move);
880
+ }
881
+ else {
882
+ controller.addActor(fighter);
883
+ }
884
+ }
885
+ return controller;
886
+ }
887
+ export function createGameCameraDirector(options = {}) {
888
+ const baseFov = options.baseFov ?? 42;
889
+ const baseDistance = options.distance ?? 6.2;
890
+ const targetY = options.targetY ?? 0.95;
891
+ const minZoom = options.minZoom ?? 0.86;
892
+ const maxZoom = options.maxZoom ?? 1.24;
893
+ const reducedMotion = options.reducedMotion ?? false;
894
+ const mode = options.mode ?? "side-fighter";
895
+ const stageBounds = options.stageBounds ?? options.bounds;
896
+ const impactShake = options.impactShake ?? true;
897
+ let state = {
898
+ kind: "aura-game-camera-director",
899
+ position: [0, 1.35, baseDistance],
900
+ target: [0, targetY, 0],
901
+ fov: baseFov,
902
+ zoom: 1,
903
+ shake: 0,
904
+ reducedMotion,
905
+ mode,
906
+ targetIds: options.targetIds
907
+ };
908
+ let shakeRemaining = 0;
909
+ let shakeIntensity = 0;
910
+ let specialRemaining = 0;
911
+ let specialTarget;
912
+ return {
913
+ update(dt, targets) {
914
+ const seconds = Math.max(0, dt);
915
+ shakeRemaining = Math.max(0, shakeRemaining - seconds);
916
+ specialRemaining = Math.max(0, specialRemaining - seconds);
917
+ const positions = targets.length ? targets.map((target) => target.position) : [state.target];
918
+ const minX = Math.min(...positions.map((position) => position[0]));
919
+ const maxX = Math.max(...positions.map((position) => position[0]));
920
+ const centerX = clamp((minX + maxX) / 2, stageBounds?.minX ?? -100, stageBounds?.maxX ?? 100);
921
+ const distance = Math.max(1, maxX - minX);
922
+ const zoom = clamp(1 + (distance - 2.2) * 0.08, minZoom, maxZoom);
923
+ const focus = specialRemaining > 0 && specialTarget ? specialTarget : [centerX, targetY, 0];
924
+ const shake = reducedMotion ? 0 : shakeRemaining > 0 ? shakeIntensity * (shakeRemaining / Math.max(0.001, shakeRemaining + seconds)) : 0;
925
+ state = {
926
+ kind: "aura-game-camera-director",
927
+ target: focus,
928
+ position: [focus[0] + shake * 0.04, focus[1] + 0.42 + shake * 0.02, baseDistance * zoom],
929
+ fov: baseFov / zoom,
930
+ zoom,
931
+ shake,
932
+ reducedMotion,
933
+ mode,
934
+ targetIds: options.targetIds ?? targets.map((target) => target.id).filter((id) => Boolean(id))
935
+ };
936
+ return state;
937
+ },
938
+ impact(intensity = 1, duration = 0.16) {
939
+ if (reducedMotion || !impactShake)
940
+ return;
941
+ shakeIntensity = Math.max(shakeIntensity, intensity);
942
+ shakeRemaining = Math.max(shakeRemaining, duration);
943
+ },
944
+ special(target, duration = 0.8) {
945
+ specialTarget = target;
946
+ specialRemaining = duration;
947
+ },
948
+ snapshot() {
949
+ return state;
950
+ }
951
+ };
952
+ }
953
+ export function createGameEffects(options = {}) {
954
+ const poolSize = options.poolSize ?? 96;
955
+ const reducedMotion = options.reducedMotion ?? false;
956
+ const reducedFlash = options.reducedFlash ?? false;
957
+ let spawned = 0;
958
+ let effects = [];
959
+ const snapshot = () => ({
960
+ kind: "aura-game-effects",
961
+ active: effects.length,
962
+ spawned,
963
+ pooled: poolSize,
964
+ reducedMotion,
965
+ reducedFlash,
966
+ effects: effects.map(publicGameEffectInstance)
967
+ });
968
+ const spawn = (kind, position, effectOptions = {}) => {
969
+ spawned += 1;
970
+ const flashLimited = reducedFlash && (kind === "impact-flash" || kind === "super-flash");
971
+ const motionLimited = reducedMotion && (kind === "dash-trail" || kind === "slash-trail" || kind === "shockwave" || kind === "ring-shockwave");
972
+ const attachment = effectOptions.attachment;
973
+ const effect = {
974
+ id: effectOptions.ownerId ? `${effectOptions.ownerId}:${kind}:${spawned}` : `${kind}:${spawned}`,
975
+ kind,
976
+ position: resolveEffectAttachmentPosition(attachment, position),
977
+ color: effectOptions.color ?? defaultEffectColor(kind),
978
+ intensity: Math.min(effectOptions.intensity ?? 1, flashLimited ? 0.35 : motionLimited ? 0.55 : Number.POSITIVE_INFINITY),
979
+ duration: Math.min(effectOptions.duration ?? defaultEffectDuration(kind), motionLimited ? 0.18 : Number.POSITIVE_INFINITY),
980
+ radius: effectOptions.radius ?? defaultEffectRadius(kind),
981
+ ownerId: effectOptions.ownerId,
982
+ attachmentId: attachment?.targetId ?? attachment?.id,
983
+ attachmentOffset: attachment?.offset,
984
+ attachment,
985
+ age: 0
986
+ };
987
+ if (effects.length >= poolSize)
988
+ effects.shift();
989
+ effects.push(effect);
990
+ return publicGameEffectInstance(effect);
991
+ };
992
+ return {
993
+ spawn,
994
+ emit(combatEvents, effectOptions) {
995
+ const emitted = [];
996
+ for (const event of combatEvents) {
997
+ if (event.type === "hit")
998
+ emitted.push(spawn("hit-spark", event.position, { ownerId: event.attackerId, ...effectOptions }));
999
+ if (event.type === "blocked")
1000
+ emitted.push(spawn("block-spark", event.position, { ownerId: event.attackerId, ...effectOptions }));
1001
+ if (event.type === "push")
1002
+ emitted.push(spawn("ground-dust", event.position, { ownerId: event.attackerId, intensity: 0.45, duration: 0.14, ...effectOptions }));
1003
+ }
1004
+ return emitted;
1005
+ },
1006
+ hitSpark: (position, effectOptions) => spawn("hit-spark", position, effectOptions),
1007
+ blockSpark: (position, effectOptions) => spawn("block-spark", position, effectOptions),
1008
+ impactDecal: (position, effectOptions) => spawn("impact-decal", position, effectOptions),
1009
+ groundDust: (position, effectOptions) => spawn("ground-dust", position, effectOptions),
1010
+ dashTrail: (position, effectOptions) => spawn("dash-trail", position, effectOptions),
1011
+ slashTrail: (position, effectOptions) => spawn("slash-trail", position, effectOptions),
1012
+ impactFlash: (position, effectOptions) => spawn("impact-flash", position, effectOptions),
1013
+ auraBurst: (position, effectOptions) => spawn("aura-burst", position, effectOptions),
1014
+ shockwave: (position, effectOptions) => spawn("shockwave", position, effectOptions),
1015
+ ringShockwave: (position, effectOptions) => spawn("ring-shockwave", position, effectOptions),
1016
+ superFlash: (position, effectOptions) => spawn("super-flash", position, effectOptions),
1017
+ update(dt) {
1018
+ const seconds = Math.max(0, dt);
1019
+ effects = effects
1020
+ .map((effect) => ({
1021
+ ...effect,
1022
+ position: resolveEffectAttachmentPosition(effect.attachment, effect.position),
1023
+ age: effect.age + seconds
1024
+ }))
1025
+ .filter((effect) => effect.age <= effect.duration);
1026
+ return snapshot();
1027
+ },
1028
+ snapshot,
1029
+ nodes() {
1030
+ return effects.map(effectToSceneNode);
1031
+ },
1032
+ clear() {
1033
+ effects = [];
1034
+ }
1035
+ };
1036
+ }
1037
+ export function applyGameCombatEventsToRuntime(events, options = {}) {
1038
+ const effectIds = [];
1039
+ let cameraImpacts = 0;
1040
+ for (const event of events) {
1041
+ if (event.type === "hit") {
1042
+ const effect = options.effects?.hitSpark(event.position, { ownerId: event.attackerId });
1043
+ if (effect)
1044
+ effectIds.push(effect.id);
1045
+ options.camera?.impact(1.1);
1046
+ if (options.camera)
1047
+ cameraImpacts += 1;
1048
+ }
1049
+ else if (event.type === "blocked") {
1050
+ const effect = options.effects?.blockSpark(event.position, { ownerId: event.attackerId });
1051
+ if (effect)
1052
+ effectIds.push(effect.id);
1053
+ options.camera?.impact(0.55);
1054
+ if (options.camera)
1055
+ cameraImpacts += 1;
1056
+ }
1057
+ else if (event.type === "push") {
1058
+ const effect = options.effects?.groundDust(event.position, { ownerId: event.attackerId, intensity: 0.45, duration: 0.14 });
1059
+ if (effect)
1060
+ effectIds.push(effect.id);
1061
+ }
1062
+ }
1063
+ const hud = options.hudBindings && options.combat
1064
+ ? createGameHudSnapshot({
1065
+ bindings: options.hudBindings,
1066
+ combat: options.combat,
1067
+ events,
1068
+ round: options.round,
1069
+ rules: options.rules,
1070
+ input: options.input,
1071
+ runtime: options.runtime,
1072
+ debug: options.debug,
1073
+ appState: options.appState
1074
+ })
1075
+ : undefined;
1076
+ return {
1077
+ kind: "aura-game-combat-event-runtime-bridge",
1078
+ events: [...events],
1079
+ effectIds,
1080
+ cameraImpacts,
1081
+ hud
1082
+ };
1083
+ }
1084
+ export function createGameBoxCollider(options = {}) {
1085
+ const size = size3(options.size, [
1086
+ options.width ?? 1,
1087
+ options.height ?? 1,
1088
+ options.depth ?? (options.dimension === 2 ? 0 : 1)
1089
+ ]);
1090
+ return {
1091
+ ...colliderBase(options, size[2] === 0 ? 2 : 3),
1092
+ kind: "box",
1093
+ size
1094
+ };
1095
+ }
1096
+ export function createGameSphereCollider(options = {}) {
1097
+ const radius = Math.max(0, options.radius ?? 0.5);
1098
+ return {
1099
+ ...colliderBase(options, options.dimension ?? 3),
1100
+ kind: "sphere",
1101
+ radius
1102
+ };
1103
+ }
1104
+ export function createGameCapsuleCollider(options = {}) {
1105
+ return {
1106
+ ...colliderBase(options, options.dimension ?? 3),
1107
+ kind: "capsule",
1108
+ radius: Math.max(0, options.radius ?? 0.35),
1109
+ height: Math.max(0, options.height ?? 1.7),
1110
+ axis: options.axis ?? "y"
1111
+ };
1112
+ }
1113
+ export function createGameRectCollider(options = {}) {
1114
+ return {
1115
+ ...colliderBase(options, 2),
1116
+ kind: "rect",
1117
+ size: vec2(options.size, [options.width ?? 1, options.height ?? 1]),
1118
+ plane: options.plane ?? "xy"
1119
+ };
1120
+ }
1121
+ export const gameColliders = {
1122
+ box: createGameBoxCollider,
1123
+ sphere: createGameSphereCollider,
1124
+ capsule: createGameCapsuleCollider,
1125
+ rect: createGameRectCollider
1126
+ };
1127
+ export function createGameCollisionBox(options = {}, tag = "hitbox") {
1128
+ const size = size3(options.size, [options.width ?? 1, options.height ?? 1, options.depth ?? 0.5]);
1129
+ return {
1130
+ id: options.id,
1131
+ offset: vec3Flexible(options.offset, [0, 0, 0]),
1132
+ center: options.center ? vec3Flexible(options.center, [0, 0, 0]) : undefined,
1133
+ size,
1134
+ tags: [...(options.tags ?? []), tag]
1135
+ };
1136
+ }
1137
+ export function createGameHitboxRect(options = {}) {
1138
+ return createGameCollisionBox(options, "hitbox");
1139
+ }
1140
+ export function createGameHurtboxRect(options = {}) {
1141
+ return createGameCollisionBox(options, "hurtbox");
1142
+ }
1143
+ export function createGameGuardboxRect(options = {}) {
1144
+ return createGameCollisionBox(options, "guardbox");
1145
+ }
1146
+ export function createGamePushboxRect(options = {}) {
1147
+ return createGameCollisionBox(options, "pushbox");
1148
+ }
1149
+ export const gameHitboxes = {
1150
+ rect: createGameHitboxRect,
1151
+ box: createGameHitboxRect
1152
+ };
1153
+ export const gameHurtboxes = {
1154
+ rect: createGameHurtboxRect,
1155
+ box: createGameHurtboxRect
1156
+ };
1157
+ export const gameGuardboxes = {
1158
+ rect: createGameGuardboxRect,
1159
+ box: createGameGuardboxRect
1160
+ };
1161
+ export const gamePushboxes = {
1162
+ rect: createGamePushboxRect,
1163
+ box: createGamePushboxRect
1164
+ };
1165
+ export const gameTriggerVolumes = {
1166
+ box: (options = {}) => createGameBoxCollider({ ...options, sensor: true, tags: [...(options.tags ?? []), "trigger"] }),
1167
+ sphere: (options = {}) => createGameSphereCollider({ ...options, sensor: true, tags: [...(options.tags ?? []), "trigger"] }),
1168
+ capsule: (options = {}) => createGameCapsuleCollider({ ...options, sensor: true, tags: [...(options.tags ?? []), "trigger"] }),
1169
+ rect: (options = {}) => createGameRectCollider({ ...options, sensor: true, tags: [...(options.tags ?? []), "trigger"] })
1170
+ };
1171
+ export const gameEffectPresets = {
1172
+ hitSpark: (options = {}) => ({ kind: "hit-spark", options }),
1173
+ blockSpark: (options = {}) => ({ kind: "block-spark", options }),
1174
+ groundDust: (options = {}) => ({ kind: "ground-dust", options }),
1175
+ dashTrail: (options = {}) => ({ kind: "dash-trail", options }),
1176
+ slashTrail: (options = {}) => ({ kind: "slash-trail", options }),
1177
+ shockwave: (options = {}) => ({ kind: "shockwave", options }),
1178
+ auraBurst: (options = {}) => ({ kind: "aura-burst", options }),
1179
+ superBurst: (options = {}) => ({ kind: "super-flash", options })
1180
+ };
1181
+ export function gameColliderAabb(collider) {
1182
+ if (collider.kind === "box")
1183
+ return aabb(collider.center, collider.size);
1184
+ if (collider.kind === "sphere") {
1185
+ const diameter = collider.radius * 2;
1186
+ return aabb(collider.center, [diameter, diameter, collider.dimension === 2 ? 0 : diameter]);
1187
+ }
1188
+ if (collider.kind === "capsule") {
1189
+ const diameter = collider.radius * 2;
1190
+ const size = collider.axis === "x"
1191
+ ? [collider.height, diameter, collider.dimension === 2 ? 0 : diameter]
1192
+ : collider.axis === "z"
1193
+ ? [diameter, diameter, collider.height]
1194
+ : [diameter, collider.height, collider.dimension === 2 ? 0 : diameter];
1195
+ return aabb(collider.center, size);
1196
+ }
1197
+ const size = collider.plane === "xz"
1198
+ ? [collider.size[0], 0, collider.size[1]]
1199
+ : collider.plane === "yz"
1200
+ ? [0, collider.size[0], collider.size[1]]
1201
+ : [collider.size[0], collider.size[1], 0];
1202
+ return aabb(collider.center, size);
1203
+ }
1204
+ export function createGameColliderDebugGeometry(colliders, options = {}) {
1205
+ return colliders.map((collider, index) => debugGeometryFromCollider(collider, options, index));
1206
+ }
1207
+ export function createGameHitboxDebugGeometry(hitboxes, options = {}) {
1208
+ const origin = vec3(options.origin, [0, 0, 0]);
1209
+ const facing = options.facing ?? 1;
1210
+ const mirrorX = options.mirrorX ?? true;
1211
+ return hitboxes.map((hitbox, index) => {
1212
+ const offset = vec3(hitbox.offset ?? hitbox.center, [0, 0, 0]);
1213
+ const center = addVec3(origin, [offset[0] * (mirrorX ? facing : 1), offset[1], offset[2]]);
1214
+ return debugGeometryFromCollider(createGameBoxCollider({
1215
+ id: hitbox.id,
1216
+ center,
1217
+ size: hitbox.size,
1218
+ tags: hitbox.tags,
1219
+ dimension: hitbox.size[2] === 0 ? 2 : 3
1220
+ }), { color: "#ff5c8a", source: "hitbox", ...options }, index);
1221
+ });
1222
+ }
1223
+ export function createGameCombatDebugGeometry(snapshot, options = {}) {
1224
+ const nodes = [];
1225
+ for (const actor of snapshot.actors) {
1226
+ nodes.push(...createGameHitboxDebugGeometry(actor.hurtboxes, {
1227
+ color: "#47d16c",
1228
+ source: `${actor.id}:hurtbox`,
1229
+ origin: actor.position,
1230
+ facing: actor.facing,
1231
+ ...options
1232
+ }));
1233
+ nodes.push(...createGameHitboxDebugGeometry(actor.guardboxes, {
1234
+ color: "#5cc8ff",
1235
+ source: `${actor.id}:guardbox`,
1236
+ origin: actor.position,
1237
+ facing: actor.facing,
1238
+ ...options
1239
+ }));
1240
+ nodes.push(...createGameHitboxDebugGeometry(actor.pushboxes, {
1241
+ color: "#ffd166",
1242
+ source: `${actor.id}:pushbox`,
1243
+ origin: actor.position,
1244
+ facing: actor.facing,
1245
+ ...options
1246
+ }));
1247
+ }
1248
+ for (const attack of snapshot.activeAttacks) {
1249
+ const actor = snapshot.actors.find((candidate) => candidate.id === attack.attackerId);
1250
+ if (!actor)
1251
+ continue;
1252
+ const source = `${attack.attackerId}:${attack.moveId}:${attack.active ? "active-hitbox" : "inactive-hitbox"}`;
1253
+ const activeTags = [
1254
+ "attack-hitbox",
1255
+ attack.active ? "active-frame" : "inactive-frame",
1256
+ `move:${attack.moveId}`,
1257
+ `frame:${attack.frame}`,
1258
+ `active:${attack.activeFrames[0]}-${attack.activeFrames[1]}`
1259
+ ];
1260
+ nodes.push(...createGameHitboxDebugGeometry(attack.hitboxes, {
1261
+ color: attack.active ? "#ff5c8a" : "#7c2d12",
1262
+ source,
1263
+ origin: actor.position,
1264
+ facing: actor.facing,
1265
+ ...options
1266
+ }).map((node) => ({
1267
+ ...node,
1268
+ tags: [...node.tags, ...activeTags]
1269
+ })));
1270
+ }
1271
+ snapshot.events.forEach((event, index) => {
1272
+ nodes.push(debugGeometryFromCollider(createGameSphereCollider({
1273
+ id: `contact:${event.frame}:${event.attackerId}:${event.targetId ?? "none"}:${index}`,
1274
+ center: event.position,
1275
+ radius: event.type === "hit" || event.type === "blocked" ? 0.075 : 0.045,
1276
+ tags: ["contact-point", `event:${event.type}`, `attacker:${event.attackerId}`, event.targetId ? `target:${event.targetId}` : "target:none"]
1277
+ }), {
1278
+ color: event.type === "blocked" ? "#5cc8ff" : event.type === "hit" ? "#ffffff" : "#ffd166",
1279
+ opacity: 0.82,
1280
+ wireframe: false,
1281
+ source: `contact:${event.type}`,
1282
+ ...options
1283
+ }, index));
1284
+ });
1285
+ return nodes;
1286
+ }
1287
+ export function createGameDebugOverlayData(options = {}) {
1288
+ const inputSnapshot = options.input && "kind" in options.input && options.input.kind === "aura-game-input-snapshot"
1289
+ ? options.input
1290
+ : options.input && "snapshot" in options.input
1291
+ ? options.input.snapshot()
1292
+ : undefined;
1293
+ const bodySnapshots = options.bodySnapshots ?? options.bodies?.map((body) => body.snapshot()) ?? [];
1294
+ const combatSnapshot = options.combat && "kind" in options.combat && options.combat.kind === "aura-game-combat-world"
1295
+ ? options.combat
1296
+ : options.combat && "snapshot" in options.combat
1297
+ ? options.combat.snapshot()
1298
+ : undefined;
1299
+ const effectsSnapshot = options.effects && "kind" in options.effects && options.effects.kind === "aura-game-effects"
1300
+ ? options.effects
1301
+ : options.effects && "snapshot" in options.effects
1302
+ ? options.effects.snapshot()
1303
+ : undefined;
1304
+ const cameraSnapshot = options.camera && "kind" in options.camera && options.camera.kind === "aura-game-camera-director"
1305
+ ? options.camera
1306
+ : options.camera && "snapshot" in options.camera
1307
+ ? options.camera.snapshot()
1308
+ : undefined;
1309
+ const sections = [];
1310
+ if (options.runtime) {
1311
+ sections.push({
1312
+ id: "runtime",
1313
+ title: "Runtime",
1314
+ metrics: [
1315
+ { id: "frame", label: "Frame", value: options.runtime.frame },
1316
+ { id: "time", label: "Time", value: round(options.runtime.time, 3) },
1317
+ { id: "paused", label: "Paused", value: options.runtime.paused ?? false }
1318
+ ]
1319
+ });
1320
+ }
1321
+ if (inputSnapshot) {
1322
+ sections.push({
1323
+ id: "input",
1324
+ title: "Input",
1325
+ metrics: [
1326
+ { id: "inputFrame", label: "Input frame", value: inputSnapshot.frame },
1327
+ { id: "bindings", label: "Active bindings", value: inputSnapshot.activeBindings.join(", ") || "none" },
1328
+ { id: "actions", label: "Held actions", value: Object.entries(inputSnapshot.actions).filter(([, state]) => state.held).map(([action]) => action).join(", ") || "none" }
1329
+ ]
1330
+ });
1331
+ }
1332
+ if (bodySnapshots.length) {
1333
+ sections.push({
1334
+ id: "physics",
1335
+ title: "Physics",
1336
+ metrics: [
1337
+ { id: "bodies", label: "Bodies", value: bodySnapshots.length },
1338
+ { id: "grounded", label: "Grounded", value: bodySnapshots.filter((body) => body.grounded).length },
1339
+ { id: "bufferedJumps", label: "Buffered jumps", value: bodySnapshots.filter((body) => body.jumpBuffered).length }
1340
+ ]
1341
+ });
1342
+ }
1343
+ if (combatSnapshot) {
1344
+ sections.push({
1345
+ id: "combat",
1346
+ title: "Combat",
1347
+ metrics: [
1348
+ { id: "actors", label: "Actors", value: combatSnapshot.actors.length },
1349
+ { id: "activeAttacks", label: "Active attacks", value: combatSnapshot.activeAttacks.length },
1350
+ { id: "activeFrames", label: "Active frames", value: combatSnapshot.activeAttacks.filter((attack) => attack.active).map((attack) => `${attack.moveId}:${attack.frame}/${attack.activeFrames[0]}-${attack.activeFrames[1]}`).join(", ") || "none" },
1351
+ { id: "contactPoints", label: "Contact points", value: combatSnapshot.events.filter((event) => event.type === "hit" || event.type === "blocked" || event.type === "push").length },
1352
+ { id: "events", label: "Events", value: combatSnapshot.events.length }
1353
+ ]
1354
+ });
1355
+ }
1356
+ if (effectsSnapshot) {
1357
+ sections.push({
1358
+ id: "effects",
1359
+ title: "Effects",
1360
+ metrics: [
1361
+ { id: "active", label: "Active", value: effectsSnapshot.active },
1362
+ { id: "spawned", label: "Spawned", value: effectsSnapshot.spawned },
1363
+ { id: "pooled", label: "Pool", value: effectsSnapshot.pooled }
1364
+ ]
1365
+ });
1366
+ }
1367
+ if (cameraSnapshot) {
1368
+ sections.push({
1369
+ id: "camera",
1370
+ title: "Camera",
1371
+ metrics: [
1372
+ { id: "fov", label: "FOV", value: round(cameraSnapshot.fov, 2) },
1373
+ { id: "zoom", label: "Zoom", value: round(cameraSnapshot.zoom, 3) },
1374
+ { id: "shake", label: "Shake", value: round(cameraSnapshot.shake, 3) }
1375
+ ]
1376
+ });
1377
+ }
1378
+ const geometry = [
1379
+ ...createGameColliderDebugGeometry(options.colliders ?? [], { source: "collider" }),
1380
+ ...createGameHitboxDebugGeometry(options.hitboxes ?? [], { source: "hitbox" }),
1381
+ ...(combatSnapshot ? createGameCombatDebugGeometry(combatSnapshot) : [])
1382
+ ];
1383
+ return {
1384
+ kind: "aura-game-debug-overlay",
1385
+ frame: options.runtime?.frame ?? inputSnapshot?.frame ?? combatSnapshot?.frame ?? 0,
1386
+ time: options.runtime?.time ?? inputSnapshot?.time ?? combatSnapshot?.time ?? 0,
1387
+ paused: options.runtime?.paused ?? false,
1388
+ sections,
1389
+ geometry,
1390
+ labels: options.labels ?? {},
1391
+ warnings: options.warnings ?? []
1392
+ };
1393
+ }
1394
+ export function createGameDebugSceneNodes(debug, options = {}) {
1395
+ const geometry = Array.isArray(debug)
1396
+ ? debug
1397
+ : debug.geometry;
1398
+ const runtimePrefix = options.runtimePrefix ?? "debug";
1399
+ const primitives = geometry.map((node) => ({
1400
+ kind: "primitive",
1401
+ primitive: debugScenePrimitive(node.primitive),
1402
+ name: `${runtimePrefix}:${node.id}`,
1403
+ position: node.position,
1404
+ scale: debugSceneScale(node),
1405
+ material: {
1406
+ color: node.color,
1407
+ emissive: node.color,
1408
+ emissiveIntensity: node.tags.includes("contact-point") ? 0.95 : 0.38,
1409
+ opacity: node.opacity,
1410
+ transparent: node.opacity < 1,
1411
+ wireframe: node.wireframe
1412
+ },
1413
+ runtime: {
1414
+ id: `${runtimePrefix}:${node.id}`,
1415
+ mutable: true,
1416
+ tags: ["debug", "game-runtime", node.source, ...node.tags]
1417
+ },
1418
+ debug: {
1419
+ source: node.source,
1420
+ tags: node.tags,
1421
+ aabb: node.aabb
1422
+ }
1423
+ }));
1424
+ if (!options.includeLabels)
1425
+ return primitives;
1426
+ const labels = geometry
1427
+ .filter((node) => node.tags.includes("active-frame") || node.tags.includes("contact-point"))
1428
+ .map((node) => ({
1429
+ kind: "label",
1430
+ name: `${runtimePrefix}:${node.id}:label`,
1431
+ position: [node.position[0], node.position[1] + Math.max(0.08, node.scale[1] * 0.5 + 0.08), node.position[2]],
1432
+ text: node.tags.includes("contact-point") ? node.source : `${node.source} ${node.tags.find((tag) => tag.startsWith("frame:")) ?? ""}`.trim(),
1433
+ color: node.color,
1434
+ background: "#07111f",
1435
+ size: 0.12,
1436
+ runtime: {
1437
+ id: `${runtimePrefix}:${node.id}:label`,
1438
+ mutable: true,
1439
+ tags: ["debug", "game-runtime", "label", node.source, ...node.tags]
1440
+ },
1441
+ debug: {
1442
+ source: node.source,
1443
+ tags: node.tags,
1444
+ aabb: node.aabb
1445
+ }
1446
+ }));
1447
+ return [...primitives, ...labels];
1448
+ }
1449
+ function publicGameEffectInstance(effect) {
1450
+ const { attachment: _attachment, ...publicEffect } = effect;
1451
+ return { ...publicEffect };
1452
+ }
1453
+ function resolveEffectAttachmentPosition(attachment, fallback) {
1454
+ if (!attachment?.getPosition)
1455
+ return fallback;
1456
+ return addVec3(attachment.getPosition(), vec3(attachment.offset, [0, 0, 0]));
1457
+ }
1458
+ function normalizeActor(actor) {
1459
+ return {
1460
+ id: actor.id,
1461
+ team: actor.team ?? actor.id,
1462
+ position: vec3(actor.position, [0, 0, 0]),
1463
+ facing: actor.facing ?? 1,
1464
+ health: actor.health ?? 100,
1465
+ guard: actor.guard ?? 100,
1466
+ meter: actor.meter ?? 0,
1467
+ hurtboxes: actor.hurtboxes ?? [{ id: "body", offset: [0, 0.85, 0], size: [0.62, 1.55, 0.48] }],
1468
+ guardboxes: actor.guardboxes ?? [{ id: "guard", offset: [0.22, 0.92, 0], size: [0.54, 1.35, 0.54] }],
1469
+ pushboxes: actor.pushboxes ?? [{ id: "push", offset: [0, 0.72, 0], size: [0.72, 1.2, 0.52] }],
1470
+ guarding: actor.guarding ?? false,
1471
+ stun: 0,
1472
+ recovery: 0
1473
+ };
1474
+ }
1475
+ function normalizeCombatMove(move) {
1476
+ return {
1477
+ ...move,
1478
+ hitboxes: moveHitboxes(move)
1479
+ };
1480
+ }
1481
+ function moveHitboxes(move) {
1482
+ return move.hitboxes ?? (move.hitbox ? [move.hitbox] : []);
1483
+ }
1484
+ function moveActiveFrames(move, fps) {
1485
+ if (move.activeFrames)
1486
+ return move.activeFrames;
1487
+ if (move.startup !== undefined || move.active !== undefined) {
1488
+ const startupFrames = Math.max(0, Math.round((move.startup ?? 0) * fps));
1489
+ const activeFrames = Math.max(1, Math.round((move.active ?? 1 / fps) * fps));
1490
+ return [startupFrames + 1, startupFrames + activeFrames];
1491
+ }
1492
+ return [1, move.durationFrames ?? 12];
1493
+ }
1494
+ function moveDurationFrames(move, activeFrames, fps) {
1495
+ if (move.durationFrames !== undefined)
1496
+ return move.durationFrames;
1497
+ const secondsMode = move.startup !== undefined || move.active !== undefined;
1498
+ const recoveryFrames = secondsMode ? Math.max(0, Math.round((move.recovery ?? 0) * fps)) : move.recovery ?? 8;
1499
+ return activeFrames[1] + recoveryFrames;
1500
+ }
1501
+ function isGameKinematicBody(fighter) {
1502
+ return typeof fighter.update === "function" && typeof fighter.snapshot === "function";
1503
+ }
1504
+ function syncActorsFromBodies(bodies, actors) {
1505
+ for (const [id, body] of bodies) {
1506
+ const actor = actors.get(id);
1507
+ if (!actor)
1508
+ continue;
1509
+ actor.position = body.position;
1510
+ actor.facing = body.facing;
1511
+ }
1512
+ }
1513
+ function syncBodiesFromActors(bodies, actors) {
1514
+ for (const [id, body] of bodies) {
1515
+ const actor = actors.get(id);
1516
+ if (actor)
1517
+ body.position = actor.position;
1518
+ }
1519
+ }
1520
+ function resolvePushboxSeparation(actors, stageBounds, frame, time, events) {
1521
+ const actorList = [...actors.values()];
1522
+ for (let leftIndex = 0; leftIndex < actorList.length; leftIndex += 1) {
1523
+ for (let rightIndex = leftIndex + 1; rightIndex < actorList.length; rightIndex += 1) {
1524
+ const left = actorList[leftIndex];
1525
+ const right = actorList[rightIndex];
1526
+ if (!left || !right || left.team === right.team)
1527
+ continue;
1528
+ const leftBox = left.pushboxes[0] ? worldBox(left, left.pushboxes[0], false) : undefined;
1529
+ const rightBox = right.pushboxes[0] ? worldBox(right, right.pushboxes[0], false) : undefined;
1530
+ if (!leftBox || !rightBox || !intersects(leftBox, rightBox))
1531
+ continue;
1532
+ const overlap = Math.min(leftBox.max[0] - rightBox.min[0], rightBox.max[0] - leftBox.min[0]);
1533
+ if (overlap <= 0)
1534
+ continue;
1535
+ const direction = left.position[0] <= right.position[0] ? -1 : 1;
1536
+ left.position = clampVec3ToBounds([left.position[0] + direction * overlap * 0.5, left.position[1], left.position[2]], stageBounds);
1537
+ right.position = clampVec3ToBounds([right.position[0] - direction * overlap * 0.5, right.position[1], right.position[2]], stageBounds);
1538
+ events.push({
1539
+ type: "push",
1540
+ frame,
1541
+ time,
1542
+ attackerId: left.id,
1543
+ targetId: right.id,
1544
+ position: [(left.position[0] + right.position[0]) / 2, (left.position[1] + right.position[1]) / 2 + 0.72, (left.position[2] + right.position[2]) / 2]
1545
+ });
1546
+ }
1547
+ }
1548
+ }
1549
+ function actorSnapshot(actor) {
1550
+ return {
1551
+ id: actor.id,
1552
+ team: actor.team,
1553
+ position: actor.position,
1554
+ facing: actor.facing,
1555
+ health: actor.health,
1556
+ guard: actor.guard,
1557
+ meter: actor.meter,
1558
+ hurtboxes: actor.hurtboxes,
1559
+ guardboxes: actor.guardboxes,
1560
+ pushboxes: actor.pushboxes,
1561
+ guarding: actor.guarding,
1562
+ stun: actor.stun,
1563
+ recovery: actor.recovery
1564
+ };
1565
+ }
1566
+ function resolveAttack(attacker, attack, actors, stageBounds, frame, time, events) {
1567
+ for (const target of actors.values()) {
1568
+ if (target.id === attacker.id || target.team === attacker.team || attack.hitTargets.has(target.id))
1569
+ continue;
1570
+ for (const hitbox of moveHitboxes(attack.move)) {
1571
+ const worldHitbox = worldBox(attacker, hitbox, true);
1572
+ const guarded = target.guarding &&
1573
+ attack.move.blockable !== false &&
1574
+ target.guardboxes.some((guardbox) => intersects(worldHitbox, worldBox(target, guardbox, false)));
1575
+ const hurt = target.hurtboxes.some((hurtbox) => intersects(worldHitbox, worldBox(target, hurtbox, false)));
1576
+ if (!guarded && !hurt)
1577
+ continue;
1578
+ attack.hitTargets.add(target.id);
1579
+ const position = [
1580
+ (attacker.position[0] + target.position[0]) / 2,
1581
+ (attacker.position[1] + target.position[1]) / 2 + 0.9,
1582
+ (attacker.position[2] + target.position[2]) / 2
1583
+ ];
1584
+ if (guarded) {
1585
+ const guardDamage = attack.move.guardDamage ?? Math.ceil((attack.move.damage ?? 8) * 0.4);
1586
+ target.guard = Math.max(0, target.guard - guardDamage);
1587
+ target.stun = Math.max(target.stun, attack.move.blockStun ?? 8);
1588
+ events.push({
1589
+ type: "blocked",
1590
+ frame,
1591
+ time,
1592
+ attackerId: attacker.id,
1593
+ targetId: target.id,
1594
+ moveId: attack.move.id,
1595
+ guardDamage,
1596
+ hitStop: attack.move.hitStop ?? 0.045,
1597
+ stun: target.stun,
1598
+ position
1599
+ });
1600
+ }
1601
+ else {
1602
+ const damage = attack.move.damage ?? 8;
1603
+ const baseKnockback = vec3Flexible(attack.move.knockback, [0.08, 0, 0]);
1604
+ const knockback = [baseKnockback[0] * attacker.facing, baseKnockback[1], baseKnockback[2]];
1605
+ target.health = Math.max(0, target.health - damage);
1606
+ target.stun = Math.max(target.stun, attack.move.hitStun ?? 12);
1607
+ target.recovery = Math.max(target.recovery, attack.move.recovery ?? 8);
1608
+ target.position = clampVec3ToBounds(addVec3(target.position, knockback), stageBounds);
1609
+ attacker.meter += attack.move.meterGain ?? damage * 0.15;
1610
+ events.push({
1611
+ type: "hit",
1612
+ frame,
1613
+ time,
1614
+ attackerId: attacker.id,
1615
+ targetId: target.id,
1616
+ moveId: attack.move.id,
1617
+ damage,
1618
+ hitStop: attack.move.hitStop ?? 0.06,
1619
+ stun: target.stun,
1620
+ knockback,
1621
+ position
1622
+ });
1623
+ }
1624
+ return;
1625
+ }
1626
+ }
1627
+ }
1628
+ function worldBox(actor, box, mirrorX) {
1629
+ const offset = vec3(box.offset ?? box.center, [0, 0, 0]);
1630
+ const signedOffset = [offset[0] * (mirrorX ? actor.facing : 1), offset[1], offset[2]];
1631
+ return aabb(addVec3(actor.position, signedOffset), box.size);
1632
+ }
1633
+ function normalizeReplayEvents(events) {
1634
+ return [...events]
1635
+ .map((event) => ({
1636
+ frame: Math.max(0, Math.floor(event.frame)),
1637
+ time: Math.max(0, event.time),
1638
+ type: event.type,
1639
+ binding: event.binding
1640
+ }))
1641
+ .sort((a, b) => a.frame - b.frame || a.time - b.time || a.binding.localeCompare(b.binding) || a.type.localeCompare(b.type));
1642
+ }
1643
+ function replayChecksum(events, seed, fps) {
1644
+ let hash = 2166136261 ^ seed ^ Math.floor(fps * 1000);
1645
+ for (const event of events) {
1646
+ const chunk = `${event.frame}:${event.time.toFixed(6)}:${event.type}:${event.binding}`;
1647
+ for (let index = 0; index < chunk.length; index += 1) {
1648
+ hash ^= chunk.charCodeAt(index);
1649
+ hash = Math.imul(hash, 16777619);
1650
+ }
1651
+ }
1652
+ return (hash >>> 0).toString(16).padStart(8, "0");
1653
+ }
1654
+ function colliderBase(options, fallbackDimension) {
1655
+ return {
1656
+ id: options.id,
1657
+ center: vec3(options.center ?? options.offset, [0, 0, 0]),
1658
+ offset: options.offset,
1659
+ tags: options.tags ?? [],
1660
+ dimension: options.dimension ?? fallbackDimension,
1661
+ sensor: options.sensor ?? false
1662
+ };
1663
+ }
1664
+ function debugGeometryFromCollider(collider, options, index) {
1665
+ const aabbBox = gameColliderAabb(collider);
1666
+ return {
1667
+ kind: "aura-game-debug-geometry",
1668
+ id: collider.id ?? `${options.source ?? collider.kind}:${index}`,
1669
+ primitive: collider.kind,
1670
+ position: collider.center,
1671
+ scale: debugGeometryScale(collider),
1672
+ radius: collider.kind === "sphere" || collider.kind === "capsule" ? collider.radius : undefined,
1673
+ height: collider.kind === "capsule" ? collider.height : undefined,
1674
+ axis: collider.kind === "capsule" ? collider.axis : undefined,
1675
+ plane: collider.kind === "rect" ? collider.plane : undefined,
1676
+ color: options.color ?? defaultDebugColor(options.source ?? collider.kind),
1677
+ opacity: options.opacity ?? 0.35,
1678
+ wireframe: options.wireframe ?? true,
1679
+ source: options.source ?? collider.kind,
1680
+ tags: collider.tags,
1681
+ aabb: aabbBox
1682
+ };
1683
+ }
1684
+ function debugGeometryScale(collider) {
1685
+ if (collider.kind === "box")
1686
+ return collider.size;
1687
+ if (collider.kind === "sphere") {
1688
+ const diameter = collider.radius * 2;
1689
+ return [diameter, diameter, collider.dimension === 2 ? 0 : diameter];
1690
+ }
1691
+ if (collider.kind === "capsule")
1692
+ return gameColliderAabb(collider).size;
1693
+ if (collider.plane === "xz")
1694
+ return [collider.size[0], 0, collider.size[1]];
1695
+ if (collider.plane === "yz")
1696
+ return [0, collider.size[0], collider.size[1]];
1697
+ return [collider.size[0], collider.size[1], 0];
1698
+ }
1699
+ function debugScenePrimitive(primitive) {
1700
+ return primitive === "rect" ? "plane" : primitive;
1701
+ }
1702
+ function debugSceneScale(node) {
1703
+ if (node.primitive === "sphere")
1704
+ return node.radius ? node.radius * 2 : node.scale;
1705
+ return node.scale;
1706
+ }
1707
+ function defaultDebugColor(source) {
1708
+ if (source.includes("hurt"))
1709
+ return "#47d16c";
1710
+ if (source.includes("guard"))
1711
+ return "#5cc8ff";
1712
+ if (source.includes("push"))
1713
+ return "#ffd166";
1714
+ if (source.includes("hit"))
1715
+ return "#ff5c8a";
1716
+ if (source.includes("sensor"))
1717
+ return "#b78cff";
1718
+ return "#7dd3fc";
1719
+ }
1720
+ function aabb(center, size) {
1721
+ const half = [size[0] / 2, size[1] / 2, size[2] / 2];
1722
+ return {
1723
+ center,
1724
+ size,
1725
+ min: [center[0] - half[0], center[1] - half[1], center[2] - half[2]],
1726
+ max: [center[0] + half[0], center[1] + half[1], center[2] + half[2]]
1727
+ };
1728
+ }
1729
+ function intersects(a, b) {
1730
+ return (a.min[0] <= b.max[0] &&
1731
+ a.max[0] >= b.min[0] &&
1732
+ a.min[1] <= b.max[1] &&
1733
+ a.max[1] >= b.min[1] &&
1734
+ a.min[2] <= b.max[2] &&
1735
+ a.max[2] >= b.min[2]);
1736
+ }
1737
+ function vec3(value, fallback) {
1738
+ return value ? [value[0], value[1], value[2]] : fallback;
1739
+ }
1740
+ function vec3Flexible(value, fallback) {
1741
+ if (!value)
1742
+ return fallback;
1743
+ return value.length === 2 ? [value[0], value[1], fallback[2]] : [value[0], value[1], value[2]];
1744
+ }
1745
+ function vec2(value, fallback) {
1746
+ return value ? [value[0], value[1]] : fallback;
1747
+ }
1748
+ function size3(value, fallback) {
1749
+ if (!value)
1750
+ return fallback;
1751
+ return value.length === 2 ? [value[0], value[1], 0] : [value[0], value[1], value[2]];
1752
+ }
1753
+ function kinematicSizeFromCollider(collider) {
1754
+ if (!collider)
1755
+ return undefined;
1756
+ return gameColliderAabb(collider).size;
1757
+ }
1758
+ function addVec3(a, b) {
1759
+ return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
1760
+ }
1761
+ function scaleVec3(value, scale) {
1762
+ return [value[0] * scale, value[1] * scale, value[2] * scale];
1763
+ }
1764
+ function normalizeVec3(value) {
1765
+ const length = Math.hypot(value[0], value[1], value[2]);
1766
+ return length <= 0.0001 ? [0, 0, 0] : [value[0] / length, value[1] / length, value[2] / length];
1767
+ }
1768
+ function clamp(value, min, max) {
1769
+ return Math.max(min, Math.min(max, value));
1770
+ }
1771
+ function clampVec3ToBounds(value, bounds) {
1772
+ return [
1773
+ clamp(value[0], bounds.minX ?? Number.NEGATIVE_INFINITY, bounds.maxX ?? Number.POSITIVE_INFINITY),
1774
+ clamp(value[1], bounds.minY ?? Number.NEGATIVE_INFINITY, bounds.maxY ?? Number.POSITIVE_INFINITY),
1775
+ clamp(value[2], bounds.minZ ?? Number.NEGATIVE_INFINITY, bounds.maxZ ?? Number.POSITIVE_INFINITY)
1776
+ ];
1777
+ }
1778
+ function round(value, places) {
1779
+ const scale = 10 ** places;
1780
+ return Math.round(value * scale) / scale;
1781
+ }
1782
+ function applyDeadzone(value, deadzone, mode = "axial") {
1783
+ const nextDeadzone = clamp(deadzone, 0, 0.99);
1784
+ const magnitude = Math.abs(value);
1785
+ if (magnitude < nextDeadzone)
1786
+ return 0;
1787
+ if (mode === "scaled")
1788
+ return Math.sign(value) * clamp((magnitude - nextDeadzone) / (1 - nextDeadzone), 0, 1);
1789
+ return value;
1790
+ }
1791
+ function gamepadButtonNames(index, gamepadIndex) {
1792
+ const names = [
1793
+ "GamepadA",
1794
+ "GamepadB",
1795
+ "GamepadX",
1796
+ "GamepadY",
1797
+ "GamepadLB",
1798
+ "GamepadRB",
1799
+ "GamepadLT",
1800
+ "GamepadRT",
1801
+ "GamepadBack",
1802
+ "GamepadStart",
1803
+ "GamepadLStick",
1804
+ "GamepadRStick",
1805
+ "GamepadDPadUp",
1806
+ "GamepadDPadDown",
1807
+ "GamepadDPadLeft",
1808
+ "GamepadDPadRight"
1809
+ ];
1810
+ return [`GamepadButton${index}`, `Gamepad${gamepadIndex}:Button${index}`, names[index] ?? `GamepadButton${index}`];
1811
+ }
1812
+ function defaultEffectColor(kind) {
1813
+ if (kind === "block-spark")
1814
+ return "#8ee7ff";
1815
+ if (kind === "impact-decal")
1816
+ return "#ff8b5c";
1817
+ if (kind === "ground-dust")
1818
+ return "#c7b38f";
1819
+ if (kind === "dash-trail")
1820
+ return "#62f6c8";
1821
+ if (kind === "slash-trail")
1822
+ return "#d7fbff";
1823
+ if (kind === "aura-burst")
1824
+ return "#5cff87";
1825
+ if (kind === "shockwave" || kind === "ring-shockwave")
1826
+ return "#ffd166";
1827
+ if (kind === "impact-flash" || kind === "super-flash")
1828
+ return "#ffffff";
1829
+ return "#ffb84d";
1830
+ }
1831
+ function defaultEffectDuration(kind) {
1832
+ if (kind === "impact-decal")
1833
+ return 0.7;
1834
+ if (kind === "ground-dust")
1835
+ return 0.34;
1836
+ if (kind === "dash-trail")
1837
+ return 0.28;
1838
+ if (kind === "slash-trail")
1839
+ return 0.18;
1840
+ if (kind === "aura-burst")
1841
+ return 0.9;
1842
+ if (kind === "shockwave" || kind === "ring-shockwave")
1843
+ return 0.42;
1844
+ if (kind === "impact-flash")
1845
+ return 0.12;
1846
+ if (kind === "super-flash")
1847
+ return 0.2;
1848
+ return 0.22;
1849
+ }
1850
+ function defaultEffectRadius(kind) {
1851
+ if (kind === "impact-decal")
1852
+ return 0.42;
1853
+ if (kind === "aura-burst")
1854
+ return 1.4;
1855
+ if (kind === "shockwave" || kind === "ring-shockwave")
1856
+ return 1.05;
1857
+ if (kind === "dash-trail")
1858
+ return 0.52;
1859
+ if (kind === "slash-trail")
1860
+ return 0.62;
1861
+ if (kind === "ground-dust")
1862
+ return 0.36;
1863
+ if (kind === "super-flash")
1864
+ return 1.2;
1865
+ return 0.28;
1866
+ }
1867
+ function effectToSceneNode(effect) {
1868
+ const life = Math.max(0, 1 - effect.age / Math.max(0.001, effect.duration));
1869
+ const ring = effect.kind === "shockwave" || effect.kind === "ring-shockwave";
1870
+ const trail = effect.kind === "dash-trail" || effect.kind === "slash-trail";
1871
+ const scalarScale = Math.max(0.04, effect.radius * (ring ? 1.4 - life * 0.4 : life));
1872
+ const trailScale = [
1873
+ Math.max(0.08, effect.radius * life * 1.6),
1874
+ Math.max(0.02, effect.radius * life * 0.18),
1875
+ Math.max(0.04, effect.radius * life * 0.32)
1876
+ ];
1877
+ if (effect.kind === "aura-burst") {
1878
+ return {
1879
+ kind: "effect",
1880
+ effect: "particles",
1881
+ name: effect.id,
1882
+ color: effect.color,
1883
+ intensity: effect.intensity * life,
1884
+ particleCount: Math.round(160 + effect.intensity * 220),
1885
+ emitter: "swirl",
1886
+ radius: effect.radius,
1887
+ height: 1.4,
1888
+ materialMode: "additive-glow"
1889
+ };
1890
+ }
1891
+ return {
1892
+ kind: "primitive",
1893
+ primitive: ring ? "torus" : trail ? "box" : "sphere",
1894
+ name: effect.id,
1895
+ position: effect.position,
1896
+ scale: trail ? trailScale : scalarScale,
1897
+ material: {
1898
+ color: effect.color,
1899
+ emissive: effect.color,
1900
+ emissiveIntensity: effect.intensity * life,
1901
+ opacity: life
1902
+ }
1903
+ };
1904
+ }
1905
+ export function createGameHudHealthBinding(options) {
1906
+ return createGameHudBinding({
1907
+ binding: "health",
1908
+ id: options.id ?? `hud:${options.actorId}:health`,
1909
+ label: options.label ?? `${options.actorId} health`,
1910
+ source: "combat",
1911
+ targetId: options.actorId,
1912
+ valuePath: options.valuePath ?? `combat.actors.${options.actorId}.health`,
1913
+ maxPath: options.maxPath ?? "rules.maxHealth",
1914
+ format: "percent",
1915
+ a11yLabel: options.a11yLabel ?? `${options.actorId} health value`,
1916
+ visibleWhen: options.visibleWhen
1917
+ });
1918
+ }
1919
+ export function createGameHudMeterBinding(options) {
1920
+ return createGameHudBinding({
1921
+ binding: "meter",
1922
+ id: options.id ?? `hud:${options.actorId}:meter`,
1923
+ label: options.label ?? `${options.actorId} meter`,
1924
+ source: "combat",
1925
+ targetId: options.actorId,
1926
+ valuePath: options.valuePath ?? `combat.actors.${options.actorId}.meter`,
1927
+ maxPath: options.maxPath ?? "rules.maxMeter",
1928
+ format: "percent",
1929
+ a11yLabel: options.a11yLabel ?? `${options.actorId} super meter value`,
1930
+ visibleWhen: options.visibleWhen
1931
+ });
1932
+ }
1933
+ export function createGameHudTimerBinding(options = {}) {
1934
+ return createGameHudBinding({
1935
+ binding: "timer",
1936
+ id: options.id ?? "hud:round:timer",
1937
+ label: options.label ?? "round timer",
1938
+ source: "round",
1939
+ valuePath: options.valuePath ?? "round.timeRemaining",
1940
+ format: "clock",
1941
+ a11yLabel: options.a11yLabel ?? "round timer",
1942
+ visibleWhen: options.visibleWhen
1943
+ });
1944
+ }
1945
+ export function createGameHudComboBinding(options = {}) {
1946
+ const targetId = options.actorId;
1947
+ return createGameHudBinding({
1948
+ binding: "combo",
1949
+ id: options.id ?? (targetId ? `hud:${targetId}:combo` : "hud:combo"),
1950
+ label: options.label ?? (targetId ? `${targetId} combo` : "combo counter"),
1951
+ source: "combat",
1952
+ targetId,
1953
+ valuePath: options.valuePath ?? (targetId ? `combat.actors.${targetId}.combo` : "combat.combo"),
1954
+ format: "number",
1955
+ a11yLabel: options.a11yLabel ?? (targetId ? `${targetId} combo counter` : "combo counter"),
1956
+ visibleWhen: options.visibleWhen
1957
+ });
1958
+ }
1959
+ export function createGameHudRoundBinding(options = {}) {
1960
+ return createGameHudBinding({
1961
+ binding: "round",
1962
+ id: options.id ?? "hud:round:index",
1963
+ label: options.label ?? "round",
1964
+ source: "round",
1965
+ valuePath: options.valuePath ?? "round.index",
1966
+ format: "number",
1967
+ a11yLabel: options.a11yLabel ?? "current round",
1968
+ visibleWhen: options.visibleWhen
1969
+ });
1970
+ }
1971
+ export function createGameHudDebugToggleBinding(options = {}) {
1972
+ return createGameHudBinding({
1973
+ binding: "debug-toggle",
1974
+ id: options.id ?? "hud:debug:toggle",
1975
+ label: options.label ?? "debug overlay",
1976
+ source: "debug",
1977
+ valuePath: options.statePath ?? "debug.visible",
1978
+ format: "boolean",
1979
+ a11yLabel: options.a11yLabel ?? "debug overlay toggle",
1980
+ debugOnly: true,
1981
+ interactive: true,
1982
+ visibleWhen: options.visibleWhen ?? options.action
1983
+ });
1984
+ }
1985
+ export function createGameHudBindings(bindings) {
1986
+ return [...bindings];
1987
+ }
1988
+ export function createGameHudSnapshot(options) {
1989
+ const combatSnapshot = options.combat && "kind" in options.combat && options.combat.kind === "aura-game-combat-world"
1990
+ ? options.combat
1991
+ : options.combat && "snapshot" in options.combat
1992
+ ? options.combat.snapshot()
1993
+ : undefined;
1994
+ const inputSnapshot = options.input && "kind" in options.input && options.input.kind === "aura-game-input-snapshot"
1995
+ ? options.input
1996
+ : options.input && "snapshot" in options.input
1997
+ ? options.input.snapshot()
1998
+ : undefined;
1999
+ const events = options.events ?? combatSnapshot?.events ?? [];
2000
+ const data = createGameHudDataContext({
2001
+ combat: combatSnapshot,
2002
+ round: options.round,
2003
+ rules: options.rules,
2004
+ input: inputSnapshot,
2005
+ runtime: options.runtime,
2006
+ debug: options.debug,
2007
+ appState: options.appState
2008
+ });
2009
+ const values = options.bindings.map((binding) => {
2010
+ const value = readGameValuePath(data, binding.valuePath);
2011
+ const max = binding.maxPath ? readGameValuePath(data, binding.maxPath) : undefined;
2012
+ const changed = gameHudBindingChanged(binding, events);
2013
+ return {
2014
+ kind: "aura-game-hud-value",
2015
+ id: binding.id,
2016
+ binding: binding.binding,
2017
+ label: binding.label,
2018
+ source: binding.source,
2019
+ targetId: binding.targetId,
2020
+ valuePath: binding.valuePath,
2021
+ value,
2022
+ max,
2023
+ formatted: formatGameHudValue(value, max, binding.format),
2024
+ changed,
2025
+ debugOnly: binding.debugOnly,
2026
+ interactive: binding.interactive,
2027
+ a11yLabel: binding.a11yLabel
2028
+ };
2029
+ });
2030
+ return {
2031
+ kind: "aura-game-hud-snapshot",
2032
+ frame: combatSnapshot?.frame ?? inputSnapshot?.frame ?? 0,
2033
+ time: combatSnapshot?.time ?? inputSnapshot?.time ?? 0,
2034
+ values,
2035
+ changedIds: values.filter((value) => value.changed).map((value) => value.id),
2036
+ events
2037
+ };
2038
+ }
2039
+ export function createGameAccessibilityLabel(options) {
2040
+ return {
2041
+ kind: "aura-game-accessibility-source",
2042
+ feature: "label",
2043
+ id: options.id ?? `a11y:${options.targetId}:label`,
2044
+ owner: "app",
2045
+ label: options.label,
2046
+ targetId: options.targetId,
2047
+ role: options.role ?? "status",
2048
+ actions: [],
2049
+ source: "dom",
2050
+ evidence: options.live
2051
+ ? "App owns an aria-live label for this gameplay target."
2052
+ : "App owns a readable label for this gameplay target."
2053
+ };
2054
+ }
2055
+ export function createGameAccessibilityFocus(options) {
2056
+ return {
2057
+ kind: "aura-game-accessibility-source",
2058
+ feature: "focus",
2059
+ id: options.id ?? `a11y:${options.scopeId}:focus`,
2060
+ owner: "app",
2061
+ label: options.label ?? `${options.scopeId} focus scope`,
2062
+ targetId: options.scopeId,
2063
+ actions: options.targets ?? [],
2064
+ source: "dom",
2065
+ evidence: "App owns keyboard focus order and focus restoration for this gameplay UI scope."
2066
+ };
2067
+ }
2068
+ export function createGameReducedMotionSource(options = {}) {
2069
+ return {
2070
+ kind: "aura-game-accessibility-source",
2071
+ feature: "reduced-motion",
2072
+ id: options.id ?? "a11y:prefers-reduced-motion",
2073
+ owner: "shared",
2074
+ label: options.label ?? "prefers reduced motion",
2075
+ actions: [],
2076
+ enabled: options.enabled,
2077
+ source: "media-query",
2078
+ evidence: "App reads the user preference; Aura3D camera and effects helpers consume reducedMotion flags."
2079
+ };
2080
+ }
2081
+ export function createGameReducedFlashSource(options = {}) {
2082
+ return {
2083
+ kind: "aura-game-accessibility-source",
2084
+ feature: "reduced-flash",
2085
+ id: options.id ?? "a11y:reduced-flash",
2086
+ owner: "shared",
2087
+ label: options.label ?? "reduced flash",
2088
+ actions: [],
2089
+ enabled: options.enabled,
2090
+ source: "app-state",
2091
+ evidence: "App stores the flash preference; Aura3D effects helpers consume reducedFlash flags."
2092
+ };
2093
+ }
2094
+ export function createGameHighContrastSource(options = {}) {
2095
+ return {
2096
+ kind: "aura-game-accessibility-source",
2097
+ feature: "high-contrast",
2098
+ id: options.id ?? "a11y:high-contrast",
2099
+ owner: "app",
2100
+ label: options.label ?? "high contrast HUD",
2101
+ actions: [],
2102
+ enabled: options.enabled,
2103
+ source: "app-state",
2104
+ evidence: "App owns high-contrast CSS/theme state for HUD, menus, labels, and focus rings."
2105
+ };
2106
+ }
2107
+ export function createGamePauseControlsSource(options = {}) {
2108
+ const actions = options.actions ?? ["pause", "Escape", "GamepadStart"];
2109
+ return {
2110
+ kind: "aura-game-accessibility-source",
2111
+ feature: "pause-controls",
2112
+ id: options.id ?? "a11y:pause-controls",
2113
+ owner: "shared",
2114
+ label: options.label ?? "pause and resume controls",
2115
+ targetId: options.menuId,
2116
+ actions: [...actions, ...(options.resumeActions ?? [])],
2117
+ source: "input",
2118
+ evidence: "App maps pause/resume input and calls Aura3D app.pause(), app.resume(), or app.step() without remounting the scene."
2119
+ };
2120
+ }
2121
+ export function createGameAccessibilityRuntimeSettings(sources = [], options = {}) {
2122
+ const sourceEnabled = (feature) => sources.some((source) => source.feature === feature && source.enabled === true);
2123
+ const reducedMotion = options.reducedMotion ?? sourceEnabled("reduced-motion");
2124
+ const reducedFlash = options.reducedFlash ?? sourceEnabled("reduced-flash");
2125
+ const highContrast = options.highContrast ?? sourceEnabled("high-contrast");
2126
+ return {
2127
+ kind: "aura-game-accessibility-runtime-settings",
2128
+ reducedMotion,
2129
+ reducedFlash,
2130
+ highContrast,
2131
+ camera: { reducedMotion },
2132
+ effects: { reducedMotion, reducedFlash },
2133
+ evidence: [
2134
+ reducedMotion
2135
+ ? "Reduced-motion preference is forwarded to game.cameraDirector({ reducedMotion: true }) and game.effects({ reducedMotion: true })."
2136
+ : "Camera and effects keep normal motion because reduced-motion is not enabled.",
2137
+ reducedFlash
2138
+ ? "Reduced-flash preference is forwarded to game.effects({ reducedFlash: true }) to cap flash intensity."
2139
+ : "Effects keep normal flash intensity because reduced-flash is not enabled.",
2140
+ highContrast
2141
+ ? "High-contrast preference is available for HUD/menu rendering without querying WebGL state."
2142
+ : "HUD/menu rendering can use the default contrast theme."
2143
+ ]
2144
+ };
2145
+ }
2146
+ function createGameHudDataContext(options) {
2147
+ const actors = {};
2148
+ for (const actor of options.combat?.actors ?? [])
2149
+ actors[actor.id] = actor;
2150
+ return {
2151
+ combat: {
2152
+ ...(options.combat ?? {}),
2153
+ actors,
2154
+ events: options.combat?.events ?? []
2155
+ },
2156
+ round: options.round ?? {},
2157
+ rules: {
2158
+ maxHealth: 100,
2159
+ maxGuard: 100,
2160
+ maxMeter: 100,
2161
+ ...(options.rules ?? {})
2162
+ },
2163
+ input: options.input ?? {},
2164
+ runtime: options.runtime ?? {},
2165
+ debug: options.debug ?? {},
2166
+ appState: options.appState ?? {}
2167
+ };
2168
+ }
2169
+ function readGameValuePath(source, path) {
2170
+ const value = path.split(".").reduce((current, key) => {
2171
+ if (current === undefined || current === null)
2172
+ return undefined;
2173
+ if (typeof current !== "object")
2174
+ return undefined;
2175
+ return current[key];
2176
+ }, source);
2177
+ if (typeof value === "number" || typeof value === "string" || typeof value === "boolean" || value === undefined)
2178
+ return value;
2179
+ return JSON.stringify(value);
2180
+ }
2181
+ function formatGameHudValue(value, max, format) {
2182
+ if (value === undefined)
2183
+ return "";
2184
+ if (format === "boolean")
2185
+ return value ? "on" : "off";
2186
+ if (format === "clock")
2187
+ return formatClockValue(value);
2188
+ if (format === "percent") {
2189
+ const numericValue = Number(value);
2190
+ const numericMax = Number(max ?? 100);
2191
+ if (!Number.isFinite(numericValue) || !Number.isFinite(numericMax) || numericMax <= 0)
2192
+ return String(value);
2193
+ return `${Math.round(clamp(numericValue / numericMax, 0, 1) * 100)}%`;
2194
+ }
2195
+ if (format === "number" && typeof value === "number")
2196
+ return Number.isInteger(value) ? String(value) : value.toFixed(2);
2197
+ return String(value);
2198
+ }
2199
+ function formatClockValue(value) {
2200
+ const seconds = Number(value);
2201
+ if (!Number.isFinite(seconds))
2202
+ return String(value);
2203
+ const minutes = Math.floor(Math.max(0, seconds) / 60);
2204
+ const remaining = Math.floor(Math.max(0, seconds) % 60);
2205
+ return `${minutes}:${String(remaining).padStart(2, "0")}`;
2206
+ }
2207
+ function gameHudBindingChanged(binding, events) {
2208
+ if (!events.length)
2209
+ return false;
2210
+ if (binding.binding === "health" || binding.binding === "combo") {
2211
+ return events.some((event) => event.targetId === binding.targetId && (event.damage ?? 0) > 0);
2212
+ }
2213
+ if (binding.binding === "meter") {
2214
+ return events.some((event) => event.attackerId === binding.targetId && (event.damage ?? event.guardDamage ?? 0) > 0);
2215
+ }
2216
+ if (binding.binding === "debug-toggle")
2217
+ return events.some((event) => event.type === "hit" || event.type === "blocked");
2218
+ return events.length > 0;
2219
+ }
2220
+ function createGameHudBinding(options) {
2221
+ return {
2222
+ kind: "aura-game-hud-binding",
2223
+ owner: "app",
2224
+ debugOnly: options.debugOnly ?? false,
2225
+ interactive: options.interactive ?? false,
2226
+ ...options
2227
+ };
2228
+ }
2229
+ //# sourceMappingURL=GameRuntime.js.map