@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
@@ -12,6 +12,7 @@ export function createAdventureMaterials() {
12
12
  }),
13
13
  relic: aura.material.create({
14
14
  color: { r: 0.98, g: 0.9, b: 0.38, a: 1.0 },
15
+ emissive: { r: 0.5, g: 0.38, b: 0.1 },
15
16
  metallic: 0.7,
16
17
  roughness: 0.24,
17
18
  }),
@@ -19,21 +20,31 @@ export function createAdventureMaterials() {
19
20
  color: { r: 0.3, g: 0.44, b: 0.66, a: 1.0 },
20
21
  metallic: 0.26,
21
22
  roughness: 0.36,
23
+ sheenColor: { r: 0.12, g: 0.16, b: 0.22 },
24
+ sheenRoughness: 0.26,
22
25
  }),
23
26
  beaconLit: aura.material.create({
24
27
  color: { r: 0.58, g: 1.0, b: 0.74, a: 1.0 },
28
+ emissive: { r: 0.12, g: 0.34, b: 0.18 },
25
29
  metallic: 0.48,
26
30
  roughness: 0.24,
31
+ sheenColor: { r: 0.16, g: 0.22, b: 0.18 },
32
+ sheenRoughness: 0.2,
27
33
  }),
28
34
  goalLocked: aura.material.create({
29
35
  color: { r: 0.24, g: 0.28, b: 0.34, a: 1.0 },
30
36
  metallic: 0.12,
31
37
  roughness: 0.44,
38
+ sheenColor: { r: 0.08, g: 0.1, b: 0.12 },
39
+ sheenRoughness: 0.34,
32
40
  }),
33
41
  goalReady: aura.material.create({
34
42
  color: { r: 0.72, g: 1.0, b: 0.82, a: 1.0 },
43
+ emissive: { r: 0.12, g: 0.28, b: 0.18 },
35
44
  metallic: 0.34,
36
45
  roughness: 0.28,
46
+ sheenColor: { r: 0.16, g: 0.22, b: 0.18 },
47
+ sheenRoughness: 0.24,
37
48
  }),
38
49
  };
39
50
  }
@@ -74,6 +74,34 @@ export function createGameplayScene(context = {}) {
74
74
  let playerVisual = null;
75
75
  let sceneState = createCollectathonRunState();
76
76
 
77
+ function configureSceneLook() {
78
+ aura.light.ambient({ r: 0.94, g: 0.97, b: 1.0 }, 0.16);
79
+ aura.light.hemisphere(
80
+ { r: 0.46, g: 0.6, b: 0.82 },
81
+ { r: 0.13, g: 0.1, b: 0.08 },
82
+ 0.32,
83
+ { x: 0, y: 1, z: 0 },
84
+ );
85
+ aura.light.directional({ x: 0.52, y: -1.0, z: -0.28 }, { r: 1.0, g: 0.97, b: 0.9 }, 1.08);
86
+ aura.light.spot(
87
+ { x: -7.5, y: 7.0, z: 9.0 },
88
+ { x: 0.38, y: -1.0, z: -0.34 },
89
+ { r: 1.0, g: 0.84, b: 0.56 },
90
+ 1.45,
91
+ 30,
92
+ 0.76,
93
+ );
94
+ aura.draw3d.setFog({
95
+ mode: 'exp2',
96
+ color: { r: 0.05, g: 0.08, b: 0.11 },
97
+ density: 0.017,
98
+ });
99
+ aura.draw3d.setOcclusionCulling({ enabled: true, conservative: true });
100
+ aura.draw3d.setPostFXPass('bloom', { strength: 0.32, radius: 0.52, threshold: 0.72 });
101
+ aura.draw3d.setPostFXPass('vignette', { strength: 0.14, radius: 0.93 });
102
+ aura.draw3d.setPostFXPass('fxaa', { strength: 1.0 });
103
+ }
104
+
77
105
  function syncSessionState() {
78
106
  collectathonSession.lastSceneId = 'gameplay';
79
107
  collectathonSession.bestScore = Math.max(Number(collectathonSession.bestScore || 0), sceneState.score);
@@ -137,8 +165,7 @@ export function createGameplayScene(context = {}) {
137
165
  materials = createCollectathonMaterials();
138
166
  playerVisual = loadPlayerAvatarVisual();
139
167
 
140
- aura.light.ambient({ r: 1, g: 1, b: 1 }, 0.23);
141
- aura.light.directional({ x: 0.55, y: -1.0, z: -0.35 }, { r: 1, g: 1, b: 1 }, 1.0);
168
+ configureSceneLook();
142
169
  aura.camera3d.perspective(62, 0.1, 150);
143
170
 
144
171
  resetRun();
@@ -174,7 +201,7 @@ export function createGameplayScene(context = {}) {
174
201
  syncSessionState();
175
202
  },
176
203
  draw() {
177
- aura.draw3d.clear3d({ r: 0.06, g: 0.08, b: 0.12 });
204
+ aura.draw3d.clear3d({ r: 0.04, g: 0.06, b: 0.085 });
178
205
  drawCollectathonCourse({
179
206
  mesh: boxMesh,
180
207
  floorMaterial: materials.floor,
@@ -1,6 +1,7 @@
1
1
  import { createSceneRegistry } from './scene-registry.js';
2
2
  import { createProjectInspector } from './project-inspector.js';
3
3
  import { assertRuntimeCapabilities } from './capabilities.js';
4
+ import { initSplash, updateSplash, drawSplash, isSplashActive } from './splash.js';
4
5
 
5
6
  export function createApp() {
6
7
  const sceneRegistry = createSceneRegistry({
@@ -26,13 +27,16 @@ export function createApp() {
26
27
  },
27
28
  setup() {
28
29
  assertRuntimeCapabilities();
30
+ initSplash();
29
31
  activeScene()?.setup?.();
30
32
  },
31
33
  update(dt) {
34
+ if (isSplashActive()) { updateSplash(dt); return; }
32
35
  projectInspector.syncInput(globalThis.aura?.input || null);
33
36
  activeScene()?.update?.(dt);
34
37
  },
35
38
  draw() {
39
+ if (isSplashActive()) { drawSplash(); return; }
36
40
  activeScene()?.draw?.();
37
41
  projectInspector.draw({ activeSceneId });
38
42
  },
@@ -11,7 +11,9 @@ export function assertRuntimeCapabilities() {
11
11
  if (!hasMethod(aura.mesh, 'createBox')) missing.push('aura.mesh.createBox');
12
12
  if (!hasMethod(aura.material, 'create')) missing.push('aura.material.create');
13
13
  if (!hasMethod(aura.light, 'ambient')) missing.push('aura.light.ambient');
14
+ if (!hasMethod(aura.light, 'hemisphere')) missing.push('aura.light.hemisphere');
14
15
  if (!hasMethod(aura.light, 'directional')) missing.push('aura.light.directional');
16
+ if (!hasMethod(aura.light, 'spot')) missing.push('aura.light.spot');
15
17
  if (!hasMethod(aura.camera3d, 'perspective')) missing.push('aura.camera3d.perspective');
16
18
  if (!hasMethod(aura.camera3d, 'setPosition')) missing.push('aura.camera3d.setPosition');
17
19
  if (!hasMethod(aura.camera3d, 'lookAt')) missing.push('aura.camera3d.lookAt');
@@ -33,6 +35,9 @@ export function assertRuntimeCapabilities() {
33
35
  if (!hasMethod(aura.scene3d, 'submitRenderBindings')) missing.push('aura.scene3d.submitRenderBindings');
34
36
  if (!hasMethod(aura.draw3d, 'clear3d')) missing.push('aura.draw3d.clear3d');
35
37
  if (!hasMethod(aura.draw3d, 'drawMesh')) missing.push('aura.draw3d.drawMesh');
38
+ if (!hasMethod(aura.draw3d, 'setFog')) missing.push('aura.draw3d.setFog');
39
+ if (!hasMethod(aura.draw3d, 'setOcclusionCulling')) missing.push('aura.draw3d.setOcclusionCulling');
40
+ if (!hasMethod(aura.draw3d, 'setPostFXPass')) missing.push('aura.draw3d.setPostFXPass');
36
41
  if (!hasMethod(aura.draw2d, 'text')) missing.push('aura.draw2d.text');
37
42
  if (!hasMethod(aura.draw2d, 'measureText')) missing.push('aura.draw2d.measureText');
38
43
  if (typeof aura.rgb !== 'function') missing.push('aura.rgb');
@@ -12,23 +12,33 @@ export function createCollectathonMaterials() {
12
12
  }),
13
13
  player: aura.material.create({
14
14
  color: { r: 0.98, g: 0.55, b: 0.26, a: 1.0 },
15
+ emissive: { r: 0.09, g: 0.03, b: 0.01 },
15
16
  metallic: 0.28,
16
17
  roughness: 0.32,
18
+ sheenColor: { r: 0.16, g: 0.1, b: 0.08 },
19
+ sheenRoughness: 0.44,
17
20
  }),
18
21
  collectible: aura.material.create({
19
22
  color: { r: 0.98, g: 0.92, b: 0.38, a: 1.0 },
23
+ emissive: { r: 0.48, g: 0.38, b: 0.1 },
20
24
  metallic: 0.7,
21
25
  roughness: 0.25,
22
26
  }),
23
27
  checkpoint: aura.material.create({
24
28
  color: { r: 0.34, g: 0.9, b: 1.0, a: 1.0 },
29
+ emissive: { r: 0.05, g: 0.24, b: 0.28 },
25
30
  metallic: 0.45,
26
31
  roughness: 0.3,
32
+ sheenColor: { r: 0.12, g: 0.18, b: 0.22 },
33
+ sheenRoughness: 0.24,
27
34
  }),
28
35
  goal: aura.material.create({
29
36
  color: { r: 0.54, g: 1.0, b: 0.7, a: 1.0 },
37
+ emissive: { r: 0.08, g: 0.26, b: 0.14 },
30
38
  metallic: 0.38,
31
39
  roughness: 0.32,
40
+ sheenColor: { r: 0.14, g: 0.2, b: 0.16 },
41
+ sheenRoughness: 0.28,
32
42
  }),
33
43
  };
34
44
  }
@@ -1,4 +1,5 @@
1
1
  // {{PROJECT_TITLE}} — AuraJS blank starter
2
+ import { initSplash, updateSplash, drawSplash, isSplashActive } from './runtime/splash.js';
2
3
 
3
4
  function hasMethod(obj, method) {
4
5
  return Boolean(obj) && typeof obj[method] === 'function';
@@ -16,13 +17,16 @@ function assertRuntimeCapabilities() {
16
17
 
17
18
  aura.setup = function () {
18
19
  assertRuntimeCapabilities();
20
+ initSplash();
19
21
  console.log('{{PROJECT_TITLE}} ready');
20
22
  };
21
23
 
22
- aura.update = function (_dt) {
24
+ aura.update = function (dt) {
25
+ if (isSplashActive()) { updateSplash(dt); return; }
23
26
  // game logic
24
27
  };
25
28
 
26
29
  aura.draw = function () {
30
+ if (isSplashActive()) { drawSplash(); return; }
27
31
  aura.draw2d.clear(aura.rgba(0.06, 0.06, 0.08, 1.0));
28
32
  };
@@ -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
+ }