@auraindustry/aurajs 0.1.1 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.md +7 -0
  2. package/benchmarks/perf-thresholds.json +27 -0
  3. package/package.json +6 -1
  4. package/src/ai-guidance.mjs +302 -0
  5. package/src/asset-pack.mjs +2 -1
  6. package/src/authored-project.mjs +498 -2
  7. package/src/authored-runtime.mjs +14 -0
  8. package/src/bin-integrity.mjs +33 -26
  9. package/src/build-contract/capabilities.mjs +87 -1
  10. package/src/build-contract/constants.mjs +1 -0
  11. package/src/build-contract.mjs +2 -0
  12. package/src/bundler.mjs +143 -13
  13. package/src/cli.mjs +681 -13
  14. package/src/commands/packs.mjs +741 -0
  15. package/src/commands/project-authoring.mjs +128 -1
  16. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
  17. package/src/conformance/cases/core-runtime-cases.mjs +6 -2
  18. package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
  19. package/src/conformance/cases/systems-and-gameplay-cases.mjs +1126 -10
  20. package/src/conformance-mobile.mjs +166 -0
  21. package/src/conformance.mjs +89 -30
  22. package/src/evidence-bundle.mjs +242 -0
  23. package/src/external-package-surface.mjs +1 -1
  24. package/src/headless-test/runtime-coordinator.mjs +186 -33
  25. package/src/headless-test.mjs +2 -0
  26. package/src/helpers/2d/index.mjs +183 -0
  27. package/src/helpers/index.mjs +26 -0
  28. package/src/helpers/starter-utils/adventure-objectives.js +102 -0
  29. package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
  30. package/src/helpers/starter-utils/animation-2d.js +337 -0
  31. package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
  32. package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
  33. package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
  34. package/src/helpers/starter-utils/avatar-3d.js +404 -0
  35. package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
  36. package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
  37. package/src/helpers/starter-utils/core.js +150 -0
  38. package/src/helpers/starter-utils/dialogue-2d.js +351 -0
  39. package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
  40. package/src/helpers/starter-utils/index.js +26 -0
  41. package/src/helpers/starter-utils/inventory-2d.js +268 -0
  42. package/src/helpers/starter-utils/journal-2d.js +267 -0
  43. package/src/helpers/starter-utils/platformer-3d.js +132 -0
  44. package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
  45. package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
  46. package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
  47. package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
  48. package/src/helpers/starter-utils/triggers.js +662 -0
  49. package/src/helpers/starter-utils/tween-2d.js +615 -0
  50. package/src/helpers/starter-utils/wave-director.js +101 -0
  51. package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
  52. package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
  53. package/src/mobile/android/build.mjs +606 -0
  54. package/src/mobile/android/host-artifact.mjs +280 -0
  55. package/src/mobile/ios/build.mjs +1323 -0
  56. package/src/mobile/ios/host-artifact.mjs +819 -0
  57. package/src/mobile/shared/capabilities.mjs +174 -0
  58. package/src/package-integrity.mjs +18 -4
  59. package/src/packs/catalog.mjs +259 -0
  60. package/src/perf-benchmark-runner.mjs +17 -12
  61. package/src/perf-benchmark.mjs +408 -4
  62. package/src/publish-command.mjs +434 -17
  63. package/src/publish-validation.mjs +22 -11
  64. package/src/replay-runtime.mjs +257 -0
  65. package/src/scaffold/config.mjs +2 -0
  66. package/src/scaffold/fs.mjs +8 -1
  67. package/src/scaffold/project-docs.mjs +101 -41
  68. package/src/scaffold.mjs +4 -0
  69. package/src/session-runtime.mjs +4 -3
  70. package/src/web-conformance.mjs +0 -36
  71. package/templates/create/2d/src/runtime/app.js +4 -0
  72. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
  73. package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
  74. package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
  75. package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
  76. package/templates/create/2d-adventure/docs/design/loop.md +4 -3
  77. package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
  78. package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
  79. package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
  80. package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
  81. package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
  82. package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
  83. package/templates/create/2d-survivor/src/runtime/app.js +4 -0
  84. package/templates/create/3d/scenes/gameplay.scene.js +30 -3
  85. package/templates/create/3d/src/runtime/app.js +4 -0
  86. package/templates/create/3d/src/runtime/capabilities.js +5 -0
  87. package/templates/create/3d/src/runtime/materials.js +10 -0
  88. package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
  89. package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
  90. package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
  91. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
  92. package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
  93. package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
  94. package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
  95. package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
  96. package/templates/create/blank/assets/splash/bg.webp +0 -0
  97. package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
  98. package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
  99. package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
  100. package/templates/create/blank/assets/splash/logoholo.webp +0 -0
  101. package/templates/create/blank/src/main.js +5 -1
  102. package/templates/create/blank/src/runtime/splash.js +305 -0
  103. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +186 -12
  104. package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
  105. package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
  106. package/templates/create/shared/assets/splash/bg.webp +0 -0
  107. package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
  108. package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
  109. package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
  110. package/templates/create/shared/assets/splash/logoholo.webp +0 -0
  111. package/templates/create/shared/src/runtime/splash.js +305 -0
  112. package/templates/create/shared/src/runtime/ui-forms.js +552 -0
  113. package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
  114. package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
  115. package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
  116. package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
  117. package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
  118. package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
  119. package/templates/create/shared/src/starter-utils/index.js +15 -1
  120. package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
  121. package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
  122. package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
  123. package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
  124. package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
  125. package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
  126. package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
  127. package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
  128. package/templates/create/video-cutscene/src/runtime/app.js +4 -0
  129. package/templates/create-bin/play.js +148 -7
  130. package/templates/skills/auramaxx/SKILL.md +46 -0
  131. package/templates/skills/auramaxx/project-requirements.md +68 -0
  132. package/templates/skills/auramaxx/starter-recipes.md +104 -0
  133. package/templates/skills/auramaxx/validation-checklist.md +49 -0
  134. package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
  135. package/templates/starter/assets/splash/bg.webp +0 -0
  136. package/templates/starter/assets/splash/boot-loop.wav +0 -0
  137. package/templates/starter/assets/splash/boot-sting.wav +0 -0
  138. package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
  139. package/templates/starter/assets/splash/logoholo.webp +0 -0
  140. package/templates/starter/src/main.js +4 -0
  141. package/templates/starter/src/runtime/splash.js +305 -0
  142. package/templates/skills/aurajs/SKILL.md +0 -96
  143. package/templates/skills/aurajs/api-contract-3d.md +0 -7
  144. package/templates/skills/aurajs/api-contract.md +0 -7
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: auramaxx
3
+ description: Use when building or modifying AuraMaxx or AuraJS game projects. Helps agents infer game requirements from local project files, pick the right starter-owned edit points, and validate gameplay work with the current CLI and project wrapper commands.
4
+ ---
5
+
6
+ # AuraMaxx Skill
7
+
8
+ Use this skill when working inside an AuraMaxx or AuraJS game project.
9
+
10
+ ## Working Set
11
+
12
+ Read these files in order:
13
+
14
+ 1. `project-requirements.md`
15
+ 2. `starter-recipes.md`
16
+ 3. `validation-checklist.md`
17
+ 4. the current project files named by those docs
18
+
19
+ Use `https://www.aurajs.gg/llm.txt` only when you need broader public docs
20
+ context than the local project and installed package sources provide.
21
+
22
+ ## Rules
23
+
24
+ - Treat the current project files as the primary source of truth.
25
+ - Keep `src/main.js` thin; prefer the registry-backed runtime flow already in the scaffold.
26
+ - Put durable authored data in `config/` or `content/`, not scene-local constants.
27
+ - Put mutable runtime state in scenes or `src/runtime/app-state.js`.
28
+ - Extend `src/starter-utils/` before inventing one-off helpers.
29
+ - Use `auramaxx make` instead of hand-wiring new scaffold nouns when a generator exists.
30
+
31
+ ## Default Loop
32
+
33
+ 1. Infer the current game requirements with `project-requirements.md`.
34
+ 2. Choose the starter-specific edit path in `starter-recipes.md`.
35
+ 3. Implement the smallest playable slice that changes one mechanic at a time.
36
+ 4. Run the relevant checks from `validation-checklist.md`.
37
+
38
+ ## Retrieval Boundary
39
+
40
+ Open local project files first. If you still need package-level behavior or
41
+ template intent, inspect:
42
+
43
+ - `node_modules/@auraindustry/aurajs/src/`
44
+ - `node_modules/@auraindustry/aurajs/templates/`
45
+
46
+ Use `aurajs.gg/llm.txt` after that, not before.
@@ -0,0 +1,68 @@
1
+ # AuraMaxx Project Requirements
2
+
3
+ Use this checklist to understand the game before you edit it.
4
+
5
+ ## 1. Identity
6
+
7
+ - Read `package.json` and `aura.config.json`.
8
+ - Capture: package name, bin name, project title, version, runtime targets, and
9
+ whether the project is intended to be published.
10
+
11
+ ## 2. Template and module shape
12
+
13
+ - Read `aura.capabilities.json` and `config/gameplay/game.config.json`.
14
+ - Capture: template id, required APIs, optional modules, `startingSceneId`,
15
+ `gameplaySceneId`, `hudScreenId`, and `playerPrefabId`.
16
+
17
+ ## 3. Player promise
18
+
19
+ - Read `README.md`, `RUNBOOK.md`, `docs/design/game-pillars.md`,
20
+ `docs/design/loop.md`, and `assets/starter/dev-notes.md`.
21
+ - Write down:
22
+ - genre and player fantasy
23
+ - first 60-second goal
24
+ - controls
25
+ - win and lose states
26
+ - what “done for this task” should feel like on screen
27
+
28
+ ## 4. Authored owners
29
+
30
+ - Read `src/runtime/project-registry.js`, `config/gameplay/*`,
31
+ `content/gameplay/*`, `content/registries/*`, `prefabs/*.prefab.js`,
32
+ `scenes/*.scene.js`, and `ui/*.screen.js`.
33
+ - Map which files own:
34
+ - loop logic
35
+ - tuning values
36
+ - authored nouns and layout
37
+ - HUD and modal UI
38
+ - progression, save, dialogue, inventory, or multiplayer state
39
+
40
+ ## 5. Assets and starter data
41
+
42
+ - Read `assets/starter/*` first, then only the `assets/**` files actually used
43
+ by the touched scene or prefab.
44
+ - Capture: placeholder assets still in use, required art/audio hooks, and any
45
+ starter JSON or notes that define layout, waves, cards, checkpoints, or room
46
+ behavior.
47
+
48
+ ## 6. Decide where the change belongs
49
+
50
+ - Stable tunable: `config/`
51
+ - Authored noun or layout data: `content/`
52
+ - Reusable entity or world descriptor: `prefabs/`
53
+ - Live mechanic or per-frame flow: `scenes/`
54
+ - HUD, overlay, or modal surface: `ui/`
55
+ - Cross-scene mutable app state: `src/runtime/app-state.js`
56
+ - Reusable starter helper: `src/starter-utils/`
57
+
58
+ ## 7. Discovery commands
59
+
60
+ - `auramaxx explain`
61
+ - `auramaxx check`
62
+ - `auramaxx make list --json`
63
+ - `npm run dev`
64
+ - `npm run state -- export --compact`
65
+ - `npm run action -- schema --compact`
66
+
67
+ Use these to confirm what the project already exposes before you add new
68
+ structure.
@@ -0,0 +1,104 @@
1
+ # AuraMaxx Starter Recipes
2
+
3
+ Use the current template id from `aura.capabilities.json` or `aura.config.json`
4
+ to pick the right recipe.
5
+
6
+ ## 2d-adventure
7
+
8
+ - Edit first:
9
+ - `content/gameplay/world.js`
10
+ - `config/gameplay/adventure.config.js`
11
+ - `scenes/gameplay.scene.js`
12
+ - `prefabs/world.prefab.js`
13
+ - `prefabs/relic.prefab.js`
14
+ - `ui/hud.screen.js`
15
+ - Good first tasks:
16
+ - tighten movement and interaction feel
17
+ - rewrite room nouns, relic goals, dialogue, and route beats
18
+ - add one strong interaction before widening scope
19
+ - Avoid:
20
+ - burying route data inside the scene
21
+ - replacing starter-utils with duplicated local helpers
22
+
23
+ ## 2d-shooter and 2d-survivor
24
+
25
+ - Edit first:
26
+ - `config/gameplay/*.config.js`
27
+ - `scenes/gameplay.scene.js`
28
+ - `prefabs/player.prefab.js`
29
+ - enemy or projectile prefabs
30
+ - `ui/*.screen.js`
31
+ - Good first tasks:
32
+ - pacing, spawn pressure, weapon feel, hit feedback, restart loop
33
+ - Avoid:
34
+ - mixing balance constants into render code
35
+ - adding many enemy types before one wave feels good
36
+
37
+ ## deckbuilder-2d
38
+
39
+ - Edit first:
40
+ - starter-owned `content/registries/*`
41
+ - `config/gameplay/*`
42
+ - combat scene files
43
+ - HUD and card presentation screens
44
+ - Good first tasks:
45
+ - one coherent card set
46
+ - one enemy intent loop
47
+ - one relic or progression rule
48
+ - Avoid:
49
+ - putting card data directly in scene logic
50
+
51
+ ## 3d-adventure, 3d-platformer, 3d-collectathon
52
+
53
+ - Edit first:
54
+ - `content/gameplay/course.js`
55
+ - `config/gameplay/*.config.js`
56
+ - `scenes/gameplay.scene.js`
57
+ - `prefabs/world.prefab.js`
58
+ - `prefabs/player.prefab.js`
59
+ - `src/starter-utils/avatar-3d.js`
60
+ - `src/starter-utils/platformer-3d.js`
61
+ - Good first tasks:
62
+ - movement feel, camera follow, checkpoints, collectibles, route readability
63
+ - Avoid:
64
+ - hardcoding course layout in the scene
65
+ - treating imported models as the only source of gameplay truth
66
+
67
+ ## local-multiplayer
68
+
69
+ - Edit first:
70
+ - `config/gameplay/local-multiplayer.config.js`
71
+ - `content/gameplay/room-layout.js`
72
+ - `scenes/gameplay.scene.js`
73
+ - `ui/hud.screen.js`
74
+ - Good first tasks:
75
+ - one host/join room loop
76
+ - one shared objective
77
+ - one clear disconnect or retry path
78
+ - Avoid:
79
+ - breaking the local-first host flow while chasing internet features
80
+ - assuming headless state or action flows work unless you verify them
81
+
82
+ ## blank
83
+
84
+ - Edit first:
85
+ - `src/main.js`
86
+ - `scenes/gameplay.scene.js`
87
+ - `prefabs/player.prefab.js`
88
+ - `ui/hud.screen.js`
89
+ - `config/gameplay/game.config.json`
90
+ - Good first tasks:
91
+ - one visible mechanic
92
+ - one HUD element
93
+ - one authored config value
94
+ - Avoid:
95
+ - recreating the whole scaffold model from scratch
96
+
97
+ ## Cross-starter recipe
98
+
99
+ For any starter:
100
+
101
+ 1. make one mechanic feel good
102
+ 2. make the goal readable in HUD or world feedback
103
+ 3. move stable data into `config/` or `content/`
104
+ 4. only then add more systems
@@ -0,0 +1,49 @@
1
+ # AuraMaxx Validation Checklist
2
+
3
+ Pick the smallest set that matches the task.
4
+
5
+ ## Always
6
+
7
+ - Run `auramaxx explain` after structure changes.
8
+ - Run `auramaxx check` before and after edits.
9
+ - If you add new scaffold nouns, run `auramaxx make list --json` first and use
10
+ `auramaxx make ...` where possible.
11
+
12
+ ## Gameplay slice
13
+
14
+ - Run `npm run dev`.
15
+ - Confirm:
16
+ - the game boots
17
+ - the first objective is readable
18
+ - controls match the intended loop
19
+ - HUD or world feedback reflects the new mechanic
20
+ - there are no obvious stuck states or soft-locks
21
+
22
+ ## State and action tooling
23
+
24
+ - Run `npm run state -- export --compact` when the project claims state export.
25
+ - Run `npm run action -- schema --compact` when the project claims action
26
+ tooling.
27
+ - If these fail, record the exact reason instead of pretending the flow works.
28
+
29
+ ## Session and headless flows
30
+
31
+ - Use `auramaxx session start --headless --name ci-loop --compact` only after
32
+ verifying the starter and runtime actually support that lane.
33
+ - If the project is multiplayer or starter-heavy, verify headless flows
34
+ explicitly instead of assuming parity with visible `npm run dev`.
35
+
36
+ ## Publish and package surface
37
+
38
+ - Run `auramaxx publish --dry-run` for package-surface and build validation.
39
+ - Run `auramaxx external-assets generate --public-base-url <url>` only after the
40
+ build artifacts exist and the project actually needs self-hosted assets.
41
+
42
+ ## What to report
43
+
44
+ When you finish, report:
45
+
46
+ - which commands passed
47
+ - which commands failed
48
+ - whether failures are task regressions or pre-existing runtime gaps
49
+ - what the player-visible outcome is
@@ -3,6 +3,7 @@
3
3
  // - modules.physics: enables aura.physics.*
4
4
  // - modules.network: enables aura.net.*
5
5
  // - modules.steam: enables aura.steam.*
6
+ import { initSplash, updateSplash, drawSplash, isSplashActive } from './runtime/splash.js';
6
7
 
7
8
  let x = 400;
8
9
  let y = 300;
@@ -153,6 +154,7 @@ function clearScreen(color) {
153
154
 
154
155
  aura.setup = function () {
155
156
  assertRuntimeCapabilities();
157
+ initSplash();
156
158
  console.log("{{PROJECT_NAME}} started");
157
159
  if (!hasAsset(PLAYER_SPRITE_PATH)) {
158
160
  console.log(`Tip: add assets/${PLAYER_SPRITE_PATH} to preview sprite draw2d options.`);
@@ -163,6 +165,7 @@ aura.setup = function () {
163
165
  };
164
166
 
165
167
  aura.update = function (dt) {
168
+ if (isSplashActive()) { updateSplash(dt); return; }
166
169
  if (keyDown("arrowright")) x += speed * dt;
167
170
  if (keyDown("arrowleft")) x -= speed * dt;
168
171
  if (keyDown("arrowup")) y -= speed * dt;
@@ -186,6 +189,7 @@ aura.update = function (dt) {
186
189
  };
187
190
 
188
191
  aura.draw = function () {
192
+ if (isSplashActive()) { drawSplash(); return; }
189
193
  const bg = rgba(0.08, 0.08, 0.12, 1.0);
190
194
  clearScreen(bg);
191
195
 
@@ -0,0 +1,305 @@
1
+ // AuraJS Splash — unified retro paper-card boot screen.
2
+ // Auto-wired by createApp(). Shows once on launch, then hands off to the game.
3
+
4
+ const FADE_IN = 0.7;
5
+ const HOLD = 1.8;
6
+ const FADE_OUT = 0.7;
7
+ const TOTAL = FADE_IN + HOLD + FADE_OUT;
8
+
9
+ const PAPER = [0.89, 0.89, 0.89];
10
+ const PAPER_SHADOW = [0.80, 0.80, 0.80];
11
+ const PAPER_EDGE = [0.41, 0.41, 0.41];
12
+ const INK = [0.1, 0.1, 0.1];
13
+ const INK_MUTED = [0.42, 0.42, 0.42];
14
+ const MASCOT_FRAME_W = 64;
15
+ const MASCOT_FRAME_H = 84;
16
+ const MASCOT_SEQUENCE = Object.freeze([0, 1, 2, 1]);
17
+ const SPLASH_STING_PATH = 'splash/boot-sting.wav';
18
+ const SPLASH_LOOP_PATH = 'splash/boot-loop.wav';
19
+ const SPLASH_BUS = 'splash';
20
+ const QUIET_BUSES = Object.freeze(['default', 'music', 'sfx']);
21
+
22
+ let state = null;
23
+
24
+ function has(obj, method) {
25
+ return Boolean(obj) && typeof obj[method] === 'function';
26
+ }
27
+
28
+ function color(rgb, alpha = 1) {
29
+ return aura.rgba(rgb[0], rgb[1], rgb[2], alpha);
30
+ }
31
+
32
+ function drawShadowText(text, x, y, options = {}) {
33
+ const {
34
+ shadowOffset = 2,
35
+ shadowColor = color(INK_MUTED, 0.3),
36
+ ...rest
37
+ } = options;
38
+ aura.draw2d.text(text, x + shadowOffset, y + shadowOffset, {
39
+ ...rest,
40
+ color: shadowColor,
41
+ });
42
+ aura.draw2d.text(text, x, y, rest);
43
+ }
44
+
45
+ export function initSplash() {
46
+ state = {
47
+ t: 0,
48
+ logo: null,
49
+ mascot: null,
50
+ wordmark: null,
51
+ font: null,
52
+ stingHandle: null,
53
+ loopHandle: null,
54
+ busVolumes: null,
55
+ pausedHandles: [],
56
+ };
57
+ try { if (has(aura.assets, 'load')) state.logo = aura.assets.load('splash/logoholo.webp'); } catch (_) {}
58
+ try { if (has(aura.assets, 'load')) state.mascot = aura.assets.load('splash/logo-mascot-sheet.webp'); } catch (_) {}
59
+ try { if (has(aura.assets, 'load')) state.wordmark = aura.assets.load('splash/aurajs-gg-wordmark.webp'); } catch (_) {}
60
+ try {
61
+ if (has(aura.assets, 'loadBitmapFont')) {
62
+ const result = aura.assets.loadBitmapFont();
63
+ if (result && result.ok && result.font) state.font = result.font;
64
+ }
65
+ } catch (_) {}
66
+ captureBusVolumes();
67
+ applySplashBusIsolation();
68
+ try {
69
+ if (aura.audio && aura.audio.supported !== false && typeof aura.audio.play === 'function') {
70
+ state.loopHandle = aura.audio.play(SPLASH_LOOP_PATH, {
71
+ loop: true,
72
+ volume: 0.22,
73
+ bus: SPLASH_BUS,
74
+ });
75
+ state.stingHandle = aura.audio.play(SPLASH_STING_PATH, {
76
+ loop: false,
77
+ volume: 0.54,
78
+ bus: SPLASH_BUS,
79
+ });
80
+ }
81
+ } catch (_) {}
82
+ }
83
+
84
+ export function isSplashActive() {
85
+ return state !== null;
86
+ }
87
+
88
+ export function updateSplash(dt) {
89
+ if (!state) return;
90
+ syncPausedTracks();
91
+ try {
92
+ if (aura.audio && typeof aura.audio.update === 'function') {
93
+ aura.audio.update(Number(dt) > 0 ? Number(dt) : (1 / 60));
94
+ }
95
+ } catch (_) {}
96
+ state.t += dt;
97
+ if (state.t >= TOTAL) {
98
+ stopSplashAudio();
99
+ state = null;
100
+ }
101
+ }
102
+
103
+ export function drawSplash() {
104
+ if (!state) return;
105
+ const { width: w, height: h } = has(aura.window, 'getSize')
106
+ ? aura.window.getSize()
107
+ : { width: 640, height: 480 };
108
+ const t = state.t;
109
+ const cx = w / 2;
110
+ const cy = h / 2;
111
+
112
+ let a = 1;
113
+ if (t < FADE_IN) a = easeOut(t / FADE_IN);
114
+ else if (t > FADE_IN + HOLD) a = 1 - easeIn((t - FADE_IN - HOLD) / FADE_OUT);
115
+
116
+ aura.draw2d.clear(color(PAPER));
117
+
118
+ const panelW = Math.min(Math.floor(w * 0.48), 580);
119
+ const panelH = Math.min(Math.floor(h * 0.72), 620);
120
+ const panelX = Math.floor(cx - (panelW * 0.5));
121
+ const panelY = Math.floor(cy - (panelH * 0.5));
122
+
123
+ aura.draw2d.rectFill(panelX + 6, panelY + 6, panelW, panelH, color(INK, 0.08 * a));
124
+ aura.draw2d.rectFill(panelX, panelY, panelW, panelH, color(PAPER_EDGE, a));
125
+ aura.draw2d.rectFill(panelX + 6, panelY + 6, panelW - 12, panelH - 12, color(PAPER, a));
126
+ aura.draw2d.rectFill(panelX + 12, panelY + 12, panelW - 24, panelH - 24, color(PAPER, a * 0.96));
127
+
128
+ const floatY = Math.sin(t * 1.4 * Math.PI * 2) * 2;
129
+ const breathe = 0.985 + 0.015 * (0.5 + 0.5 * Math.sin(t * Math.PI * 2));
130
+ const mascotScale = Math.max(1, Math.min(Math.floor(Math.min(w, h) / 260), 2));
131
+ const mascotW = Math.floor(MASCOT_FRAME_W * mascotScale * breathe);
132
+ const mascotH = Math.floor(MASCOT_FRAME_H * mascotScale * breathe);
133
+ const mascotX = Math.floor(cx - (mascotW * 0.5));
134
+ const mascotY = Math.floor(panelY + panelH * 0.40 + floatY);
135
+ const mascotFrame = MASCOT_SEQUENCE[Math.floor(t * 7.5) % MASCOT_SEQUENCE.length] || 0;
136
+
137
+ aura.draw2d.rectFill(
138
+ mascotX + Math.floor(mascotW * 0.18),
139
+ mascotY + mascotH - 8,
140
+ Math.floor(mascotW * 0.64),
141
+ 6,
142
+ color(INK, a * 0.08),
143
+ );
144
+
145
+ if (state.mascot) {
146
+ aura.draw2d.sprite(state.mascot, mascotX, mascotY, {
147
+ width: mascotW,
148
+ height: mascotH,
149
+ frameX: mascotFrame * MASCOT_FRAME_W,
150
+ frameY: 0,
151
+ frameW: MASCOT_FRAME_W,
152
+ frameH: MASCOT_FRAME_H,
153
+ alpha: a,
154
+ });
155
+ } else if (state.logo) {
156
+ const sz = Math.min(Math.floor(panelW * 0.24), Math.floor(h * 0.14)) * breathe;
157
+ aura.draw2d.sprite(state.logo, Math.floor(cx - sz / 2), Math.floor(mascotY + 6), {
158
+ width: sz,
159
+ height: sz,
160
+ alpha: a,
161
+ });
162
+ }
163
+
164
+ const ta = clamp(a * easeOut(clamp((t - 0.2) / (FADE_IN * 0.6))));
165
+ const sa = clamp(a * easeOut(clamp((t - 0.4) / (FADE_IN * 0.6))));
166
+ const scale = Math.min(w, h) / 480;
167
+ const tsz = Math.max(24, Math.round(scale * 28));
168
+ const ssz = Math.max(11, Math.round(scale * 12));
169
+ const wordmarkW = Math.min(Math.floor(panelW * 0.52), 290);
170
+ const wordmarkH = Math.floor(wordmarkW * (768 / 1408));
171
+ const wordmarkX = Math.floor(cx - (wordmarkW * 0.5));
172
+ const wordmarkY = Math.floor(panelY + 36);
173
+ const sY = Math.floor(panelY + panelH - 108);
174
+ const fo = state.font ? { font: state.font } : {};
175
+
176
+ if (state.wordmark) {
177
+ aura.draw2d.sprite(state.wordmark, wordmarkX, wordmarkY, {
178
+ width: wordmarkW,
179
+ height: wordmarkH,
180
+ alpha: ta,
181
+ tint: color(INK, ta),
182
+ });
183
+ } else {
184
+ drawShadowText('AuraJS.gg', cx, wordmarkY + Math.floor(wordmarkH * 0.55), {
185
+ ...fo,
186
+ size: tsz,
187
+ color: color(INK, ta),
188
+ shadowColor: color(INK_MUTED, ta * 0.28),
189
+ align: 'center',
190
+ });
191
+ }
192
+
193
+ drawShadowText('Open-Source. MIT.', cx, sY, {
194
+ ...fo,
195
+ size: ssz,
196
+ color: color(INK_MUTED, sa),
197
+ shadowColor: color(PAPER_EDGE, sa * 0.18),
198
+ shadowOffset: 1,
199
+ align: 'center',
200
+ });
201
+ drawShadowText('Who needs publishers?', cx, sY + Math.max(16, Math.round(ssz * 1.45)), {
202
+ ...fo,
203
+ size: ssz,
204
+ color: color(INK_MUTED, sa),
205
+ shadowColor: color(PAPER_EDGE, sa * 0.18),
206
+ shadowOffset: 1,
207
+ align: 'center',
208
+ });
209
+
210
+ const rw = Math.min(panelW - 140, 240);
211
+ aura.draw2d.rectFill(Math.floor(cx - rw / 2), Math.floor(sY + Math.max(34, ssz * 3.2)), rw, 2, color(PAPER_EDGE, sa * 0.38));
212
+ }
213
+
214
+ function easeOut(t) {
215
+ const u = 1 - clamp(t);
216
+ return 1 - (u * u * u);
217
+ }
218
+
219
+ function easeIn(t) {
220
+ const c = clamp(t);
221
+ return c * c * c;
222
+ }
223
+
224
+ function clamp(v) {
225
+ return v < 0 ? 0 : v > 1 ? 1 : v;
226
+ }
227
+
228
+ function stopSplashAudio() {
229
+ if (!state || !aura.audio || typeof aura.audio.stop !== 'function') return;
230
+ try {
231
+ if (state.stingHandle != null) aura.audio.stop(state.stingHandle);
232
+ } catch (_) {}
233
+ try {
234
+ if (state.loopHandle != null) aura.audio.stop(state.loopHandle);
235
+ } catch (_) {}
236
+ restoreAudioState();
237
+ state.stingHandle = null;
238
+ state.loopHandle = null;
239
+ }
240
+
241
+ function captureBusVolumes() {
242
+ if (!state || !aura.audio || typeof aura.audio.getMixerState !== 'function') return;
243
+ try {
244
+ const mixer = aura.audio.getMixerState();
245
+ const buses = Array.isArray(mixer?.buses) ? mixer.buses : [];
246
+ state.busVolumes = buses.reduce((acc, entry) => {
247
+ const bus = String(entry?.bus || '').trim();
248
+ if (!bus) return acc;
249
+ acc[bus] = Number(entry?.volume);
250
+ return acc;
251
+ }, {});
252
+ } catch (_) {}
253
+ }
254
+
255
+ function applySplashBusIsolation() {
256
+ if (!state || !aura.audio || typeof aura.audio.setBusVolume !== 'function') return;
257
+ try { aura.audio.setBusVolume(SPLASH_BUS, 1); } catch (_) {}
258
+ for (const bus of QUIET_BUSES) {
259
+ try { aura.audio.setBusVolume(bus, 0); } catch (_) {}
260
+ }
261
+ }
262
+
263
+ function syncPausedTracks() {
264
+ if (!state || !aura.audio || typeof aura.audio.getMixerState !== 'function' || typeof aura.audio.pause !== 'function') return;
265
+ try {
266
+ const mixer = aura.audio.getMixerState();
267
+ const tracks = Array.isArray(mixer?.tracks) ? mixer.tracks : [];
268
+ const splashHandles = [state.stingHandle, state.loopHandle].filter((handle) => handle != null);
269
+ const pausedHandles = new Set(Array.isArray(state.pausedHandles) ? state.pausedHandles : []);
270
+ for (const track of tracks) {
271
+ const handle = Number(track?.handle);
272
+ if (!Number.isInteger(handle) || handle <= 0) continue;
273
+ if (splashHandles.includes(handle)) continue;
274
+ if (track?.paused === true || pausedHandles.has(handle)) continue;
275
+ try {
276
+ aura.audio.pause(handle);
277
+ pausedHandles.add(handle);
278
+ } catch (_) {}
279
+ }
280
+ state.pausedHandles = Array.from(pausedHandles);
281
+ } catch (_) {}
282
+ }
283
+
284
+ function restoreAudioState() {
285
+ if (!state || !aura.audio) return;
286
+ if (typeof aura.audio.setBusVolume === 'function') {
287
+ const snapshot = state.busVolumes && typeof state.busVolumes === 'object' ? state.busVolumes : null;
288
+ if (snapshot) {
289
+ for (const [bus, volume] of Object.entries(snapshot)) {
290
+ try { aura.audio.setBusVolume(bus, Number(volume)); } catch (_) {}
291
+ }
292
+ } else {
293
+ for (const bus of QUIET_BUSES) {
294
+ try { aura.audio.setBusVolume(bus, 1); } catch (_) {}
295
+ }
296
+ }
297
+ try { aura.audio.setBusVolume(SPLASH_BUS, 1); } catch (_) {}
298
+ }
299
+ if (typeof aura.audio.resume === 'function') {
300
+ for (const handle of Array.isArray(state.pausedHandles) ? state.pausedHandles : []) {
301
+ try { aura.audio.resume(handle); } catch (_) {}
302
+ }
303
+ }
304
+ state.pausedHandles = [];
305
+ }