@auraindustry/aurajs 0.0.6 → 0.1.0

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 +103 -7
  2. package/benchmarks/perf-thresholds.json +54 -0
  3. package/package.json +4 -7
  4. package/src/asset-pack.mjs +5 -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 +4825 -1512
  17. package/src/commands/project-authoring.mjs +434 -0
  18. package/src/config.mjs +27 -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 +438 -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 +40 -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 +14 -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 +443 -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 +60 -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 +1187 -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,641 @@
1
+ import {
2
+ createStarterDeck,
3
+ resolveCardTone,
4
+ STARTER_CARD_LIBRARY,
5
+ resolveCardTotals,
6
+ } from '../content/cards/starter.deck.js';
7
+ import { createTrainingBattle } from '../content/encounters/training-battle.js';
8
+ import { DECKBUILDER_CONFIG, DECKBUILDER_TEXT } from '../config/gameplay/deckbuilder.config.js';
9
+
10
+ const INPUT_KEYS = ['1', '2', '3', '4', '5'];
11
+ const RUN_STATE_SCHEMA = 'aurajs.deckbuilder-run-state.v1';
12
+
13
+ function cloneState(value) {
14
+ return JSON.parse(JSON.stringify(value));
15
+ }
16
+
17
+ function summarizeCard(card) {
18
+ return card?.text || card?.summary || 'Custom card effect.';
19
+ }
20
+
21
+ function materializeCard(cardId, instanceId = null) {
22
+ const source = STARTER_CARD_LIBRARY[cardId] || STARTER_CARD_LIBRARY.strike;
23
+ const totals = resolveCardTotals(source);
24
+ return {
25
+ ...source,
26
+ ...totals,
27
+ name: source.title || source.name || source.id,
28
+ summary: summarizeCard(source),
29
+ instanceId: instanceId || `${source.id}-restore`,
30
+ };
31
+ }
32
+
33
+ function exportCardRefs(cards) {
34
+ return Array.isArray(cards)
35
+ ? cards.map((card, index) => ({
36
+ cardId: typeof card?.id === 'string' && card.id.trim().length > 0 ? card.id : 'strike',
37
+ instanceId: typeof card?.instanceId === 'string' && card.instanceId.trim().length > 0
38
+ ? card.instanceId
39
+ : `card-${index + 1}`,
40
+ }))
41
+ : [];
42
+ }
43
+
44
+ function createCardPool() {
45
+ const byInstanceId = new Map();
46
+ const byCardId = new Map();
47
+ for (const card of createStarterDeck()) {
48
+ byInstanceId.set(card.instanceId, card);
49
+ if (!byCardId.has(card.id)) {
50
+ byCardId.set(card.id, []);
51
+ }
52
+ byCardId.get(card.id).push(card);
53
+ }
54
+ return {
55
+ byInstanceId,
56
+ byCardId,
57
+ usedInstanceIds: new Set(),
58
+ };
59
+ }
60
+
61
+ function takeCardFromPool(pool, ref, fallbackTag) {
62
+ const instanceId = typeof ref?.instanceId === 'string' && ref.instanceId.trim().length > 0
63
+ ? ref.instanceId.trim()
64
+ : null;
65
+ const cardId = typeof ref?.cardId === 'string' && ref.cardId.trim().length > 0
66
+ ? ref.cardId.trim()
67
+ : (instanceId && instanceId.includes('-') ? instanceId.split('-')[0] : 'strike');
68
+
69
+ if (instanceId && !pool.usedInstanceIds.has(instanceId) && pool.byInstanceId.has(instanceId)) {
70
+ pool.usedInstanceIds.add(instanceId);
71
+ return cloneState(pool.byInstanceId.get(instanceId));
72
+ }
73
+
74
+ const candidates = pool.byCardId.get(cardId) || [];
75
+ while (candidates.length > 0) {
76
+ const candidate = candidates.shift();
77
+ if (!pool.usedInstanceIds.has(candidate.instanceId)) {
78
+ pool.usedInstanceIds.add(candidate.instanceId);
79
+ return cloneState(candidate);
80
+ }
81
+ }
82
+
83
+ return materializeCard(cardId, instanceId || `${cardId}-${fallbackTag}`);
84
+ }
85
+
86
+ function restorePile(pool, cards, slot) {
87
+ if (!Array.isArray(cards)) return [];
88
+ return cards.map((ref, index) => takeCardFromPool(pool, ref, `${slot}-${index + 1}`));
89
+ }
90
+
91
+ function exportRunState(state) {
92
+ return {
93
+ schema: RUN_STATE_SCHEMA,
94
+ turn: state.turn,
95
+ energy: state.energy,
96
+ playerHp: state.playerHp,
97
+ playerBlock: state.playerBlock,
98
+ drawPile: exportCardRefs(state.drawPile),
99
+ hand: exportCardRefs(state.hand),
100
+ discardPile: exportCardRefs(state.discardPile),
101
+ outcome: state.outcome,
102
+ logLines: Array.isArray(state.logLines) ? [...state.logLines] : [],
103
+ encounter: {
104
+ id: state.encounter?.id || 'training-battle',
105
+ enemyId: state.encounter?.enemyId || 'training-automaton',
106
+ enemyHp: Number(state.encounter?.enemyHp || 0),
107
+ enemyBlock: Number(state.encounter?.enemyBlock || 0),
108
+ },
109
+ };
110
+ }
111
+
112
+ function restoreRunState(snapshot) {
113
+ if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) {
114
+ return {
115
+ ok: false,
116
+ reasonCode: 'scene_state_invalid',
117
+ };
118
+ }
119
+ if (snapshot.schema && snapshot.schema !== RUN_STATE_SCHEMA) {
120
+ return {
121
+ ok: false,
122
+ reasonCode: 'scene_state_invalid',
123
+ };
124
+ }
125
+
126
+ const state = createRunState();
127
+ if (snapshot.encounter?.id && snapshot.encounter.id !== state.encounter.id) {
128
+ return {
129
+ ok: false,
130
+ reasonCode: 'scene_state_invalid',
131
+ };
132
+ }
133
+ if (snapshot.encounter?.enemyId && snapshot.encounter.enemyId !== state.encounter.enemyId) {
134
+ return {
135
+ ok: false,
136
+ reasonCode: 'scene_state_invalid',
137
+ };
138
+ }
139
+
140
+ const pool = createCardPool();
141
+ state.turn = Number.isFinite(Number(snapshot.turn)) && Number(snapshot.turn) > 0
142
+ ? Number(snapshot.turn)
143
+ : state.turn;
144
+ state.energy = Number.isFinite(Number(snapshot.energy))
145
+ ? Math.max(0, Number(snapshot.energy))
146
+ : state.energy;
147
+ state.playerHp = Number.isFinite(Number(snapshot.playerHp))
148
+ ? Math.max(0, Number(snapshot.playerHp))
149
+ : state.playerHp;
150
+ state.playerBlock = Number.isFinite(Number(snapshot.playerBlock))
151
+ ? Math.max(0, Number(snapshot.playerBlock))
152
+ : state.playerBlock;
153
+ state.drawPile = restorePile(pool, snapshot.drawPile, 'draw');
154
+ state.hand = restorePile(pool, snapshot.hand, 'hand');
155
+ state.discardPile = restorePile(pool, snapshot.discardPile, 'discard');
156
+ state.outcome = typeof snapshot.outcome === 'string' && snapshot.outcome.trim().length > 0
157
+ ? snapshot.outcome
158
+ : null;
159
+ state.logLines = Array.isArray(snapshot.logLines) && snapshot.logLines.length > 0
160
+ ? snapshot.logLines.filter((entry) => typeof entry === 'string' && entry.trim().length > 0).slice(0, 4)
161
+ : state.logLines;
162
+ state.encounter.enemyHp = Number.isFinite(Number(snapshot.encounter?.enemyHp))
163
+ ? Math.max(0, Math.min(state.encounter.enemyMaxHp, Number(snapshot.encounter.enemyHp)))
164
+ : state.encounter.enemyHp;
165
+ state.encounter.enemyBlock = Number.isFinite(Number(snapshot.encounter?.enemyBlock))
166
+ ? Math.max(0, Number(snapshot.encounter.enemyBlock))
167
+ : state.encounter.enemyBlock;
168
+
169
+ return {
170
+ ok: true,
171
+ state,
172
+ };
173
+ }
174
+
175
+ function shuffleCards(cards) {
176
+ const output = [...cards];
177
+ for (let index = output.length - 1; index > 0; index -= 1) {
178
+ const swapIndex = Math.floor(Math.random() * (index + 1));
179
+ [output[index], output[swapIndex]] = [output[swapIndex], output[index]];
180
+ }
181
+ return output;
182
+ }
183
+
184
+ function createRunState() {
185
+ const encounter = createTrainingBattle();
186
+ const state = {
187
+ turn: 1,
188
+ energy: DECKBUILDER_CONFIG.energyPerTurn,
189
+ playerHp: DECKBUILDER_CONFIG.playerMaxHp,
190
+ playerBlock: 0,
191
+ drawPile: shuffleCards(createStarterDeck()),
192
+ hand: [],
193
+ discardPile: [],
194
+ outcome: null,
195
+ encounter,
196
+ logLines: [
197
+ encounter.intro,
198
+ encounter.rewardNote,
199
+ ],
200
+ };
201
+ drawCards(state, DECKBUILDER_CONFIG.handSize);
202
+ return state;
203
+ }
204
+
205
+ function recycleDiscard(state) {
206
+ if (state.drawPile.length > 0 || state.discardPile.length <= 0) return;
207
+ state.drawPile = shuffleCards(state.discardPile);
208
+ state.discardPile = [];
209
+ }
210
+
211
+ function drawCards(state, count) {
212
+ for (let index = 0; index < count; index += 1) {
213
+ recycleDiscard(state);
214
+ if (state.drawPile.length <= 0) return;
215
+ state.hand.push(state.drawPile.shift());
216
+ }
217
+ }
218
+
219
+ function pushLog(state, line) {
220
+ if (!line) return;
221
+ state.logLines = [line, ...state.logLines].slice(0, 4);
222
+ }
223
+
224
+ function currentEnemyIntent(state) {
225
+ const cycle = state.encounter.moves;
226
+ return cycle[(state.turn - 1) % cycle.length];
227
+ }
228
+
229
+ function createDeckbuilderUiStateSeed() {
230
+ return {
231
+ showBattleHint: true,
232
+ };
233
+ }
234
+
235
+ function ensureDeckbuilderUiState(context, appState) {
236
+ if (typeof context.ensureUiState === 'function') {
237
+ return context.ensureUiState('deckbuilderHud', createDeckbuilderUiStateSeed());
238
+ }
239
+ if (!appState.ui || typeof appState.ui !== 'object' || Array.isArray(appState.ui)) {
240
+ appState.ui = {};
241
+ }
242
+ if (!appState.ui.deckbuilderHud || typeof appState.ui.deckbuilderHud !== 'object' || Array.isArray(appState.ui.deckbuilderHud)) {
243
+ appState.ui.deckbuilderHud = createDeckbuilderUiStateSeed();
244
+ }
245
+ return appState.ui.deckbuilderHud;
246
+ }
247
+
248
+ function syncHud(context, state) {
249
+ const intent = currentEnemyIntent(state);
250
+ const appState = context.appState || {};
251
+ const deckbuilderUiState = ensureDeckbuilderUiState(context, appState);
252
+ context.screenShell.setHud('hud', {
253
+ sceneLabel: 'BATTLE',
254
+ title: '{{PROJECT_TITLE}} Deckbuilder',
255
+ subtitle: `${state.encounter.enemyName} · intent: ${intent.name}`,
256
+ turn: state.turn,
257
+ energy: state.energy,
258
+ playerHp: state.playerHp,
259
+ playerBlock: state.playerBlock,
260
+ enemyHp: state.encounter.enemyHp,
261
+ enemyBlock: state.encounter.enemyBlock,
262
+ drawPile: state.drawPile.length,
263
+ discardPile: state.discardPile.length,
264
+ handSize: state.hand.length,
265
+ hint: state.outcome
266
+ ? 'Press R to restart or ESC for boot.'
267
+ : (deckbuilderUiState.showBattleHint !== false ? DECKBUILDER_TEXT.battleHint : ''),
268
+ logLines: state.logLines,
269
+ outcome: state.outcome,
270
+ });
271
+ }
272
+
273
+ function startNextTurn(state) {
274
+ state.turn += 1;
275
+ state.energy = DECKBUILDER_CONFIG.energyPerTurn;
276
+ state.playerBlock = 0;
277
+ state.encounter.enemyBlock = 0;
278
+ while (state.hand.length > 0) {
279
+ state.discardPile.push(state.hand.shift());
280
+ }
281
+ drawCards(state, DECKBUILDER_CONFIG.handSize);
282
+ }
283
+
284
+ function applyDamageToEnemy(state, damage) {
285
+ const absorbed = Math.min(state.encounter.enemyBlock, damage);
286
+ const spill = Math.max(0, damage - absorbed);
287
+ state.encounter.enemyBlock -= absorbed;
288
+ state.encounter.enemyHp = Math.max(0, state.encounter.enemyHp - spill);
289
+ }
290
+
291
+ function resolvePlayerCard(state, card) {
292
+ if (card.damage > 0) {
293
+ applyDamageToEnemy(state, card.damage);
294
+ }
295
+ if (card.block > 0) {
296
+ state.playerBlock += card.block;
297
+ }
298
+ if (card.draw > 0) {
299
+ drawCards(state, card.draw);
300
+ }
301
+ }
302
+
303
+ function playCard(state, cardIndex) {
304
+ const card = state.hand[cardIndex];
305
+ if (!card) return;
306
+ if (card.cost > state.energy) {
307
+ pushLog(state, `${card.name} needs ${card.cost} energy.`);
308
+ return;
309
+ }
310
+
311
+ state.energy -= card.cost;
312
+ state.hand.splice(cardIndex, 1);
313
+ resolvePlayerCard(state, card);
314
+ state.discardPile.push(card);
315
+ pushLog(state, `Played ${card.name}. ${card.summary}`);
316
+
317
+ if (state.encounter.enemyHp <= 0) {
318
+ state.outcome = 'VICTORY';
319
+ pushLog(state, `${state.encounter.enemyName} was defeated.`);
320
+ }
321
+ }
322
+
323
+ function resolveEnemyTurn(state) {
324
+ const intent = currentEnemyIntent(state);
325
+ if (intent.block > 0) {
326
+ state.encounter.enemyBlock += intent.block;
327
+ }
328
+
329
+ let damage = intent.damage;
330
+ const absorbed = Math.min(state.playerBlock, damage);
331
+ state.playerBlock -= absorbed;
332
+ damage -= absorbed;
333
+ if (damage > 0) {
334
+ state.playerHp = Math.max(0, state.playerHp - damage);
335
+ }
336
+
337
+ pushLog(state, `${state.encounter.enemyName} used ${intent.name}. ${intent.summary}`);
338
+ if (state.playerHp <= 0) {
339
+ state.outcome = 'DEFEAT';
340
+ pushLog(state, 'The starter run ended. Press R to restart.');
341
+ return;
342
+ }
343
+
344
+ startNextTurn(state);
345
+ }
346
+
347
+ function drawPanel(x, y, width, height, outer, inner) {
348
+ aura.draw2d.rectFill(x, y, width, height, outer);
349
+ aura.draw2d.rectFill(x + 3, y + 3, width - 6, height - 6, inner);
350
+ }
351
+
352
+ function drawBar(x, y, width, value, maxValue, fillColor) {
353
+ const ratio = maxValue <= 0 ? 0 : Math.max(0, Math.min(1, value / maxValue));
354
+ aura.draw2d.rectFill(x, y, width, 14, aura.rgb(0.12, 0.14, 0.18));
355
+ aura.draw2d.rectFill(x + 2, y + 2, Math.max(0, Math.floor((width - 4) * ratio)), 10, fillColor);
356
+ }
357
+
358
+ function drawCard(card, index, x, y, selected) {
359
+ const tone = resolveCardTone(card);
360
+ const edge = selected ? aura.rgb(0.96, 0.86, 0.42) : aura.rgb(0.22, 0.24, 0.32);
361
+ drawPanel(x, y, DECKBUILDER_CONFIG.cardWidth, DECKBUILDER_CONFIG.cardHeight, edge, aura.rgb(0.08, 0.09, 0.14));
362
+ aura.draw2d.rectFill(x + 6, y + 6, DECKBUILDER_CONFIG.cardWidth - 12, 20, aura.rgb(tone.r, tone.g, tone.b));
363
+ aura.draw2d.text(`${index + 1}`, x + 12, y + 8, {
364
+ size: 12,
365
+ color: aura.Color.WHITE,
366
+ });
367
+ aura.draw2d.text(card.name, x + 34, y + 8, {
368
+ size: 13,
369
+ color: aura.Color.WHITE,
370
+ });
371
+ aura.draw2d.text(`${card.cost}E`, x + DECKBUILDER_CONFIG.cardWidth - 44, y + 8, {
372
+ size: 12,
373
+ color: aura.Color.WHITE,
374
+ });
375
+ aura.draw2d.text(card.summary, x + 12, y + 38, {
376
+ size: 12,
377
+ color: aura.rgb(0.88, 0.9, 0.95),
378
+ });
379
+ aura.draw2d.text(`DMG ${card.damage}`, x + 12, y + 72, {
380
+ size: 11,
381
+ color: aura.rgb(0.92, 0.44, 0.4),
382
+ });
383
+ aura.draw2d.text(`BLK ${card.block}`, x + 72, y + 72, {
384
+ size: 11,
385
+ color: aura.rgb(0.48, 0.72, 0.94),
386
+ });
387
+ aura.draw2d.text(`DRAW ${card.draw}`, x + 124, y + 72, {
388
+ size: 11,
389
+ color: aura.rgb(0.76, 0.78, 0.94),
390
+ });
391
+ }
392
+
393
+ function createDeckbuilderSessionSeed() {
394
+ return {
395
+ runsStarted: 0,
396
+ bestTurn: 0,
397
+ lastOutcome: 'idle',
398
+ lastSceneId: 'gameplay',
399
+ };
400
+ }
401
+
402
+ function ensureDeckbuilderSession(context, appState) {
403
+ if (typeof context.ensureSessionState === 'function') {
404
+ return context.ensureSessionState('deckbuilder', createDeckbuilderSessionSeed());
405
+ }
406
+ if (!appState.session || typeof appState.session !== 'object' || Array.isArray(appState.session)) {
407
+ appState.session = {};
408
+ }
409
+ if (!appState.session.deckbuilder || typeof appState.session.deckbuilder !== 'object' || Array.isArray(appState.session.deckbuilder)) {
410
+ appState.session.deckbuilder = createDeckbuilderSessionSeed();
411
+ }
412
+ return appState.session.deckbuilder;
413
+ }
414
+
415
+ export function createGameplayScene(context = {}) {
416
+ const appState = context.appState || {};
417
+ const deckbuilderSession = ensureDeckbuilderSession(context, appState);
418
+ const deckbuilderUiState = ensureDeckbuilderUiState(context, appState);
419
+ let sceneState = createRunState();
420
+
421
+ function syncSessionState() {
422
+ deckbuilderSession.lastSceneId = 'gameplay';
423
+ deckbuilderSession.bestTurn = Math.max(Number(deckbuilderSession.bestTurn || 0), sceneState.turn);
424
+ deckbuilderSession.lastOutcome = sceneState.outcome || 'running';
425
+ deckbuilderSession.lastTurn = sceneState.turn;
426
+ deckbuilderSession.lastPlayerHp = sceneState.playerHp;
427
+ deckbuilderSession.lastEnemyHp = sceneState.encounter.enemyHp;
428
+ }
429
+
430
+ function resetRun() {
431
+ sceneState = createRunState();
432
+ deckbuilderSession.runsStarted += 1;
433
+ syncSessionState();
434
+ syncHud(context, sceneState);
435
+ }
436
+
437
+ function isPauseOpen() {
438
+ const shellState = context.getScreenShellState?.();
439
+ return Array.isArray(shellState?.modals) && shellState.modals.some((entry) => entry.screenId === 'pause');
440
+ }
441
+
442
+ function syncPauseModal() {
443
+ if (!aura.input.isKeyPressed('escape')) return false;
444
+
445
+ const shellState = context.getScreenShellState?.();
446
+ const topModal = shellState?.modals?.[shellState.modals.length - 1] || null;
447
+ if (topModal) {
448
+ context.popModalScreen?.({ action: 'resume' });
449
+ return true;
450
+ }
451
+
452
+ context.pushModalScreen?.('pause', {
453
+ turn: sceneState.turn,
454
+ playerHp: sceneState.playerHp,
455
+ enemyHp: sceneState.encounter.enemyHp,
456
+ enemyName: sceneState.encounter.enemyName,
457
+ outcome: sceneState.outcome,
458
+ });
459
+ return true;
460
+ }
461
+
462
+ return {
463
+ id: 'gameplay',
464
+ template: 'deckbuilder-2d',
465
+ getState() {
466
+ return exportRunState(sceneState);
467
+ },
468
+ applyState(nextState) {
469
+ const restored = restoreRunState(nextState);
470
+ if (!restored.ok) {
471
+ return restored;
472
+ }
473
+ sceneState = restored.state;
474
+ syncSessionState();
475
+ syncHud(context, sceneState);
476
+ return {
477
+ ok: true,
478
+ reasonCode: 'scene_state_apply_ok',
479
+ };
480
+ },
481
+ setup() {
482
+ syncSessionState();
483
+ syncHud(context, sceneState);
484
+ },
485
+ enter() {
486
+ resetRun();
487
+ },
488
+ exit() {
489
+ context.screenShell.clearHud();
490
+ },
491
+ update() {
492
+ if (syncPauseModal()) {
493
+ syncSessionState();
494
+ syncHud(context, sceneState);
495
+ return;
496
+ }
497
+ if (isPauseOpen()) {
498
+ syncSessionState();
499
+ syncHud(context, sceneState);
500
+ return;
501
+ }
502
+
503
+ if (aura.input.isKeyPressed('r')) {
504
+ resetRun();
505
+ return;
506
+ }
507
+
508
+ if (!sceneState.outcome) {
509
+ for (let index = 0; index < INPUT_KEYS.length; index += 1) {
510
+ if (aura.input.isKeyPressed(INPUT_KEYS[index])) {
511
+ deckbuilderUiState.showBattleHint = false;
512
+ playCard(sceneState, index);
513
+ syncSessionState();
514
+ syncHud(context, sceneState);
515
+ return;
516
+ }
517
+ }
518
+
519
+ if (aura.input.isKeyPressed('enter') || aura.input.isKeyPressed('space')) {
520
+ deckbuilderUiState.showBattleHint = false;
521
+ resolveEnemyTurn(sceneState);
522
+ syncSessionState();
523
+ syncHud(context, sceneState);
524
+ return;
525
+ }
526
+ }
527
+
528
+ syncSessionState();
529
+ syncHud(context, sceneState);
530
+ },
531
+ draw() {
532
+ const size = aura.window.getSize();
533
+ const boardTop = DECKBUILDER_CONFIG.hudHeight + 16;
534
+ const handY = size.height - DECKBUILDER_CONFIG.cardHeight - 28;
535
+ const handGap = 14;
536
+ const handTotalWidth = (DECKBUILDER_CONFIG.cardWidth * DECKBUILDER_CONFIG.handSize)
537
+ + (handGap * (DECKBUILDER_CONFIG.handSize - 1));
538
+ const handStartX = Math.max(DECKBUILDER_CONFIG.boardPadding, Math.floor((size.width - handTotalWidth) * 0.5));
539
+
540
+ aura.draw2d.rectFill(0, 0, size.width, size.height, aura.rgb(0.05, 0.06, 0.09));
541
+ aura.draw2d.rectFill(0, boardTop, size.width, size.height - boardTop, aura.rgb(0.09, 0.1, 0.15));
542
+
543
+ drawPanel(36, boardTop + 24, size.width - 72, 198, aura.rgb(0.16, 0.17, 0.23), aura.rgb(0.11, 0.12, 0.18));
544
+ aura.draw2d.text(sceneState.encounter.enemyName, 58, boardTop + 42, {
545
+ size: 24,
546
+ color: aura.rgb(0.96, 0.9, 0.82),
547
+ });
548
+ aura.draw2d.text(`Intent: ${currentEnemyIntent(sceneState).name}`, 58, boardTop + 74, {
549
+ size: 15,
550
+ color: aura.rgb(0.8, 0.86, 0.95),
551
+ });
552
+ drawBar(
553
+ 58,
554
+ boardTop + 106,
555
+ 340,
556
+ sceneState.encounter.enemyHp,
557
+ sceneState.encounter.enemyMaxHp,
558
+ aura.rgb(0.86, 0.28, 0.28),
559
+ );
560
+ aura.draw2d.text(`Enemy HP ${sceneState.encounter.enemyHp}/${sceneState.encounter.enemyMaxHp}`, 58, boardTop + 126, {
561
+ size: 13,
562
+ color: aura.Color.WHITE,
563
+ });
564
+ aura.draw2d.text(`Enemy Block ${sceneState.encounter.enemyBlock}`, 58, boardTop + 148, {
565
+ size: 13,
566
+ color: aura.rgb(0.58, 0.8, 0.96),
567
+ });
568
+
569
+ drawPanel(size.width - 360, boardTop + 24, 324, 198, aura.rgb(0.16, 0.17, 0.23), aura.rgb(0.11, 0.12, 0.18));
570
+ aura.draw2d.text('Pilot', size.width - 338, boardTop + 42, {
571
+ size: 22,
572
+ color: aura.rgb(0.92, 0.94, 0.97),
573
+ });
574
+ aura.draw2d.text(`Energy ${sceneState.energy}`, size.width - 338, boardTop + 74, {
575
+ size: 16,
576
+ color: aura.rgb(0.94, 0.84, 0.36),
577
+ });
578
+ drawBar(
579
+ size.width - 338,
580
+ boardTop + 106,
581
+ 260,
582
+ sceneState.playerHp,
583
+ DECKBUILDER_CONFIG.playerMaxHp,
584
+ aura.rgb(0.32, 0.72, 0.42),
585
+ );
586
+ aura.draw2d.text(`Player HP ${sceneState.playerHp}/${DECKBUILDER_CONFIG.playerMaxHp}`, size.width - 338, boardTop + 126, {
587
+ size: 13,
588
+ color: aura.Color.WHITE,
589
+ });
590
+ aura.draw2d.text(`Player Block ${sceneState.playerBlock}`, size.width - 338, boardTop + 148, {
591
+ size: 13,
592
+ color: aura.rgb(0.58, 0.8, 0.96),
593
+ });
594
+ aura.draw2d.text(
595
+ `Draw ${sceneState.drawPile.length} Discard ${sceneState.discardPile.length}`,
596
+ size.width - 338,
597
+ boardTop + 170,
598
+ {
599
+ size: 13,
600
+ color: aura.rgb(0.84, 0.87, 0.95),
601
+ },
602
+ );
603
+
604
+ for (let index = 0; index < DECKBUILDER_CONFIG.handSize; index += 1) {
605
+ const card = sceneState.hand[index];
606
+ const x = handStartX + (index * (DECKBUILDER_CONFIG.cardWidth + handGap));
607
+ if (!card) {
608
+ drawPanel(x, handY, DECKBUILDER_CONFIG.cardWidth, DECKBUILDER_CONFIG.cardHeight, aura.rgb(0.18, 0.2, 0.28), aura.rgb(0.11, 0.12, 0.18));
609
+ aura.draw2d.text(`${index + 1}`, x + 12, handY + 8, {
610
+ size: 12,
611
+ color: aura.rgb(0.55, 0.6, 0.72),
612
+ });
613
+ continue;
614
+ }
615
+ drawCard(card, index, x, handY, card.cost <= sceneState.energy && !sceneState.outcome);
616
+ }
617
+
618
+ if (sceneState.outcome) {
619
+ const bannerWidth = 280;
620
+ const bannerX = Math.floor((size.width - bannerWidth) * 0.5);
621
+ const bannerY = boardTop + 248;
622
+ drawPanel(
623
+ bannerX,
624
+ bannerY,
625
+ bannerWidth,
626
+ 78,
627
+ sceneState.outcome === 'VICTORY' ? aura.rgb(0.44, 0.34, 0.12) : aura.rgb(0.36, 0.12, 0.12),
628
+ aura.rgb(0.1, 0.11, 0.16),
629
+ );
630
+ aura.draw2d.text(sceneState.outcome, bannerX + 86, bannerY + 16, {
631
+ size: 26,
632
+ color: sceneState.outcome === 'VICTORY' ? aura.rgb(0.98, 0.9, 0.36) : aura.rgb(0.96, 0.46, 0.42),
633
+ });
634
+ aura.draw2d.text('Press R to restart or ESC for boot.', bannerX + 34, bannerY + 48, {
635
+ size: 13,
636
+ color: aura.Color.WHITE,
637
+ });
638
+ }
639
+ },
640
+ };
641
+ }
@@ -0,0 +1,17 @@
1
+ // {{PROJECT_TITLE}} - AuraJS deckbuilder-2d starter
2
+
3
+ import { createApp } from './runtime/app.js';
4
+
5
+ const app = createApp();
6
+
7
+ aura.setup = function () {
8
+ app.setup();
9
+ };
10
+
11
+ aura.update = function (dt) {
12
+ app.update(dt);
13
+ };
14
+
15
+ aura.draw = function () {
16
+ app.draw();
17
+ };
@@ -0,0 +1,22 @@
1
+ function hasMethod(obj, method) {
2
+ return Boolean(obj) && typeof obj[method] === 'function';
3
+ }
4
+
5
+ function failWithReason(reasonCode, message) {
6
+ throw new Error(`[deckbuilder-2d-template] ${message} [reason:${reasonCode}]`);
7
+ }
8
+
9
+ export function assertRuntimeCapabilities() {
10
+ const missing = [];
11
+ if (!hasMethod(aura.window, 'getSize')) missing.push('aura.window.getSize');
12
+ if (!hasMethod(aura.input, 'isKeyPressed')) missing.push('aura.input.isKeyPressed');
13
+ if (!hasMethod(aura.draw2d, 'rectFill')) missing.push('aura.draw2d.rectFill');
14
+ if (!hasMethod(aura.draw2d, 'text')) missing.push('aura.draw2d.text');
15
+ if (typeof aura.rgb !== 'function') missing.push('aura.rgb');
16
+ if (typeof aura.rgba !== 'function') missing.push('aura.rgba');
17
+ if (!aura.Color || !aura.Color.WHITE) missing.push('aura.Color.WHITE');
18
+
19
+ if (missing.length > 0) {
20
+ failWithReason('missing_runtime_api', `runtime missing required APIs: ${missing.join(', ')}`);
21
+ }
22
+ }