@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,267 @@
1
+ function normalizeId(value, fallback) {
2
+ if (typeof value === 'string' && value.trim().length > 0) {
3
+ return value.trim();
4
+ }
5
+ return fallback;
6
+ }
7
+
8
+ function normalizeLabel(value, fallback) {
9
+ if (typeof value === 'string' && value.trim().length > 0) {
10
+ return value.trim();
11
+ }
12
+ return fallback;
13
+ }
14
+
15
+ function normalizeStatus(value, fallback = 'active') {
16
+ switch (String(value || '').trim()) {
17
+ case 'active':
18
+ case 'completed':
19
+ case 'archived':
20
+ case 'optional':
21
+ return String(value).trim();
22
+ default:
23
+ return fallback;
24
+ }
25
+ }
26
+
27
+ function normalizeCategoryEntry(entry, index) {
28
+ const fallbackId = `category-${index + 1}`;
29
+ const id = normalizeId(entry?.id, fallbackId);
30
+ return {
31
+ id,
32
+ label: normalizeLabel(entry?.label ?? entry?.name, id),
33
+ };
34
+ }
35
+
36
+ function deriveMissingCategories(entries = [], journalEntries = []) {
37
+ const categories = Array.isArray(entries)
38
+ ? entries.map((entry, index) => normalizeCategoryEntry(entry, index))
39
+ : [];
40
+ const known = new Set(categories.map((entry) => entry.id));
41
+ for (const entry of Array.isArray(journalEntries) ? journalEntries : []) {
42
+ const categoryId = normalizeId(entry?.categoryId ?? entry?.category, null);
43
+ if (!categoryId || known.has(categoryId)) continue;
44
+ categories.push({
45
+ id: categoryId,
46
+ label: normalizeLabel(entry?.categoryLabel, categoryId),
47
+ });
48
+ known.add(categoryId);
49
+ }
50
+ return categories;
51
+ }
52
+
53
+ function normalizeJournalEntry(entry, index, categories = []) {
54
+ const fallbackId = `entry-${index + 1}`;
55
+ const id = normalizeId(entry?.id, fallbackId);
56
+ const categoryId = normalizeId(entry?.categoryId ?? entry?.category, categories[0]?.id || 'journal');
57
+ const status = normalizeStatus(entry?.status, entry?.completed === true ? 'completed' : 'active');
58
+ return {
59
+ id,
60
+ label: normalizeLabel(entry?.label ?? entry?.title ?? entry?.name, id),
61
+ description: typeof entry?.description === 'string'
62
+ ? entry.description
63
+ : (typeof entry?.summary === 'string' ? entry.summary : null),
64
+ detail: typeof entry?.detail === 'string'
65
+ ? entry.detail
66
+ : (typeof entry?.body === 'string' ? entry.body : null),
67
+ badge: typeof entry?.badge === 'string' && entry.badge.trim().length > 0
68
+ ? entry.badge.trim()
69
+ : null,
70
+ categoryId,
71
+ categoryLabel: normalizeLabel(entry?.categoryLabel, categoryId),
72
+ status,
73
+ active: status === 'active',
74
+ completed: status === 'completed',
75
+ optional: status === 'optional',
76
+ archived: status === 'archived',
77
+ disabled: entry?.disabled === true,
78
+ meta: typeof entry?.meta === 'string' && entry.meta.trim().length > 0
79
+ ? entry.meta.trim()
80
+ : (typeof entry?.updatedAt === 'string' && entry.updatedAt.trim().length > 0
81
+ ? entry.updatedAt.trim()
82
+ : null),
83
+ };
84
+ }
85
+
86
+ function ensureCategory(journal, categoryId, categoryLabel = null) {
87
+ if (!journal || !Array.isArray(journal.categories)) return null;
88
+ const existing = journal.categories.find((entry) => entry.id === categoryId) || null;
89
+ if (existing) return existing;
90
+ const category = {
91
+ id: normalizeId(categoryId, `category-${journal.categories.length + 1}`),
92
+ label: normalizeLabel(categoryLabel, categoryId),
93
+ };
94
+ journal.categories.push(category);
95
+ return category;
96
+ }
97
+
98
+ function activeCategoryId(journal) {
99
+ if (!journal || !Array.isArray(journal.categories) || journal.categories.length <= 0) return null;
100
+ if (journal.selectedCategoryId && journal.categories.some((entry) => entry.id === journal.selectedCategoryId)) {
101
+ return journal.selectedCategoryId;
102
+ }
103
+ return journal.categories[0].id;
104
+ }
105
+
106
+ function firstEntryInCategory(journal, categoryId = null) {
107
+ const entries = listJournalEntries2D(journal, categoryId);
108
+ return entries[0] || null;
109
+ }
110
+
111
+ function touchJournal(journal) {
112
+ if (!journal || typeof journal !== 'object') return;
113
+ journal.revision = Number(journal.revision || 0) + 1;
114
+ }
115
+
116
+ export function createJournal2D(options = {}) {
117
+ const categories = deriveMissingCategories(options.categories, options.entries);
118
+ const journal = {
119
+ categories,
120
+ entries: [],
121
+ selectedCategoryId: categories[0]?.id || null,
122
+ selectedEntryId: null,
123
+ revision: 0,
124
+ };
125
+
126
+ for (const [index, entry] of (Array.isArray(options.entries) ? options.entries : []).entries()) {
127
+ const normalized = normalizeJournalEntry(entry, index, categories);
128
+ ensureCategory(journal, normalized.categoryId, normalized.categoryLabel);
129
+ journal.entries.push(normalized);
130
+ }
131
+
132
+ if (typeof options.selectedCategoryId === 'string') {
133
+ selectJournalCategory2D(journal, options.selectedCategoryId);
134
+ }
135
+ if (typeof options.selectedEntryId === 'string') {
136
+ selectJournalEntry2D(journal, options.selectedEntryId);
137
+ }
138
+ if (!journal.selectedEntryId) {
139
+ const first = firstEntryInCategory(journal, journal.selectedCategoryId);
140
+ journal.selectedEntryId = first?.id || null;
141
+ }
142
+
143
+ return journal;
144
+ }
145
+
146
+ export function upsertJournalEntry2D(journal, entry) {
147
+ if (!journal || !Array.isArray(journal.entries)) return null;
148
+ const normalized = normalizeJournalEntry(entry, journal.entries.length, journal.categories);
149
+ ensureCategory(journal, normalized.categoryId, normalized.categoryLabel);
150
+
151
+ const existing = journal.entries.find((candidate) => candidate.id === normalized.id) || null;
152
+ if (existing) {
153
+ Object.assign(existing, normalized);
154
+ touchJournal(journal);
155
+ if (!journal.selectedCategoryId) journal.selectedCategoryId = existing.categoryId;
156
+ if (!journal.selectedEntryId) journal.selectedEntryId = existing.id;
157
+ return existing;
158
+ }
159
+
160
+ journal.entries.push(normalized);
161
+ if (!journal.selectedCategoryId) journal.selectedCategoryId = normalized.categoryId;
162
+ if (!journal.selectedEntryId) journal.selectedEntryId = normalized.id;
163
+ touchJournal(journal);
164
+ return normalized;
165
+ }
166
+
167
+ export function setJournalEntryStatus2D(journal, entryId, status, updates = {}) {
168
+ if (!journal || !Array.isArray(journal.entries)) return false;
169
+ const targetId = normalizeId(entryId, null);
170
+ const entry = journal.entries.find((candidate) => candidate.id === targetId) || null;
171
+ if (!entry) return false;
172
+ const nextStatus = normalizeStatus(status, entry.status);
173
+ entry.status = nextStatus;
174
+ entry.active = nextStatus === 'active';
175
+ entry.completed = nextStatus === 'completed';
176
+ entry.optional = nextStatus === 'optional';
177
+ entry.archived = nextStatus === 'archived';
178
+ if (typeof updates.badge === 'string') {
179
+ entry.badge = updates.badge.trim().length > 0 ? updates.badge.trim() : null;
180
+ }
181
+ if (typeof updates.meta === 'string') {
182
+ entry.meta = updates.meta.trim().length > 0 ? updates.meta.trim() : null;
183
+ }
184
+ if (typeof updates.description === 'string') {
185
+ entry.description = updates.description;
186
+ }
187
+ if (typeof updates.detail === 'string') {
188
+ entry.detail = updates.detail;
189
+ }
190
+ touchJournal(journal);
191
+ return true;
192
+ }
193
+
194
+ export function listJournalEntries2D(journal, categoryId = null, options = {}) {
195
+ if (!journal || !Array.isArray(journal.entries)) return [];
196
+ const targetCategory = normalizeId(categoryId, null) || activeCategoryId(journal);
197
+ const statusFilter = normalizeId(options.status, null);
198
+ return journal.entries.filter((entry) => {
199
+ if (targetCategory && entry.categoryId !== targetCategory) return false;
200
+ if (statusFilter && entry.status !== statusFilter) return false;
201
+ return true;
202
+ });
203
+ }
204
+
205
+ export function selectJournalCategory2D(journal, categoryId) {
206
+ if (!journal || !Array.isArray(journal.categories)) return null;
207
+ const targetId = normalizeId(categoryId, null);
208
+ if (!targetId || !journal.categories.some((entry) => entry.id === targetId)) {
209
+ return journal.selectedCategoryId || null;
210
+ }
211
+ journal.selectedCategoryId = targetId;
212
+ const first = firstEntryInCategory(journal, targetId);
213
+ if (!first) {
214
+ journal.selectedEntryId = null;
215
+ } else if (!journal.entries.some((entry) => entry.id === journal.selectedEntryId && entry.categoryId === targetId)) {
216
+ journal.selectedEntryId = first.id;
217
+ }
218
+ touchJournal(journal);
219
+ return journal.selectedCategoryId;
220
+ }
221
+
222
+ export function selectJournalEntry2D(journal, entryId) {
223
+ if (!journal || !Array.isArray(journal.entries)) return null;
224
+ const targetId = normalizeId(entryId, null);
225
+ const entry = journal.entries.find((candidate) => candidate.id === targetId) || null;
226
+ if (!entry) return journal.selectedEntryId || null;
227
+ journal.selectedCategoryId = entry.categoryId;
228
+ journal.selectedEntryId = entry.id;
229
+ touchJournal(journal);
230
+ return journal.selectedEntryId;
231
+ }
232
+
233
+ export function getSelectedJournalEntry2D(journal) {
234
+ if (!journal || !Array.isArray(journal.entries)) return null;
235
+ return journal.entries.find((entry) => entry.id === journal.selectedEntryId) || null;
236
+ }
237
+
238
+ export function getJournalView2D(journal) {
239
+ const categories = Array.isArray(journal?.categories) ? journal.categories : [];
240
+ const entries = listJournalEntries2D(journal);
241
+ const selectedEntry = getSelectedJournalEntry2D(journal);
242
+ return {
243
+ selectedCategoryId: activeCategoryId(journal),
244
+ selectedEntryId: selectedEntry?.id || null,
245
+ categories: categories.map((entry) => ({
246
+ id: entry.id,
247
+ label: entry.label,
248
+ selected: entry.id === activeCategoryId(journal),
249
+ count: listJournalEntries2D(journal, entry.id).length,
250
+ activeCount: listJournalEntries2D(journal, entry.id, { status: 'active' }).length,
251
+ completedCount: listJournalEntries2D(journal, entry.id, { status: 'completed' }).length,
252
+ })),
253
+ entries: entries.map((entry) => ({
254
+ id: entry.id,
255
+ label: entry.label,
256
+ description: entry.description,
257
+ detail: entry.detail,
258
+ badge: entry.badge || (entry.completed ? 'Completed' : entry.active ? 'Active' : normalizeLabel(entry.status, 'Journal')),
259
+ status: entry.status,
260
+ selected: entry.id === selectedEntry?.id,
261
+ disabled: entry.disabled === true,
262
+ meta: entry.meta,
263
+ })),
264
+ selectedEntry,
265
+ revision: Number(journal?.revision || 0),
266
+ };
267
+ }
@@ -0,0 +1,236 @@
1
+ function finiteNumber(value, fallback) {
2
+ const numeric = Number(value)
3
+ return Number.isFinite(numeric) ? numeric : fallback
4
+ }
5
+
6
+ function nonNegativeNumber(value, fallback) {
7
+ return Math.max(0, finiteNumber(value, fallback))
8
+ }
9
+
10
+ function hasAudioMethod(sceneAudio, name) {
11
+ return !!sceneAudio?.aura?.audio
12
+ && sceneAudio.aura.audio.supported !== false
13
+ && typeof sceneAudio.aura.audio[name] === 'function'
14
+ }
15
+
16
+ function mixerTracksHandle(sceneAudio, handle) {
17
+ if (handle == null || !hasAudioMethod(sceneAudio, 'getMixerState')) {
18
+ return true
19
+ }
20
+ try {
21
+ const mixer = sceneAudio.aura.audio.getMixerState()
22
+ return Array.isArray(mixer?.tracks)
23
+ ? mixer.tracks.some((track) => Number(track?.handle) === Number(handle))
24
+ : true
25
+ } catch {
26
+ return true
27
+ }
28
+ }
29
+
30
+ export function createSceneAudio2D(aura, options = {}) {
31
+ return {
32
+ aura,
33
+ configured: false,
34
+ musicHandle: null,
35
+ musicPath: '',
36
+ musicBus: typeof options.musicBus === 'string' && options.musicBus.trim().length > 0
37
+ ? options.musicBus.trim()
38
+ : 'music',
39
+ sfxBus: typeof options.sfxBus === 'string' && options.sfxBus.trim().length > 0
40
+ ? options.sfxBus.trim()
41
+ : 'sfx',
42
+ busVolumes: {
43
+ music: nonNegativeNumber(options.musicBusVolume, 0.18),
44
+ sfx: nonNegativeNumber(options.sfxBusVolume, 0.82),
45
+ },
46
+ defaultMusicOptions: {
47
+ volume: nonNegativeNumber(options.defaultMusicOptions?.volume, 0.58),
48
+ pitch: finiteNumber(options.defaultMusicOptions?.pitch, 0.98),
49
+ },
50
+ defaultStingerOptions: {
51
+ volume: nonNegativeNumber(options.defaultStingerOptions?.volume, 0.84),
52
+ pitch: finiteNumber(options.defaultStingerOptions?.pitch, 1),
53
+ },
54
+ musicVolume: -1,
55
+ pauseVolume: nonNegativeNumber(options.pauseVolume, 0.08),
56
+ paused: false,
57
+ duckTimer: 0,
58
+ duckVolume: null,
59
+ }
60
+ }
61
+
62
+ export function configureSceneAudio2D(sceneAudio) {
63
+ if (!sceneAudio || sceneAudio.configured || !sceneAudio.aura?.audio) {
64
+ return !!sceneAudio?.aura?.audio
65
+ }
66
+ sceneAudio.configured = true
67
+ if (hasAudioMethod(sceneAudio, 'setBusVolume')) {
68
+ try {
69
+ sceneAudio.aura.audio.setBusVolume(sceneAudio.musicBus, sceneAudio.busVolumes.music)
70
+ } catch {}
71
+ try {
72
+ sceneAudio.aura.audio.setBusVolume(sceneAudio.sfxBus, sceneAudio.busVolumes.sfx)
73
+ } catch {}
74
+ }
75
+ return true
76
+ }
77
+
78
+ export function setSceneMusicVolume2D(sceneAudio, volume) {
79
+ if (!sceneAudio || !hasAudioMethod(sceneAudio, 'setBusVolume')) {
80
+ return
81
+ }
82
+ const resolvedVolume = nonNegativeNumber(volume, sceneAudio.busVolumes.music)
83
+ if (Math.abs(sceneAudio.musicVolume - resolvedVolume) <= 0.01) {
84
+ return
85
+ }
86
+ sceneAudio.musicVolume = resolvedVolume
87
+ try {
88
+ sceneAudio.aura.audio.setBusVolume(sceneAudio.musicBus, resolvedVolume)
89
+ } catch {}
90
+ }
91
+
92
+ export function stopSceneAudio2D(sceneAudio, options = {}) {
93
+ if (!sceneAudio) {
94
+ return
95
+ }
96
+ if (sceneAudio.musicHandle != null && hasAudioMethod(sceneAudio, 'stop')) {
97
+ try {
98
+ sceneAudio.aura.audio.stop(sceneAudio.musicHandle)
99
+ } catch {}
100
+ }
101
+ sceneAudio.musicHandle = null
102
+ sceneAudio.musicPath = ''
103
+ sceneAudio.duckTimer = 0
104
+ sceneAudio.duckVolume = null
105
+ if (options.clearPause !== false) {
106
+ sceneAudio.paused = false
107
+ }
108
+ if (options.resetConfiguration === true) {
109
+ sceneAudio.musicVolume = -1
110
+ sceneAudio.configured = false
111
+ }
112
+ }
113
+
114
+ export function ensureSceneMusic2D(sceneAudio, path, options = {}) {
115
+ configureSceneAudio2D(sceneAudio)
116
+ if (!sceneAudio || typeof path !== 'string' || path.trim().length === 0 || !hasAudioMethod(sceneAudio, 'play')) {
117
+ return null
118
+ }
119
+ const resolvedPath = path.trim()
120
+ if (
121
+ sceneAudio.musicHandle != null
122
+ && sceneAudio.musicPath === resolvedPath
123
+ && mixerTracksHandle(sceneAudio, sceneAudio.musicHandle)
124
+ ) {
125
+ return sceneAudio.musicHandle
126
+ }
127
+ stopSceneAudio2D(sceneAudio, {
128
+ clearPause: false,
129
+ resetConfiguration: false,
130
+ })
131
+ try {
132
+ sceneAudio.musicHandle = sceneAudio.aura.audio.play(resolvedPath, {
133
+ loop: true,
134
+ bus: sceneAudio.musicBus,
135
+ ...sceneAudio.defaultMusicOptions,
136
+ ...options,
137
+ })
138
+ sceneAudio.musicPath = resolvedPath
139
+ } catch {}
140
+ return sceneAudio.musicHandle
141
+ }
142
+
143
+ export function duckSceneMusic2D(sceneAudio, options = {}) {
144
+ if (!sceneAudio) {
145
+ return
146
+ }
147
+ const duration = nonNegativeNumber(options.duration, 0.16)
148
+ const volume = nonNegativeNumber(options.volume, 0.14)
149
+ sceneAudio.duckTimer = Math.max(sceneAudio.duckTimer, duration)
150
+ sceneAudio.duckVolume = sceneAudio.duckVolume == null
151
+ ? volume
152
+ : Math.min(sceneAudio.duckVolume, volume)
153
+ }
154
+
155
+ export function playSceneStinger2D(sceneAudio, path, options = {}) {
156
+ configureSceneAudio2D(sceneAudio)
157
+ if (!sceneAudio || typeof path !== 'string' || path.trim().length === 0 || !hasAudioMethod(sceneAudio, 'play')) {
158
+ return null
159
+ }
160
+ const resolvedPath = path.trim()
161
+ const playOptions = {
162
+ loop: false,
163
+ bus: sceneAudio.sfxBus,
164
+ ...sceneAudio.defaultStingerOptions,
165
+ ...options,
166
+ }
167
+ delete playOptions.duck
168
+ let handle = null
169
+ try {
170
+ handle = sceneAudio.aura.audio.play(resolvedPath, playOptions)
171
+ } catch {}
172
+ if (options.duck) {
173
+ duckSceneMusic2D(sceneAudio, options.duck === true ? {} : options.duck)
174
+ }
175
+ return handle
176
+ }
177
+
178
+ export function pauseSceneAudio2D(sceneAudio, options = {}) {
179
+ if (!sceneAudio) {
180
+ return
181
+ }
182
+ sceneAudio.paused = true
183
+ sceneAudio.pauseVolume = nonNegativeNumber(options.volume, sceneAudio.pauseVolume)
184
+ if (options.pauseMusic !== false && sceneAudio.musicHandle != null && hasAudioMethod(sceneAudio, 'pause')) {
185
+ try {
186
+ sceneAudio.aura.audio.pause(sceneAudio.musicHandle)
187
+ } catch {}
188
+ }
189
+ }
190
+
191
+ export function resumeSceneAudio2D(sceneAudio, options = {}) {
192
+ if (!sceneAudio) {
193
+ return
194
+ }
195
+ sceneAudio.paused = false
196
+ if (options.resumeMusic !== false && sceneAudio.musicHandle != null && hasAudioMethod(sceneAudio, 'resume')) {
197
+ try {
198
+ sceneAudio.aura.audio.resume(sceneAudio.musicHandle)
199
+ } catch {}
200
+ }
201
+ }
202
+
203
+ export function syncSceneAudio2D(sceneAudio, dt, options = {}) {
204
+ configureSceneAudio2D(sceneAudio)
205
+ if (!sceneAudio) {
206
+ return null
207
+ }
208
+ const safeDt = nonNegativeNumber(dt, 0)
209
+ if (hasAudioMethod(sceneAudio, 'update')) {
210
+ try {
211
+ sceneAudio.aura.audio.update(safeDt)
212
+ } catch {}
213
+ }
214
+ if (typeof options.musicPath === 'string' && options.musicPath.trim().length > 0) {
215
+ ensureSceneMusic2D(sceneAudio, options.musicPath, options.musicOptions)
216
+ }
217
+
218
+ if (sceneAudio.duckTimer > 0) {
219
+ sceneAudio.duckTimer = Math.max(0, sceneAudio.duckTimer - safeDt)
220
+ if (sceneAudio.duckTimer <= 0) {
221
+ sceneAudio.duckVolume = null
222
+ }
223
+ }
224
+
225
+ let targetVolume = options.musicVolume != null
226
+ ? nonNegativeNumber(options.musicVolume, sceneAudio.busVolumes.music)
227
+ : sceneAudio.busVolumes.music
228
+ if (sceneAudio.paused) {
229
+ targetVolume = Math.min(targetVolume, sceneAudio.pauseVolume)
230
+ }
231
+ if (sceneAudio.duckTimer > 0 && sceneAudio.duckVolume != null) {
232
+ targetVolume = Math.min(targetVolume, sceneAudio.duckVolume)
233
+ }
234
+ setSceneMusicVolume2D(sceneAudio, targetVolume)
235
+ return sceneAudio.musicHandle
236
+ }