@auraindustry/aurajs 0.0.7 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/README.md +98 -2
  2. package/benchmarks/perf-thresholds.json +54 -0
  3. package/package.json +4 -7
  4. package/src/asset-pack.mjs +8 -1
  5. package/src/authored-project.mjs +1449 -0
  6. package/src/authored-runtime.mjs +2016 -0
  7. package/src/authoring/avatar-animation-graph.mjs +648 -0
  8. package/src/bin-integrity.mjs +272 -0
  9. package/src/build-contract/assets.mjs +130 -0
  10. package/src/build-contract/capabilities.mjs +116 -0
  11. package/src/build-contract/constants.mjs +6 -0
  12. package/src/build-contract/helpers.mjs +44 -0
  13. package/src/build-contract/web-templates.mjs +5993 -0
  14. package/src/build-contract.mjs +27 -2910
  15. package/src/bundler.mjs +188 -55
  16. package/src/cli.mjs +4840 -1512
  17. package/src/commands/project-authoring.mjs +454 -0
  18. package/src/config.mjs +44 -0
  19. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +3309 -0
  20. package/src/conformance/cases/core-runtime-cases.mjs +1431 -0
  21. package/src/conformance/cases/index.mjs +11 -0
  22. package/src/conformance/cases/scene3d-and-media-cases.mjs +2094 -0
  23. package/src/conformance/cases/systems-and-gameplay-cases.mjs +1776 -0
  24. package/src/conformance/shared.mjs +27 -0
  25. package/src/conformance-runner.mjs +25 -13
  26. package/src/conformance.mjs +619 -4020
  27. package/src/cutscene.mjs +362 -5
  28. package/src/dev-cli-action.mjs +249 -0
  29. package/src/dev-cli-inspect.mjs +92 -0
  30. package/src/dev-cli-state.mjs +80 -0
  31. package/src/external-asset-cache.mjs +587 -0
  32. package/src/external-asset-policy.mjs +217 -0
  33. package/src/external-package-surface.mjs +206 -0
  34. package/src/game-action-runtime.mjs +869 -0
  35. package/src/game-state-runtime.mjs +206 -6
  36. package/src/headless-action.mjs +186 -0
  37. package/src/headless-test/runtime-animation.mjs +1173 -0
  38. package/src/headless-test/runtime-coordinator.mjs +1514 -0
  39. package/src/headless-test/runtime-primitives.mjs +320 -0
  40. package/src/headless-test/runtime-world.mjs +2253 -0
  41. package/src/headless-test.mjs +392 -4298
  42. package/src/host-binary.mjs +342 -14
  43. package/src/icon-discovery.mjs +64 -0
  44. package/src/make-catalog.mjs +109 -0
  45. package/src/make.mjs +197 -0
  46. package/src/package-integrity.mjs +586 -0
  47. package/src/perf-benchmark.mjs +353 -0
  48. package/src/postinstall.mjs +5 -5
  49. package/src/prefabs/index.mjs +34 -0
  50. package/src/prefabs/scene-serialization.mjs +184 -0
  51. package/src/project-importer.mjs +620 -0
  52. package/src/project-registry.mjs +24 -0
  53. package/src/publish-command.mjs +195 -0
  54. package/src/publish-env-example.mjs +83 -0
  55. package/src/publish-validation.mjs +708 -0
  56. package/src/retro/assets/compile.mjs +232 -0
  57. package/src/retro/backend-gba/authoring.mjs +1029 -0
  58. package/src/retro/backend-gba/rom.mjs +363 -0
  59. package/src/retro/backend-gbc/rom.mjs +85 -0
  60. package/src/retro/build.mjs +278 -0
  61. package/src/retro/cli/commands.mjs +292 -0
  62. package/src/retro/cli/templates.mjs +84 -0
  63. package/src/retro/diagnostics/catalog.mjs +110 -0
  64. package/src/retro/diagnostics/emit.mjs +72 -0
  65. package/src/retro/emulator/case-overlay.mjs +64 -0
  66. package/src/retro/emulator/discovery.mjs +158 -0
  67. package/src/retro/emulator/macos-case-overlay.swift +220 -0
  68. package/src/retro/emulator/profiles.mjs +146 -0
  69. package/src/retro/emulator/runner.mjs +289 -0
  70. package/src/retro/frontend/load-project.mjs +98 -0
  71. package/src/retro/index.mjs +30 -0
  72. package/src/retro/ir/build-ir.mjs +108 -0
  73. package/src/retro/runtime-gba/contract.mjs +151 -0
  74. package/src/retro/runtime-gbc/contract.mjs +117 -0
  75. package/src/retro/shared/span.mjs +26 -0
  76. package/src/retro/shared/targets.mjs +64 -0
  77. package/src/retro/validator/check-project.mjs +114 -0
  78. package/src/runtime-hotspot-audit.mjs +707 -0
  79. package/src/scaffold/config.mjs +1000 -0
  80. package/src/scaffold/fs.mjs +56 -0
  81. package/src/scaffold/layout.mjs +318 -0
  82. package/src/scaffold/project-docs.mjs +439 -0
  83. package/src/scaffold.mjs +93 -596
  84. package/src/scene-composition/index.mjs +326 -0
  85. package/src/scene-composition/runtime.mjs +751 -0
  86. package/src/self-hosted-assets.mjs +604 -0
  87. package/src/session-client.mjs +750 -0
  88. package/src/session-native-launcher.mjs +74 -0
  89. package/src/session-protocol.mjs +75 -0
  90. package/src/session-runtime.mjs +321 -0
  91. package/src/session-server.mjs +360 -0
  92. package/src/shader-kits/index.mjs +773 -0
  93. package/src/starter-content-registry.mjs +292 -0
  94. package/src/state-artifacts.mjs +662 -24
  95. package/src/state-dev-reload.mjs +99 -2
  96. package/src/terminal-ui.mjs +245 -0
  97. package/src/web-conformance.mjs +219 -0
  98. package/templates/create/2d/config/gameplay/shooter.config.js +26 -0
  99. package/templates/create/2d/content/gameplay/waves.json +26 -0
  100. package/templates/create/2d/content/registries/.gitkeep +1 -0
  101. package/templates/create/2d/docs/design/.gitkeep +1 -0
  102. package/templates/create/2d/docs/design/loop.md +5 -0
  103. package/templates/create/2d/prefabs/enemies.prefab.js +90 -0
  104. package/templates/create/2d/prefabs/enemy-basic.prefab.js +18 -0
  105. package/templates/create/2d/prefabs/player.prefab.js +36 -0
  106. package/templates/create/2d/prefabs/projectiles.prefab.js +35 -0
  107. package/templates/create/2d/scenes/boot.scene.js +12 -0
  108. package/templates/create/2d/scenes/gameplay.scene.js +230 -0
  109. package/templates/create/2d/scenes/menu.scene.js +9 -0
  110. package/templates/create/2d/src/main.js +6 -185
  111. package/templates/create/2d/src/runtime/app.js +49 -0
  112. package/templates/create/2d/src/runtime/capabilities.js +35 -0
  113. package/templates/create/2d/ui/hud.screen.js +40 -0
  114. package/templates/create/2d/ui/pause.screen.js +149 -0
  115. package/templates/create/2d/ui/settings.screen.js +347 -0
  116. package/templates/create/2d/ui/title.screen.js +13 -0
  117. package/templates/create/2d-adventure/aura.config.json +28 -0
  118. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +14 -0
  119. package/templates/create/2d-adventure/content/gameplay/world.js +46 -0
  120. package/templates/create/2d-adventure/content/registries/.gitkeep +1 -0
  121. package/templates/create/2d-adventure/docs/design/loop.md +5 -0
  122. package/templates/create/2d-adventure/prefabs/player.prefab.js +54 -0
  123. package/templates/create/2d-adventure/prefabs/relic.prefab.js +38 -0
  124. package/templates/create/2d-adventure/prefabs/world.prefab.js +125 -0
  125. package/templates/create/2d-adventure/scenes/gameplay.scene.js +256 -0
  126. package/templates/create/2d-adventure/src/runtime/capabilities.js +34 -0
  127. package/templates/create/2d-adventure/ui/hud.screen.js +60 -0
  128. package/templates/create/2d-survivor/config/gameplay/survivor.config.js +33 -0
  129. package/templates/create/2d-survivor/content/gameplay/spawn-zones.json +29 -0
  130. package/templates/create/2d-survivor/content/registries/.gitkeep +1 -0
  131. package/templates/create/2d-survivor/docs/design/.gitkeep +1 -0
  132. package/templates/create/2d-survivor/docs/design/loop.md +5 -0
  133. package/templates/create/2d-survivor/prefabs/enemies.prefab.js +178 -0
  134. package/templates/create/2d-survivor/prefabs/enemy-swarm.prefab.js +18 -0
  135. package/templates/create/2d-survivor/prefabs/player.prefab.js +42 -0
  136. package/templates/create/2d-survivor/prefabs/projectiles.prefab.js +56 -0
  137. package/templates/create/2d-survivor/scenes/boot.scene.js +12 -0
  138. package/templates/create/2d-survivor/scenes/gameplay.scene.js +314 -0
  139. package/templates/create/2d-survivor/scenes/menu.scene.js +9 -0
  140. package/templates/create/2d-survivor/src/main.js +5 -332
  141. package/templates/create/2d-survivor/src/runtime/app.js +49 -0
  142. package/templates/create/2d-survivor/src/runtime/capabilities.js +35 -0
  143. package/templates/create/2d-survivor/ui/hud.screen.js +45 -0
  144. package/templates/create/2d-survivor/ui/title.screen.js +13 -0
  145. package/templates/create/3d/assets/models/starter-avatar.gltf +184 -0
  146. package/templates/create/3d/config/gameplay/.gitkeep +1 -0
  147. package/templates/create/3d/content/gameplay/checkpoints.json +33 -0
  148. package/templates/create/3d/content/gameplay/course.js +40 -0
  149. package/templates/create/3d/content/registries/.gitkeep +1 -0
  150. package/templates/create/3d/docs/design/.gitkeep +1 -0
  151. package/templates/create/3d/docs/design/loop.md +5 -0
  152. package/templates/create/3d/prefabs/checkpoint.prefab.js +15 -0
  153. package/templates/create/3d/prefabs/player.prefab.js +204 -0
  154. package/templates/create/3d/prefabs/world.prefab.js +112 -0
  155. package/templates/create/3d/scenes/boot.scene.js +12 -0
  156. package/templates/create/3d/scenes/checkpoint.scene.js +9 -0
  157. package/templates/create/3d/scenes/gameplay.scene.js +292 -0
  158. package/templates/create/3d/src/main.js +6 -295
  159. package/templates/create/3d/src/runtime/app.js +49 -0
  160. package/templates/create/3d/src/runtime/capabilities.js +53 -0
  161. package/templates/create/3d/src/runtime/materials.js +34 -0
  162. package/templates/create/3d/src/runtime/state.js +39 -0
  163. package/templates/create/3d/ui/hud.screen.js +75 -0
  164. package/templates/create/3d/ui/pause.screen.js +166 -0
  165. package/templates/create/3d/ui/settings.screen.js +387 -0
  166. package/templates/create/3d-adventure/assets/models/starter-avatar.gltf +184 -0
  167. package/templates/create/3d-adventure/aura.config.json +28 -0
  168. package/templates/create/3d-adventure/config/gameplay/adventure.config.js +9 -0
  169. package/templates/create/3d-adventure/content/gameplay/course.js +62 -0
  170. package/templates/create/3d-adventure/content/registries/.gitkeep +1 -0
  171. package/templates/create/3d-adventure/docs/design/loop.md +5 -0
  172. package/templates/create/3d-adventure/prefabs/player.prefab.js +168 -0
  173. package/templates/create/3d-adventure/prefabs/relic.prefab.js +35 -0
  174. package/templates/create/3d-adventure/prefabs/world.prefab.js +119 -0
  175. package/templates/create/3d-adventure/scenes/gameplay.scene.js +358 -0
  176. package/templates/create/3d-adventure/src/runtime/capabilities.js +56 -0
  177. package/templates/create/3d-adventure/src/runtime/materials.js +39 -0
  178. package/templates/create/3d-adventure/src/runtime/state.js +31 -0
  179. package/templates/create/3d-adventure/ui/hud.screen.js +70 -0
  180. package/templates/create/3d-adventure/ui/pause.screen.js +437 -0
  181. package/templates/create/3d-collectathon/assets/models/starter-avatar.gltf +184 -0
  182. package/templates/create/3d-collectathon/config/gameplay/.gitkeep +1 -0
  183. package/templates/create/3d-collectathon/content/gameplay/collectibles.json +26 -0
  184. package/templates/create/3d-collectathon/content/gameplay/course.js +46 -0
  185. package/templates/create/3d-collectathon/content/registries/.gitkeep +1 -0
  186. package/templates/create/3d-collectathon/docs/design/.gitkeep +1 -0
  187. package/templates/create/3d-collectathon/docs/design/loop.md +5 -0
  188. package/templates/create/3d-collectathon/prefabs/collectible.prefab.js +15 -0
  189. package/templates/create/3d-collectathon/prefabs/player.prefab.js +207 -0
  190. package/templates/create/3d-collectathon/prefabs/world.prefab.js +112 -0
  191. package/templates/create/3d-collectathon/scenes/boot.scene.js +12 -0
  192. package/templates/create/3d-collectathon/scenes/checkpoint.scene.js +9 -0
  193. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +200 -0
  194. package/templates/create/3d-collectathon/src/main.js +5 -355
  195. package/templates/create/3d-collectathon/src/runtime/app.js +49 -0
  196. package/templates/create/3d-collectathon/src/runtime/capabilities.js +53 -0
  197. package/templates/create/3d-collectathon/src/runtime/materials.js +34 -0
  198. package/templates/create/3d-collectathon/src/runtime/state.js +27 -0
  199. package/templates/create/3d-collectathon/ui/hud.screen.js +66 -0
  200. package/templates/create/3d-collectathon/ui/pause.screen.js +13 -0
  201. package/templates/create/blank/config/gameplay/.gitkeep +1 -0
  202. package/templates/create/blank/content/gameplay/.gitkeep +1 -0
  203. package/templates/create/blank/content/registries/.gitkeep +1 -0
  204. package/templates/create/blank/docs/design/.gitkeep +1 -0
  205. package/templates/create/blank/docs/design/loop.md +5 -0
  206. package/templates/create/blank/prefabs/.gitkeep +1 -0
  207. package/templates/create/blank/scenes/.gitkeep +1 -0
  208. package/templates/create/blank/src/runtime/.gitkeep +1 -0
  209. package/templates/create/blank/ui/.gitkeep +1 -0
  210. package/templates/create/deckbuilder-2d/assets/audio/.gitkeep +1 -0
  211. package/templates/create/deckbuilder-2d/assets/fonts/.gitkeep +1 -0
  212. package/templates/create/deckbuilder-2d/assets/sprites/.gitkeep +1 -0
  213. package/templates/create/deckbuilder-2d/assets/starter/README.md +11 -0
  214. package/templates/create/deckbuilder-2d/assets/ui/.gitkeep +1 -0
  215. package/templates/create/deckbuilder-2d/aura.config.json +28 -0
  216. package/templates/create/deckbuilder-2d/config/gameplay/deckbuilder.config.js +26 -0
  217. package/templates/create/deckbuilder-2d/content/cards/guard.card.js +19 -0
  218. package/templates/create/deckbuilder-2d/content/cards/spark.card.js +20 -0
  219. package/templates/create/deckbuilder-2d/content/cards/starter.deck.js +69 -0
  220. package/templates/create/deckbuilder-2d/content/cards/strike.card.js +19 -0
  221. package/templates/create/deckbuilder-2d/content/cards/survey.card.js +20 -0
  222. package/templates/create/deckbuilder-2d/content/encounters/training-battle.encounter.js +14 -0
  223. package/templates/create/deckbuilder-2d/content/encounters/training-battle.js +65 -0
  224. package/templates/create/deckbuilder-2d/content/enemies/training-automaton.enemy.js +48 -0
  225. package/templates/create/deckbuilder-2d/content/gameplay/.gitkeep +1 -0
  226. package/templates/create/deckbuilder-2d/content/registries/cards.registry.js +26 -0
  227. package/templates/create/deckbuilder-2d/content/registries/encounters.registry.js +20 -0
  228. package/templates/create/deckbuilder-2d/content/registries/enemies.registry.js +20 -0
  229. package/templates/create/deckbuilder-2d/content/registries/relics.registry.js +20 -0
  230. package/templates/create/deckbuilder-2d/content/relics/ember-charm.relic.js +18 -0
  231. package/templates/create/deckbuilder-2d/docs/design/loop.md +12 -0
  232. package/templates/create/deckbuilder-2d/prefabs/.gitkeep +1 -0
  233. package/templates/create/deckbuilder-2d/scenes/boot.scene.js +84 -0
  234. package/templates/create/deckbuilder-2d/scenes/gameplay.scene.js +641 -0
  235. package/templates/create/deckbuilder-2d/src/components/.gitkeep +1 -0
  236. package/templates/create/deckbuilder-2d/src/main.js +17 -0
  237. package/templates/create/deckbuilder-2d/src/runtime/capabilities.js +22 -0
  238. package/templates/create/deckbuilder-2d/src/shared/.gitkeep +1 -0
  239. package/templates/create/deckbuilder-2d/src/systems/.gitkeep +1 -0
  240. package/templates/create/deckbuilder-2d/tests/smoke/.gitkeep +1 -0
  241. package/templates/create/deckbuilder-2d/ui/hud.screen.js +80 -0
  242. package/templates/create/deckbuilder-2d/ui/pause.screen.js +146 -0
  243. package/templates/create/deckbuilder-2d/ui/settings.screen.js +342 -0
  244. package/templates/create/local-multiplayer/aura.config.json +41 -0
  245. package/templates/create/local-multiplayer/config/gameplay/local-multiplayer.config.js +26 -0
  246. package/templates/create/local-multiplayer/content/gameplay/room-layout.js +13 -0
  247. package/templates/create/local-multiplayer/content/registries/.gitkeep +1 -0
  248. package/templates/create/local-multiplayer/docs/design/loop.md +16 -0
  249. package/templates/create/local-multiplayer/prefabs/player.prefab.js +99 -0
  250. package/templates/create/local-multiplayer/scenes/boot.scene.js +12 -0
  251. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +472 -0
  252. package/templates/create/local-multiplayer/src/main.js +17 -0
  253. package/templates/create/local-multiplayer/src/runtime/capabilities.js +28 -0
  254. package/templates/create/local-multiplayer/ui/hud.screen.js +65 -0
  255. package/templates/create/shared/src/runtime/project-inspector.js +105 -0
  256. package/templates/create/shared/src/runtime/scene-flow.js +290 -0
  257. package/templates/create/shared/src/runtime/screen-shell.js +222 -0
  258. package/templates/create/shared/src/runtime/ui-forms.js +209 -0
  259. package/templates/create/shared/src/runtime/ui-settings.js +237 -0
  260. package/templates/create/shared/src/runtime/ui-theme.js +352 -0
  261. package/templates/create/shared/src/starter-utils/adventure-objectives.js +102 -0
  262. package/templates/create/shared/src/starter-utils/animation-2d.js +337 -0
  263. package/templates/create/shared/src/starter-utils/avatar-3d.js +404 -0
  264. package/templates/create/shared/src/starter-utils/combat-feedback-2d.js +320 -0
  265. package/templates/create/shared/src/starter-utils/core.js +39 -3
  266. package/templates/create/shared/src/starter-utils/index.js +8 -2
  267. package/templates/create/shared/src/starter-utils/platformer-3d.js +34 -3
  268. package/templates/create/shared/src/starter-utils/triggers.js +662 -0
  269. package/templates/create/shared/src/starter-utils/tween-2d.js +615 -0
  270. package/templates/create/video-cutscene/assets/video/.gitkeep +0 -0
  271. package/templates/create/video-cutscene/aura.config.json +28 -0
  272. package/templates/create/video-cutscene/config/gameplay/.gitkeep +0 -0
  273. package/templates/create/video-cutscene/content/gameplay/.gitkeep +0 -0
  274. package/templates/create/video-cutscene/content/registries/.gitkeep +0 -0
  275. package/templates/create/video-cutscene/docs/design/loop.md +22 -0
  276. package/templates/create/video-cutscene/prefabs/.gitkeep +0 -0
  277. package/templates/create/video-cutscene/scenes/boot.scene.js +11 -0
  278. package/templates/create/video-cutscene/scenes/cutscene.scene.js +113 -0
  279. package/templates/create/video-cutscene/scenes/gameplay.scene.js +50 -0
  280. package/templates/create/video-cutscene/src/main.js +17 -0
  281. package/templates/create/video-cutscene/src/runtime/app.js +52 -0
  282. package/templates/create/video-cutscene/src/runtime/capabilities.js +35 -0
  283. package/templates/create/video-cutscene/src/runtime/state.js +13 -0
  284. package/templates/create/video-cutscene/ui/.gitkeep +0 -0
  285. package/templates/create-bin/play.js +1192 -0
  286. package/templates/make/README.md +46 -0
  287. package/templates/make/catalog.json +51 -0
  288. package/templates/make/component/files/{{MAKE_NAME}}.component.js +20 -0
  289. package/templates/make/component/manifest.json +9 -0
  290. package/templates/make/data/files/{{MAKE_NAME}}.json +14 -0
  291. package/templates/make/data/manifest.json +9 -0
  292. package/templates/make/material/files/{{MAKE_NAME}}.material.json +17 -0
  293. package/templates/make/material/manifest.json +9 -0
  294. package/templates/make/prefab/files/{{MAKE_NAME}}.prefab.js +20 -0
  295. package/templates/make/prefab/manifest.json +9 -0
  296. package/templates/make/scene/files/{{MAKE_NAME}}.scene.js +31 -0
  297. package/templates/make/scene/manifest.json +9 -0
  298. package/templates/make/shader/files/{{MAKE_NAME}}.shader.js +23 -0
  299. package/templates/make/shader/manifest.json +9 -0
  300. package/templates/make/system/files/{{MAKE_NAME}}.system.js +15 -0
  301. package/templates/make/system/manifest.json +9 -0
  302. package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.js +16 -0
  303. package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.json +23 -0
  304. package/templates/make/ui-screen/manifest.json +10 -0
  305. package/templates/make-starters/deckbuilder-2d/card/files/{{MAKE_NAME}}.card.js +22 -0
  306. package/templates/make-starters/deckbuilder-2d/card/manifest.json +9 -0
  307. package/templates/make-starters/deckbuilder-2d/catalog.json +34 -0
  308. package/templates/make-starters/deckbuilder-2d/encounter/files/{{MAKE_NAME}}.encounter.js +18 -0
  309. package/templates/make-starters/deckbuilder-2d/encounter/manifest.json +9 -0
  310. package/templates/make-starters/deckbuilder-2d/enemy/files/{{MAKE_NAME}}.enemy.js +28 -0
  311. package/templates/make-starters/deckbuilder-2d/enemy/manifest.json +9 -0
  312. package/templates/make-starters/deckbuilder-2d/relic/files/{{MAKE_NAME}}.relic.js +23 -0
  313. package/templates/make-starters/deckbuilder-2d/relic/manifest.json +9 -0
  314. package/templates/retro/platformer/README.md +10 -0
  315. package/templates/retro/platformer/assets/retro/assets.json +91 -0
  316. package/templates/retro/platformer/aura.config.json +7 -0
  317. package/templates/retro/platformer/package.json +5 -0
  318. package/templates/retro/platformer/src/main.js +40 -0
  319. package/templates/retro/puzzle-grid/README.md +10 -0
  320. package/templates/retro/puzzle-grid/assets/retro/assets.json +90 -0
  321. package/templates/retro/puzzle-grid/aura.config.json +7 -0
  322. package/templates/retro/puzzle-grid/package.json +5 -0
  323. package/templates/retro/puzzle-grid/src/main.js +29 -0
  324. package/templates/retro/tactics-grid/README.md +10 -0
  325. package/templates/retro/tactics-grid/assets/retro/assets.json +90 -0
  326. package/templates/retro/tactics-grid/aura.config.json +7 -0
  327. package/templates/retro/tactics-grid/package.json +5 -0
  328. package/templates/retro/tactics-grid/src/main.js +35 -0
  329. package/templates/retro/topdown-adventure/README.md +10 -0
  330. package/templates/retro/topdown-adventure/assets/retro/assets.json +95 -0
  331. package/templates/retro/topdown-adventure/aura.config.json +7 -0
  332. package/templates/retro/topdown-adventure/package.json +5 -0
  333. package/templates/retro/topdown-adventure/src/main.js +29 -0
  334. package/templates/skills/aurajs/SKILL.md +61 -5
@@ -0,0 +1,751 @@
1
+ export const SCENE_COMPOSITION_SCHEMA = 'aurajs.scene-composition.v1';
2
+ export const SCENE_COMPOSITION_RUNTIME_SCHEMA = 'aurajs.scene-composition-runtime.v1';
3
+
4
+ const DEFAULT_CAMERA = Object.freeze({
5
+ mode: 'perspective',
6
+ fovDegrees: 60,
7
+ near: 0.1,
8
+ far: 100,
9
+ orthographicHeight: 12,
10
+ position: Object.freeze({ x: 0, y: 3, z: 8 }),
11
+ target: Object.freeze({ x: 0, y: 1, z: 0 }),
12
+ up: Object.freeze({ x: 0, y: 1, z: 0 }),
13
+ });
14
+
15
+ const DEFAULT_AMBIENT_LIGHT = Object.freeze({
16
+ color: Object.freeze({ r: 1, g: 1, b: 1, a: 1 }),
17
+ intensity: 0.24,
18
+ });
19
+
20
+ const DEFAULT_TRANSFORM = Object.freeze({
21
+ position: Object.freeze({ x: 0, y: 0, z: 0 }),
22
+ rotation: Object.freeze({ x: 0, y: 0, z: 0 }),
23
+ scale: Object.freeze({ x: 1, y: 1, z: 1 }),
24
+ });
25
+
26
+ const DEFAULT_BILLBOARD_COLOR = Object.freeze({ r: 1, g: 1, b: 1, a: 1 });
27
+
28
+ function isPlainObject(value) {
29
+ return !!value && typeof value === 'object' && !Array.isArray(value);
30
+ }
31
+
32
+ function cloneValue(value) {
33
+ return value == null ? value : structuredClone(value);
34
+ }
35
+
36
+ function normalizeString(value, fallback = null) {
37
+ if (typeof value !== 'string') return fallback;
38
+ const trimmed = value.trim();
39
+ return trimmed.length > 0 ? trimmed : fallback;
40
+ }
41
+
42
+ function normalizeNumber(value, fallback) {
43
+ return Number.isFinite(Number(value)) ? Number(value) : fallback;
44
+ }
45
+
46
+ function normalizeBoolean(value, fallback = false) {
47
+ return typeof value === 'boolean' ? value : fallback;
48
+ }
49
+
50
+ function normalizeVec3(value, fallback = DEFAULT_TRANSFORM.position) {
51
+ const source = isPlainObject(value) ? value : {};
52
+ return {
53
+ x: normalizeNumber(source.x, fallback.x),
54
+ y: normalizeNumber(source.y, fallback.y),
55
+ z: normalizeNumber(source.z, fallback.z),
56
+ };
57
+ }
58
+
59
+ function normalizeColor(value, fallback = DEFAULT_BILLBOARD_COLOR) {
60
+ const source = isPlainObject(value) ? value : {};
61
+ return {
62
+ r: normalizeNumber(source.r, fallback.r),
63
+ g: normalizeNumber(source.g, fallback.g),
64
+ b: normalizeNumber(source.b, fallback.b),
65
+ a: normalizeNumber(source.a, fallback.a),
66
+ };
67
+ }
68
+
69
+ function normalizeTransform(value) {
70
+ const source = isPlainObject(value) ? value : {};
71
+ return {
72
+ position: normalizeVec3(source.position, DEFAULT_TRANSFORM.position),
73
+ rotation: normalizeVec3(source.rotation, DEFAULT_TRANSFORM.rotation),
74
+ scale: normalizeVec3(source.scale, DEFAULT_TRANSFORM.scale),
75
+ };
76
+ }
77
+
78
+ function normalizeCameraConfig(value) {
79
+ const source = isPlainObject(value) ? value : {};
80
+ const mode = normalizeString(source.mode, DEFAULT_CAMERA.mode);
81
+ return {
82
+ mode: mode === 'orthographic' ? 'orthographic' : 'perspective',
83
+ fovDegrees: normalizeNumber(source.fovDegrees, DEFAULT_CAMERA.fovDegrees),
84
+ near: normalizeNumber(source.near, DEFAULT_CAMERA.near),
85
+ far: normalizeNumber(source.far, DEFAULT_CAMERA.far),
86
+ orthographicHeight: normalizeNumber(
87
+ source.orthographicHeight,
88
+ DEFAULT_CAMERA.orthographicHeight,
89
+ ),
90
+ position: normalizeVec3(source.position, DEFAULT_CAMERA.position),
91
+ target: normalizeVec3(source.target, DEFAULT_CAMERA.target),
92
+ up: normalizeVec3(source.up, DEFAULT_CAMERA.up),
93
+ };
94
+ }
95
+
96
+ function normalizeDirectionalLight(value, fallbackId) {
97
+ const source = isPlainObject(value) ? value : {};
98
+ return {
99
+ id: normalizeString(source.id, fallbackId),
100
+ direction: normalizeVec3(source.direction, { x: -0.4, y: -1, z: -0.3 }),
101
+ color: normalizeColor(source.color, DEFAULT_BILLBOARD_COLOR),
102
+ intensity: normalizeNumber(source.intensity, 1),
103
+ };
104
+ }
105
+
106
+ function normalizePointLight(value, fallbackId) {
107
+ const source = isPlainObject(value) ? value : {};
108
+ return {
109
+ id: normalizeString(source.id, fallbackId),
110
+ position: normalizeVec3(source.position, DEFAULT_TRANSFORM.position),
111
+ color: normalizeColor(source.color, DEFAULT_BILLBOARD_COLOR),
112
+ intensity: normalizeNumber(source.intensity, 1),
113
+ range: normalizeNumber(source.range, 8),
114
+ };
115
+ }
116
+
117
+ function normalizeSpotLight(value, fallbackId) {
118
+ const source = isPlainObject(value) ? value : {};
119
+ return {
120
+ id: normalizeString(source.id, fallbackId),
121
+ position: normalizeVec3(source.position, DEFAULT_TRANSFORM.position),
122
+ direction: normalizeVec3(source.direction, { x: 0, y: -1, z: 0 }),
123
+ color: normalizeColor(source.color, DEFAULT_BILLBOARD_COLOR),
124
+ intensity: normalizeNumber(source.intensity, 1),
125
+ range: normalizeNumber(source.range, 12),
126
+ innerConeDegrees: normalizeNumber(source.innerConeDegrees, 18),
127
+ outerConeDegrees: normalizeNumber(source.outerConeDegrees, 28),
128
+ };
129
+ }
130
+
131
+ function normalizeEntityList(value, normalizeEntry, defaultPrefix) {
132
+ const out = [];
133
+ if (Array.isArray(value)) {
134
+ for (let index = 0; index < value.length; index += 1) {
135
+ const normalized = normalizeEntry(value[index], `${defaultPrefix}-${index + 1}`);
136
+ if (normalized) out.push(normalized);
137
+ }
138
+ return out;
139
+ }
140
+ if (isPlainObject(value)) {
141
+ const keys = Object.keys(value).sort((left, right) => left.localeCompare(right));
142
+ for (const key of keys) {
143
+ const entry = value[key];
144
+ const normalized = normalizeEntry(
145
+ isPlainObject(entry) ? { id: key, ...entry } : { id: key, value: entry },
146
+ key,
147
+ );
148
+ if (normalized) out.push(normalized);
149
+ }
150
+ }
151
+ return out;
152
+ }
153
+
154
+ function normalizeNodeEntry(value, fallbackId) {
155
+ const source = isPlainObject(value) ? value : {};
156
+ const id = normalizeString(source.id, fallbackId);
157
+ if (!id) return null;
158
+ return {
159
+ id,
160
+ parentId: normalizeString(source.parentId, null),
161
+ transform: normalizeTransform(source.transform || source),
162
+ tags: normalizeTags(source.tags),
163
+ metadata: isPlainObject(source.metadata) ? cloneValue(source.metadata) : {},
164
+ };
165
+ }
166
+
167
+ function normalizeMeshEntry(value, fallbackId) {
168
+ const source = isPlainObject(value) ? value : {};
169
+ const id = normalizeString(source.id, fallbackId);
170
+ if (!id) return null;
171
+ return {
172
+ id,
173
+ nodeId: normalizeString(source.nodeId, null),
174
+ mesh: source.mesh ?? source.meshHandle ?? null,
175
+ material: source.material ?? source.materialHandle ?? null,
176
+ visible: source.visible !== false,
177
+ transform: normalizeTransform(source.transform),
178
+ tags: normalizeTags(source.tags),
179
+ metadata: isPlainObject(source.metadata) ? cloneValue(source.metadata) : {},
180
+ };
181
+ }
182
+
183
+ function normalizeBillboardEntry(value, fallbackId) {
184
+ const source = isPlainObject(value) ? value : {};
185
+ const id = normalizeString(source.id, fallbackId);
186
+ if (!id) return null;
187
+ return {
188
+ id,
189
+ nodeId: normalizeString(source.nodeId, null),
190
+ textureHandle: source.textureHandle ?? source.texture ?? null,
191
+ visible: source.visible !== false,
192
+ width: normalizeNumber(source.width, 1),
193
+ height: normalizeNumber(source.height, 1),
194
+ mode: normalizeString(source.mode, 'face') === 'axis' ? 'axis' : 'face',
195
+ color: normalizeColor(source.color, DEFAULT_BILLBOARD_COLOR),
196
+ offset: normalizeVec3(source.offset, DEFAULT_TRANSFORM.position),
197
+ tags: normalizeTags(source.tags),
198
+ metadata: isPlainObject(source.metadata) ? cloneValue(source.metadata) : {},
199
+ };
200
+ }
201
+
202
+ function normalizeAvatarEntry(value, fallbackId) {
203
+ const source = isPlainObject(value) ? value : {};
204
+ const id = normalizeString(source.id, fallbackId);
205
+ if (!id) return null;
206
+ return {
207
+ id,
208
+ nodeId: normalizeString(source.nodeId, null),
209
+ runtime: isAvatarRuntime(source.runtime) ? source.runtime : null,
210
+ factory: typeof source.factory === 'function' ? source.factory : null,
211
+ graph: isPlainObject(source.graph) ? cloneValue(source.graph) : null,
212
+ options: isPlainObject(source.options) ? cloneValue(source.options) : {},
213
+ spawn: isPlainObject(source.spawn) ? cloneValue(source.spawn) : {},
214
+ input: isPlainObject(source.input) ? cloneValue(source.input) : {},
215
+ autoSpawn: source.autoSpawn !== false,
216
+ tags: normalizeTags(source.tags),
217
+ metadata: isPlainObject(source.metadata) ? cloneValue(source.metadata) : {},
218
+ };
219
+ }
220
+
221
+ function normalizeTriggerWatchEntry(value, fallbackId) {
222
+ if (typeof value === 'string') {
223
+ const id = normalizeString(value, fallbackId);
224
+ if (!id) return null;
225
+ return {
226
+ id,
227
+ avatarId: id,
228
+ nodeId: null,
229
+ position: null,
230
+ getPosition: null,
231
+ };
232
+ }
233
+ const source = isPlainObject(value) ? value : {};
234
+ const id = normalizeString(source.id, fallbackId);
235
+ if (!id) return null;
236
+ return {
237
+ id,
238
+ avatarId: normalizeString(source.avatarId, null),
239
+ nodeId: normalizeString(source.nodeId, null),
240
+ position: isPlainObject(source.position)
241
+ ? normalizeVec3(source.position, DEFAULT_TRANSFORM.position)
242
+ : null,
243
+ getPosition: typeof source.getPosition === 'function' ? source.getPosition : null,
244
+ };
245
+ }
246
+
247
+ function normalizeTriggerEntry(value, fallbackId) {
248
+ const source = isPlainObject(value) ? value : {};
249
+ const id = normalizeString(source.id, fallbackId);
250
+ if (!id) return null;
251
+ const shape = isPlainObject(source.shape) ? source.shape : {};
252
+ const type = normalizeString(shape.type, 'box');
253
+ return {
254
+ id,
255
+ nodeId: normalizeString(source.nodeId, null),
256
+ offset: normalizeVec3(source.offset, DEFAULT_TRANSFORM.position),
257
+ shape: {
258
+ type: type === 'sphere' ? 'sphere' : 'box',
259
+ size: normalizeVec3(shape.size, { x: 1, y: 1, z: 1 }),
260
+ radius: normalizeNumber(shape.radius, 0.75),
261
+ },
262
+ watch: normalizeEntityList(source.watch, normalizeTriggerWatchEntry, `${id}-watch`),
263
+ onEnter: typeof source.onEnter === 'function' ? source.onEnter : null,
264
+ onExit: typeof source.onExit === 'function' ? source.onExit : null,
265
+ tags: normalizeTags(source.tags),
266
+ metadata: isPlainObject(source.metadata) ? cloneValue(source.metadata) : {},
267
+ };
268
+ }
269
+
270
+ function normalizeTags(value) {
271
+ if (!Array.isArray(value)) return [];
272
+ return [...new Set(value
273
+ .map((entry) => normalizeString(entry, null))
274
+ .filter(Boolean))]
275
+ .sort((left, right) => left.localeCompare(right));
276
+ }
277
+
278
+ function indexEntities(entries) {
279
+ const byId = new Map();
280
+ for (const entry of entries) {
281
+ byId.set(entry.id, entry);
282
+ }
283
+ return byId;
284
+ }
285
+
286
+ function mergeEntityLists(baseEntries = [], patchEntries = []) {
287
+ const byId = new Map(baseEntries.map((entry) => [entry.id, cloneValue(entry)]));
288
+ for (const entry of patchEntries) {
289
+ byId.set(entry.id, cloneValue(entry));
290
+ }
291
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
292
+ }
293
+
294
+ export function normalizeSceneCompositionConfig(config = {}) {
295
+ if (!isPlainObject(config)) {
296
+ throw new TypeError('Scene composition config must be an object.');
297
+ }
298
+
299
+ const lights = isPlainObject(config.lights) ? config.lights : {};
300
+ return {
301
+ schema: SCENE_COMPOSITION_SCHEMA,
302
+ name: normalizeString(config.name, null),
303
+ clearColor: isPlainObject(config.clearColor)
304
+ ? normalizeColor(config.clearColor, { r: 0.02, g: 0.025, b: 0.03, a: 1 })
305
+ : null,
306
+ camera: normalizeCameraConfig(config.camera),
307
+ lights: {
308
+ ambient: isPlainObject(lights.ambient)
309
+ ? {
310
+ color: normalizeColor(lights.ambient.color, DEFAULT_AMBIENT_LIGHT.color),
311
+ intensity: normalizeNumber(lights.ambient.intensity, DEFAULT_AMBIENT_LIGHT.intensity),
312
+ }
313
+ : {
314
+ color: normalizeColor(DEFAULT_AMBIENT_LIGHT.color),
315
+ intensity: DEFAULT_AMBIENT_LIGHT.intensity,
316
+ },
317
+ directional: normalizeEntityList(lights.directional, normalizeDirectionalLight, 'directional'),
318
+ point: normalizeEntityList(lights.point, normalizePointLight, 'point'),
319
+ spot: normalizeEntityList(lights.spot, normalizeSpotLight, 'spot'),
320
+ },
321
+ nodes: normalizeEntityList(config.nodes, normalizeNodeEntry, 'node')
322
+ .sort((left, right) => left.id.localeCompare(right.id)),
323
+ meshes: normalizeEntityList(config.meshes, normalizeMeshEntry, 'mesh')
324
+ .sort((left, right) => left.id.localeCompare(right.id)),
325
+ billboards: normalizeEntityList(config.billboards, normalizeBillboardEntry, 'billboard')
326
+ .sort((left, right) => left.id.localeCompare(right.id)),
327
+ avatars: normalizeEntityList(config.avatars, normalizeAvatarEntry, 'avatar')
328
+ .sort((left, right) => left.id.localeCompare(right.id)),
329
+ triggers: normalizeEntityList(config.triggers, normalizeTriggerEntry, 'trigger')
330
+ .sort((left, right) => left.id.localeCompare(right.id)),
331
+ metadata: isPlainObject(config.metadata) ? cloneValue(config.metadata) : {},
332
+ };
333
+ }
334
+
335
+ export function mergeSceneCompositionConfig(baseConfig = {}, patchConfig = {}) {
336
+ const base = normalizeSceneCompositionConfig(baseConfig);
337
+ const patch = normalizeSceneCompositionConfig(patchConfig);
338
+ const source = isPlainObject(patchConfig) ? patchConfig : {};
339
+ return {
340
+ schema: SCENE_COMPOSITION_SCHEMA,
341
+ name: Object.prototype.hasOwnProperty.call(source, 'name') ? patch.name : base.name,
342
+ clearColor: Object.prototype.hasOwnProperty.call(source, 'clearColor')
343
+ ? patch.clearColor
344
+ : base.clearColor,
345
+ camera: Object.prototype.hasOwnProperty.call(source, 'camera') ? patch.camera : base.camera,
346
+ lights: Object.prototype.hasOwnProperty.call(source, 'lights') ? patch.lights : base.lights,
347
+ nodes: mergeEntityLists(base.nodes, patch.nodes),
348
+ meshes: mergeEntityLists(base.meshes, patch.meshes),
349
+ billboards: mergeEntityLists(base.billboards, patch.billboards),
350
+ avatars: mergeEntityLists(base.avatars, patch.avatars),
351
+ triggers: mergeEntityLists(base.triggers, patch.triggers),
352
+ metadata: {
353
+ ...cloneValue(base.metadata),
354
+ ...cloneValue(patch.metadata),
355
+ },
356
+ };
357
+ }
358
+
359
+ export function sanitizeSceneCompositionForSerialization(config = {}) {
360
+ const normalized = normalizeSceneCompositionConfig(config);
361
+ return {
362
+ schema: normalized.schema,
363
+ name: normalized.name,
364
+ clearColor: normalized.clearColor,
365
+ camera: normalized.camera,
366
+ lights: normalized.lights,
367
+ nodes: normalized.nodes,
368
+ meshes: normalized.meshes.map((entry) => ({
369
+ ...entry,
370
+ mesh: sanitizeHandle(entry.mesh),
371
+ material: sanitizeHandle(entry.material),
372
+ })),
373
+ billboards: normalized.billboards.map((entry) => ({
374
+ ...entry,
375
+ textureHandle: sanitizeHandle(entry.textureHandle),
376
+ })),
377
+ avatars: normalized.avatars.map((entry) => ({
378
+ id: entry.id,
379
+ nodeId: entry.nodeId,
380
+ graph: sanitizeJsonValue(entry.graph),
381
+ options: sanitizeJsonValue(entry.options),
382
+ spawn: sanitizeJsonValue(entry.spawn),
383
+ input: sanitizeJsonValue(entry.input),
384
+ autoSpawn: entry.autoSpawn,
385
+ tags: entry.tags,
386
+ metadata: sanitizeJsonValue(entry.metadata),
387
+ })),
388
+ triggers: normalized.triggers.map((entry) => ({
389
+ id: entry.id,
390
+ nodeId: entry.nodeId,
391
+ offset: entry.offset,
392
+ shape: entry.shape,
393
+ watch: entry.watch.map((watcher) => ({
394
+ id: watcher.id,
395
+ avatarId: watcher.avatarId,
396
+ nodeId: watcher.nodeId,
397
+ position: watcher.position,
398
+ })),
399
+ tags: entry.tags,
400
+ metadata: sanitizeJsonValue(entry.metadata),
401
+ })),
402
+ metadata: sanitizeJsonValue(normalized.metadata),
403
+ };
404
+ }
405
+
406
+ function sanitizeHandle(value) {
407
+ return Number.isFinite(Number(value)) || typeof value === 'string'
408
+ ? value
409
+ : null;
410
+ }
411
+
412
+ function sanitizeJsonValue(value) {
413
+ if (value == null) return value;
414
+ return JSON.parse(JSON.stringify(value));
415
+ }
416
+
417
+ export function createNodeStateMap(nodes) {
418
+ const nodeMap = new Map();
419
+ for (const definition of nodes) {
420
+ nodeMap.set(definition.id, {
421
+ id: definition.id,
422
+ parentId: definition.parentId,
423
+ local: cloneValue(definition.transform),
424
+ world: cloneValue(definition.transform),
425
+ nodeId: null,
426
+ tags: [...definition.tags],
427
+ metadata: cloneValue(definition.metadata),
428
+ });
429
+ }
430
+ return nodeMap;
431
+ }
432
+
433
+ function topologicallySortNodes(nodeMap) {
434
+ const visited = new Set();
435
+ const stack = [];
436
+
437
+ function visit(nodeId) {
438
+ if (visited.has(nodeId)) return;
439
+ visited.add(nodeId);
440
+ const node = nodeMap.get(nodeId);
441
+ if (!node) return;
442
+ if (node.parentId && nodeMap.has(node.parentId)) {
443
+ visit(node.parentId);
444
+ }
445
+ stack.push(node);
446
+ }
447
+
448
+ for (const nodeId of [...nodeMap.keys()].sort((left, right) => left.localeCompare(right))) {
449
+ visit(nodeId);
450
+ }
451
+ return stack;
452
+ }
453
+
454
+ export function composeWorldTransforms(nodeMap) {
455
+ const ordered = topologicallySortNodes(nodeMap);
456
+ for (const node of ordered) {
457
+ const parent = node.parentId ? nodeMap.get(node.parentId) : null;
458
+ if (!parent) {
459
+ node.world = cloneValue(node.local);
460
+ continue;
461
+ }
462
+ node.world = {
463
+ position: {
464
+ x: parent.world.position.x + (node.local.position.x * parent.world.scale.x),
465
+ y: parent.world.position.y + (node.local.position.y * parent.world.scale.y),
466
+ z: parent.world.position.z + (node.local.position.z * parent.world.scale.z),
467
+ },
468
+ rotation: {
469
+ x: parent.world.rotation.x + node.local.rotation.x,
470
+ y: parent.world.rotation.y + node.local.rotation.y,
471
+ z: parent.world.rotation.z + node.local.rotation.z,
472
+ },
473
+ scale: {
474
+ x: parent.world.scale.x * node.local.scale.x,
475
+ y: parent.world.scale.y * node.local.scale.y,
476
+ z: parent.world.scale.z * node.local.scale.z,
477
+ },
478
+ };
479
+ }
480
+ return nodeMap;
481
+ }
482
+
483
+ function isAvatarRuntime(value) {
484
+ return !!value
485
+ && typeof value === 'object'
486
+ && typeof value.getState === 'function'
487
+ && typeof value.tick === 'function';
488
+ }
489
+
490
+ function configureCamera(aura, camera) {
491
+ const camera3d = aura?.camera3d;
492
+ if (!camera3d) return;
493
+ if (camera.mode === 'orthographic' && typeof camera3d.orthographic === 'function') {
494
+ camera3d.orthographic(camera.orthographicHeight, camera.near, camera.far);
495
+ } else if (typeof camera3d.perspective === 'function') {
496
+ camera3d.perspective(camera.fovDegrees, camera.near, camera.far);
497
+ }
498
+ if (typeof camera3d.setPosition === 'function') {
499
+ camera3d.setPosition(camera.position.x, camera.position.y, camera.position.z);
500
+ }
501
+ if (typeof camera3d.lookAt === 'function') {
502
+ camera3d.lookAt(camera.target.x, camera.target.y, camera.target.z);
503
+ }
504
+ }
505
+
506
+ function configureLights(aura, lights) {
507
+ const light = aura?.light;
508
+ if (!light) return;
509
+ if (typeof light.ambient === 'function') {
510
+ light.ambient(cloneValue(lights.ambient.color), lights.ambient.intensity);
511
+ }
512
+ if (typeof light.directional === 'function') {
513
+ for (const entry of lights.directional) {
514
+ light.directional(cloneValue(entry.direction), cloneValue(entry.color), entry.intensity);
515
+ }
516
+ }
517
+ if (typeof light.point === 'function') {
518
+ for (const entry of lights.point) {
519
+ light.point(cloneValue(entry.position), cloneValue(entry.color), entry.intensity, entry.range);
520
+ }
521
+ }
522
+ if (typeof light.spot === 'function') {
523
+ for (const entry of lights.spot) {
524
+ light.spot({
525
+ position: cloneValue(entry.position),
526
+ direction: cloneValue(entry.direction),
527
+ color: cloneValue(entry.color),
528
+ intensity: entry.intensity,
529
+ range: entry.range,
530
+ innerConeDegrees: entry.innerConeDegrees,
531
+ outerConeDegrees: entry.outerConeDegrees,
532
+ });
533
+ }
534
+ }
535
+ }
536
+
537
+ export function mountSceneNodes(aura, nodeMap) {
538
+ const scene3d = aura?.scene3d;
539
+ if (!scene3d || typeof scene3d.createNode !== 'function') {
540
+ return nodeMap;
541
+ }
542
+ const ordered = topologicallySortNodes(nodeMap);
543
+ for (const node of ordered) {
544
+ node.nodeId = scene3d.createNode(cloneValue(node.local));
545
+ if (node.parentId) {
546
+ const parent = nodeMap.get(node.parentId);
547
+ if (parent?.nodeId && typeof scene3d.setParent === 'function') {
548
+ scene3d.setParent(node.nodeId, parent.nodeId);
549
+ }
550
+ }
551
+ if (typeof scene3d.setLocalTransform === 'function') {
552
+ scene3d.setLocalTransform(node.nodeId, cloneValue(node.local));
553
+ }
554
+ }
555
+ return nodeMap;
556
+ }
557
+
558
+ export function syncRuntimeNodeTransform(aura, nodeState) {
559
+ const scene3d = aura?.scene3d;
560
+ if (!scene3d || !nodeState?.nodeId || typeof scene3d.setLocalTransform !== 'function') return;
561
+ scene3d.setLocalTransform(nodeState.nodeId, cloneValue(nodeState.local));
562
+ }
563
+
564
+ export function createTriggerRuntimeState(definition) {
565
+ return {
566
+ id: definition.id,
567
+ activeWatcherIds: new Set(),
568
+ lastEvent: null,
569
+ enterCount: 0,
570
+ exitCount: 0,
571
+ };
572
+ }
573
+
574
+ export function resolveNodeWorldPosition(nodeMap, nodeId, offset = DEFAULT_TRANSFORM.position) {
575
+ const node = nodeId ? nodeMap.get(nodeId) : null;
576
+ const base = node ? node.world.position : DEFAULT_TRANSFORM.position;
577
+ return {
578
+ x: base.x + offset.x,
579
+ y: base.y + offset.y,
580
+ z: base.z + offset.z,
581
+ };
582
+ }
583
+
584
+ export function resolveAvatarPosition(avatarState) {
585
+ const state = avatarState?.controller?.getState ? avatarState.controller.getState() : null;
586
+ const avatar = state?.avatar || state || {};
587
+ const position = avatar.position || avatar.renderPosition || avatar.visual?.position || null;
588
+ if (!isPlainObject(position)) {
589
+ return null;
590
+ }
591
+ return normalizeVec3(position, DEFAULT_TRANSFORM.position);
592
+ }
593
+
594
+ export function resolveTriggerWatcherPosition(nodeMap, avatars, watcher) {
595
+ if (typeof watcher.getPosition === 'function') {
596
+ const resolved = watcher.getPosition();
597
+ return isPlainObject(resolved) ? normalizeVec3(resolved, DEFAULT_TRANSFORM.position) : null;
598
+ }
599
+ if (watcher.position) {
600
+ return normalizeVec3(watcher.position, DEFAULT_TRANSFORM.position);
601
+ }
602
+ if (watcher.avatarId) {
603
+ const avatarState = avatars.get(watcher.avatarId);
604
+ return resolveAvatarPosition(avatarState);
605
+ }
606
+ if (watcher.nodeId) {
607
+ return resolveNodeWorldPosition(nodeMap, watcher.nodeId);
608
+ }
609
+ return null;
610
+ }
611
+
612
+ function pointInTrigger(position, trigger, origin) {
613
+ const delta = {
614
+ x: position.x - origin.x,
615
+ y: position.y - origin.y,
616
+ z: position.z - origin.z,
617
+ };
618
+ if (trigger.shape.type === 'sphere') {
619
+ const distanceSq = (delta.x * delta.x) + (delta.y * delta.y) + (delta.z * delta.z);
620
+ return distanceSq <= (trigger.shape.radius * trigger.shape.radius);
621
+ }
622
+ return Math.abs(delta.x) <= (trigger.shape.size.x / 2)
623
+ && Math.abs(delta.y) <= (trigger.shape.size.y / 2)
624
+ && Math.abs(delta.z) <= (trigger.shape.size.z / 2);
625
+ }
626
+
627
+ export function evaluateTriggers(nodeMap, avatars, triggerDefinitions, triggerRuntimeState) {
628
+ const events = [];
629
+ for (const trigger of triggerDefinitions) {
630
+ const runtime = triggerRuntimeState.get(trigger.id);
631
+ if (!runtime) continue;
632
+ const origin = resolveNodeWorldPosition(nodeMap, trigger.nodeId, trigger.offset);
633
+ const nextActive = new Set();
634
+ for (const watcher of trigger.watch) {
635
+ const position = resolveTriggerWatcherPosition(nodeMap, avatars, watcher);
636
+ if (!position) continue;
637
+ const inside = pointInTrigger(position, trigger, origin);
638
+ if (!inside) continue;
639
+ nextActive.add(watcher.id);
640
+ if (!runtime.activeWatcherIds.has(watcher.id)) {
641
+ runtime.enterCount += 1;
642
+ runtime.lastEvent = {
643
+ type: 'enter',
644
+ triggerId: trigger.id,
645
+ watcherId: watcher.id,
646
+ position,
647
+ };
648
+ if (typeof trigger.onEnter === 'function') {
649
+ trigger.onEnter(cloneValue(runtime.lastEvent));
650
+ }
651
+ events.push(cloneValue(runtime.lastEvent));
652
+ }
653
+ }
654
+ for (const watcherId of runtime.activeWatcherIds) {
655
+ if (nextActive.has(watcherId)) continue;
656
+ runtime.exitCount += 1;
657
+ runtime.lastEvent = {
658
+ type: 'exit',
659
+ triggerId: trigger.id,
660
+ watcherId,
661
+ position: null,
662
+ };
663
+ if (typeof trigger.onExit === 'function') {
664
+ trigger.onExit(cloneValue(runtime.lastEvent));
665
+ }
666
+ events.push(cloneValue(runtime.lastEvent));
667
+ }
668
+ runtime.activeWatcherIds = nextActive;
669
+ }
670
+ return events;
671
+ }
672
+
673
+ export function buildSceneCompositionSnapshot(state) {
674
+ composeWorldTransforms(state.nodeMap);
675
+ return {
676
+ schema: SCENE_COMPOSITION_RUNTIME_SCHEMA,
677
+ name: state.config.name,
678
+ mounted: state.mounted,
679
+ clearColor: cloneValue(state.config.clearColor),
680
+ camera: cloneValue(state.config.camera),
681
+ lights: cloneValue(state.config.lights),
682
+ nodes: [...state.nodeMap.values()]
683
+ .map((entry) => ({
684
+ id: entry.id,
685
+ parentId: entry.parentId,
686
+ nodeId: entry.nodeId,
687
+ local: cloneValue(entry.local),
688
+ world: cloneValue(entry.world),
689
+ tags: [...entry.tags],
690
+ metadata: cloneValue(entry.metadata),
691
+ }))
692
+ .sort((left, right) => left.id.localeCompare(right.id)),
693
+ meshes: state.config.meshes.map((entry) => ({
694
+ id: entry.id,
695
+ nodeId: entry.nodeId,
696
+ visible: entry.visible,
697
+ mesh: sanitizeHandle(entry.mesh),
698
+ material: sanitizeHandle(entry.material),
699
+ transform: cloneValue(entry.transform),
700
+ tags: [...entry.tags],
701
+ metadata: cloneValue(entry.metadata),
702
+ })),
703
+ billboards: state.config.billboards.map((entry) => ({
704
+ id: entry.id,
705
+ nodeId: entry.nodeId,
706
+ visible: entry.visible,
707
+ textureHandle: sanitizeHandle(entry.textureHandle),
708
+ width: entry.width,
709
+ height: entry.height,
710
+ mode: entry.mode,
711
+ offset: cloneValue(entry.offset),
712
+ tags: [...entry.tags],
713
+ metadata: cloneValue(entry.metadata),
714
+ })),
715
+ avatars: [...state.avatars.values()]
716
+ .map((entry) => ({
717
+ id: entry.id,
718
+ nodeId: entry.definition.nodeId,
719
+ spawned: entry.spawned,
720
+ tags: [...entry.definition.tags],
721
+ metadata: cloneValue(entry.definition.metadata),
722
+ state: entry.controller && typeof entry.controller.getState === 'function'
723
+ ? cloneValue(entry.controller.getState())
724
+ : null,
725
+ }))
726
+ .sort((left, right) => left.id.localeCompare(right.id)),
727
+ triggers: state.config.triggers.map((entry) => {
728
+ const runtime = state.triggerState.get(entry.id);
729
+ return {
730
+ id: entry.id,
731
+ nodeId: entry.nodeId,
732
+ shape: cloneValue(entry.shape),
733
+ offset: cloneValue(entry.offset),
734
+ activeWatcherIds: runtime ? [...runtime.activeWatcherIds].sort((left, right) => left.localeCompare(right)) : [],
735
+ enterCount: runtime?.enterCount || 0,
736
+ exitCount: runtime?.exitCount || 0,
737
+ lastEvent: runtime?.lastEvent ? cloneValue(runtime.lastEvent) : null,
738
+ tags: [...entry.tags],
739
+ metadata: cloneValue(entry.metadata),
740
+ };
741
+ }),
742
+ stats: {
743
+ nodeCount: state.nodeMap.size,
744
+ meshCount: state.config.meshes.length,
745
+ billboardCount: state.config.billboards.length,
746
+ avatarCount: state.avatars.size,
747
+ triggerCount: state.config.triggers.length,
748
+ lastFrameTriggerEventCount: state.lastTriggerEvents.length,
749
+ },
750
+ };
751
+ }