@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,203 @@
1
+ import {
2
+ createSpriteAnimationLibrary2D,
3
+ createSpriteAnimator2D,
4
+ hasSpriteAnimationState2D,
5
+ registerSpriteAnimationLibrary2D,
6
+ } from './animation-2d.js'
7
+
8
+ let nextAnimationPackageId = 1
9
+
10
+ function normalizePackageName(value) {
11
+ if (typeof value === 'string' && value.trim().length > 0) {
12
+ return value.trim()
13
+ }
14
+ return `animation-package-${nextAnimationPackageId}`
15
+ }
16
+
17
+ function normalizePackageClipName(value) {
18
+ const name = typeof value === 'string' ? value.trim() : ''
19
+ if (!name) {
20
+ throw new Error('animation package clip names must be non-empty strings.')
21
+ }
22
+ return name
23
+ }
24
+
25
+ function normalizePackageStateName(value) {
26
+ const name = typeof value === 'string' ? value.trim() : ''
27
+ if (!name) {
28
+ throw new Error('animation package state names must be non-empty strings.')
29
+ }
30
+ return name
31
+ }
32
+
33
+ function normalizePackageStateSpec(stateName, spec, knownClips) {
34
+ if (typeof spec === 'string') {
35
+ const clip = spec.trim()
36
+ if (!knownClips.has(clip)) {
37
+ throw new Error(`animation package state "${stateName}" references unknown clip "${clip}".`)
38
+ }
39
+ return { clip }
40
+ }
41
+
42
+ if (!spec || typeof spec !== 'object') {
43
+ throw new Error(`animation package state "${stateName}" must be a clip name or object.`)
44
+ }
45
+
46
+ const clip = typeof spec.clip === 'string' ? spec.clip.trim() : ''
47
+ if (!clip) {
48
+ throw new Error(`animation package state "${stateName}" must declare a clip.`)
49
+ }
50
+ if (!knownClips.has(clip)) {
51
+ throw new Error(`animation package state "${stateName}" references unknown clip "${clip}".`)
52
+ }
53
+
54
+ const state = { clip }
55
+ if (spec.frameDuration != null) {
56
+ state.frameDuration = spec.frameDuration
57
+ }
58
+ if (spec.loop != null) {
59
+ state.loop = spec.loop
60
+ }
61
+ return state
62
+ }
63
+
64
+ function cloneClipSpec(clipName, spec) {
65
+ if (Array.isArray(spec)) {
66
+ if (spec.length === 0) {
67
+ throw new Error(`animation package clip "${clipName}" must include at least one frame.`)
68
+ }
69
+ return { frames: [...spec] }
70
+ }
71
+
72
+ if (Number.isInteger(spec) && spec >= 0) {
73
+ return { frame: spec }
74
+ }
75
+
76
+ if (!spec || typeof spec !== 'object') {
77
+ throw new Error(`animation package clip "${clipName}" must be an integer, frame array, or object.`)
78
+ }
79
+
80
+ const cloned = { ...spec }
81
+ if (Array.isArray(spec.frames)) {
82
+ if (spec.frames.length === 0) {
83
+ throw new Error(`animation package clip "${clipName}" must include at least one frame.`)
84
+ }
85
+ cloned.frames = [...spec.frames]
86
+ }
87
+ return cloned
88
+ }
89
+
90
+ function buildLibraryStates(packageDefinition) {
91
+ const states = {}
92
+ for (const [stateName, stateSpec] of Object.entries(packageDefinition.states)) {
93
+ const clipSpec = cloneClipSpec(stateSpec.clip, packageDefinition.clips[stateSpec.clip])
94
+ if (stateSpec.frameDuration != null) {
95
+ clipSpec.frameDuration = stateSpec.frameDuration
96
+ }
97
+ if (stateSpec.loop != null) {
98
+ clipSpec.loop = stateSpec.loop
99
+ }
100
+ states[stateName] = clipSpec
101
+ }
102
+ return states
103
+ }
104
+
105
+ export function createAnimationPackage2D(options = {}) {
106
+ const rawClips = options.clips && typeof options.clips === 'object' ? options.clips : null
107
+ const rawStates = options.states && typeof options.states === 'object' ? options.states : null
108
+
109
+ if (!rawClips || Object.keys(rawClips).length === 0) {
110
+ throw new Error('animation packages must declare at least one clip.')
111
+ }
112
+ if (!rawStates || Object.keys(rawStates).length === 0) {
113
+ throw new Error('animation packages must declare at least one state.')
114
+ }
115
+
116
+ const packageId = `anim-pack-${nextAnimationPackageId}`
117
+ nextAnimationPackageId += 1
118
+
119
+ const name = normalizePackageName(options.name)
120
+ const clips = {}
121
+ for (const [rawClipName, spec] of Object.entries(rawClips)) {
122
+ const clipName = normalizePackageClipName(rawClipName)
123
+ clips[clipName] = cloneClipSpec(clipName, spec)
124
+ }
125
+
126
+ const knownClips = new Set(Object.keys(clips))
127
+ const states = {}
128
+ for (const [rawStateName, spec] of Object.entries(rawStates)) {
129
+ const stateName = normalizePackageStateName(rawStateName)
130
+ states[stateName] = normalizePackageStateSpec(stateName, spec, knownClips)
131
+ }
132
+
133
+ const defaultState = (
134
+ typeof options.defaultState === 'string'
135
+ && Object.prototype.hasOwnProperty.call(states, options.defaultState.trim())
136
+ ? options.defaultState.trim()
137
+ : Object.keys(states)[0]
138
+ )
139
+ const source = options.source && typeof options.source === 'object'
140
+ ? { ...options.source }
141
+ : { type: 'inline-animation-package' }
142
+
143
+ const library = createSpriteAnimationLibrary2D({
144
+ image: options.image,
145
+ frameWidth: options.frameWidth,
146
+ frameHeight: options.frameHeight,
147
+ columns: options.columns,
148
+ anchorX: options.anchorX,
149
+ anchorY: options.anchorY,
150
+ defaultFrameDuration: options.defaultFrameDuration,
151
+ defaultState,
152
+ name,
153
+ states: buildLibraryStates({ clips, states }),
154
+ })
155
+
156
+ return {
157
+ packageId,
158
+ name,
159
+ defaultState: library.defaultState,
160
+ clips,
161
+ states,
162
+ library,
163
+ source,
164
+ }
165
+ }
166
+
167
+ export function hasAnimationPackageState2D(animationPackage, stateName) {
168
+ return !!animationPackage && typeof stateName === 'string'
169
+ && Object.prototype.hasOwnProperty.call(animationPackage.states || {}, stateName)
170
+ }
171
+
172
+ export function registerAnimationPackage2D(runtime, animationPackage) {
173
+ if (!animationPackage?.library) {
174
+ throw new Error('animation package is required.')
175
+ }
176
+ return registerSpriteAnimationLibrary2D(runtime, animationPackage.library)
177
+ }
178
+
179
+ export function createPackagedSpriteAnimator2D(runtime, animationPackage, options = {}) {
180
+ if (registerAnimationPackage2D(runtime, animationPackage) !== true) {
181
+ throw new Error(`failed to register animation package "${animationPackage?.packageId || 'unknown'}".`)
182
+ }
183
+
184
+ const initialState = hasAnimationPackageState2D(animationPackage, options.initialState)
185
+ ? options.initialState
186
+ : animationPackage.defaultState
187
+ const animator = createSpriteAnimator2D(runtime, animationPackage.library, { initialState })
188
+
189
+ return {
190
+ ...animator,
191
+ packageId: animationPackage.packageId,
192
+ }
193
+ }
194
+
195
+ export function getAnimationPackageLibrary2D(animationPackage) {
196
+ return animationPackage?.library || null
197
+ }
198
+
199
+ export function getAnimationPackageState2D(animationPackage, stateName) {
200
+ return hasAnimationPackageState2D(animationPackage, stateName)
201
+ ? animationPackage.states[stateName]
202
+ : null
203
+ }
@@ -0,0 +1,111 @@
1
+ import { createAnimationPackage2D } from './animation-packaging-2d.js'
2
+
3
+ let nextAtlasAssetManifestId = 1
4
+
5
+ function cloneSpecMap(value, label) {
6
+ if (!value || typeof value !== 'object') {
7
+ throw new Error(`${label} must be an object.`)
8
+ }
9
+ const entries = Object.entries(value)
10
+ if (entries.length === 0) {
11
+ throw new Error(`${label} must not be empty.`)
12
+ }
13
+ return Object.fromEntries(entries.map(([key, spec]) => {
14
+ if (Array.isArray(spec)) {
15
+ return [key, [...spec]]
16
+ }
17
+ if (spec && typeof spec === 'object') {
18
+ return [key, { ...spec, frames: Array.isArray(spec.frames) ? [...spec.frames] : spec.frames }]
19
+ }
20
+ return [key, spec]
21
+ }))
22
+ }
23
+
24
+ function readPositiveNumber(value, fallback, label) {
25
+ const numeric = Number(value)
26
+ const resolved = Number.isFinite(numeric) ? numeric : fallback
27
+ if (!(resolved > 0)) {
28
+ throw new Error(`${label} must be a positive number.`)
29
+ }
30
+ return resolved
31
+ }
32
+
33
+ function readFiniteNumber(value, fallback) {
34
+ const numeric = Number(value)
35
+ return Number.isFinite(numeric) ? numeric : fallback
36
+ }
37
+
38
+ function normalizeAtlasManifestName(value, fallback) {
39
+ if (typeof value === 'string' && value.trim().length > 0) {
40
+ return value.trim()
41
+ }
42
+ return fallback
43
+ }
44
+
45
+ export function createAtlasAssetManifest2D(options = {}) {
46
+ const image = typeof options.image === 'string' && options.image.trim().length > 0
47
+ ? options.image.trim()
48
+ : null
49
+ if (!image) {
50
+ throw new Error('atlas asset manifest image is required.')
51
+ }
52
+
53
+ const manifest = options.manifest && typeof options.manifest === 'object'
54
+ ? options.manifest
55
+ : options
56
+ const name = normalizeAtlasManifestName(
57
+ options.name ?? manifest.name,
58
+ `atlas-asset-manifest-${nextAtlasAssetManifestId}`,
59
+ )
60
+ const atlasManifestId = `atlas-asset-manifest-${nextAtlasAssetManifestId}`
61
+ nextAtlasAssetManifestId += 1
62
+
63
+ return {
64
+ atlasManifestId,
65
+ assetType: 'atlasAssetManifest2d',
66
+ name,
67
+ image,
68
+ frameWidth: readPositiveNumber(manifest.frameWidth, 0, 'atlas asset manifest frameWidth'),
69
+ frameHeight: readPositiveNumber(manifest.frameHeight, 0, 'atlas asset manifest frameHeight'),
70
+ columns: Math.max(1, Math.floor(readPositiveNumber(manifest.columns, 1, 'atlas asset manifest columns'))),
71
+ defaultFrameDuration: readPositiveNumber(
72
+ manifest.defaultFrameDuration,
73
+ 1 / 12,
74
+ 'atlas asset manifest defaultFrameDuration',
75
+ ),
76
+ anchorX: readFiniteNumber(manifest.anchorX, 0.5),
77
+ anchorY: readFiniteNumber(manifest.anchorY, 0.5),
78
+ defaultState: typeof manifest.defaultState === 'string' ? manifest.defaultState.trim() : '',
79
+ clips: cloneSpecMap(manifest.clips, 'atlas asset manifest clips'),
80
+ states: cloneSpecMap(manifest.states, 'atlas asset manifest states'),
81
+ }
82
+ }
83
+
84
+ export function createAnimationPackageFromAtlasAsset2D(atlasAsset, overrides = {}) {
85
+ if (!atlasAsset?.atlasManifestId) {
86
+ throw new Error('atlas asset manifest is required.')
87
+ }
88
+
89
+ return createAnimationPackage2D({
90
+ name: normalizeAtlasManifestName(overrides.name, atlasAsset.name),
91
+ image: typeof overrides.image === 'string' && overrides.image.trim().length > 0
92
+ ? overrides.image.trim()
93
+ : atlasAsset.image,
94
+ frameWidth: overrides.frameWidth ?? atlasAsset.frameWidth,
95
+ frameHeight: overrides.frameHeight ?? atlasAsset.frameHeight,
96
+ columns: overrides.columns ?? atlasAsset.columns,
97
+ anchorX: overrides.anchorX ?? atlasAsset.anchorX,
98
+ anchorY: overrides.anchorY ?? atlasAsset.anchorY,
99
+ defaultFrameDuration: overrides.defaultFrameDuration ?? atlasAsset.defaultFrameDuration,
100
+ clips: cloneSpecMap(atlasAsset.clips, 'atlas asset manifest clips'),
101
+ states: cloneSpecMap(atlasAsset.states, 'atlas asset manifest states'),
102
+ defaultState: typeof overrides.defaultState === 'string' && overrides.defaultState.trim().length > 0
103
+ ? overrides.defaultState.trim()
104
+ : atlasAsset.defaultState,
105
+ source: {
106
+ type: 'atlas-asset-manifest',
107
+ atlasManifestId: atlasAsset.atlasManifestId,
108
+ image: atlasAsset.image,
109
+ },
110
+ })
111
+ }
@@ -0,0 +1,215 @@
1
+ function finite(value, fallback = 0) {
2
+ const numeric = Number(value);
3
+ return Number.isFinite(numeric) ? numeric : fallback;
4
+ }
5
+
6
+ function keyName(value, fallback) {
7
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : fallback;
8
+ }
9
+
10
+ function clamp(value, min, max) {
11
+ return Math.max(min, Math.min(max, value));
12
+ }
13
+
14
+ function normalizeAutoplayAxis(axis) {
15
+ return {
16
+ x: clamp(finite(axis?.x, 0), -1, 1),
17
+ y: clamp(finite(axis?.y, 0), -1, 1),
18
+ };
19
+ }
20
+
21
+ function normalizeAutoplayControl(control) {
22
+ return {
23
+ axis: normalizeAutoplayAxis(control?.axis),
24
+ aimAngle: Number.isFinite(Number(control?.aimAngle)) ? Number(control.aimAngle) : null,
25
+ fire: control?.fire === true,
26
+ dash: control?.dash === true,
27
+ extract: control?.extract === true,
28
+ weaponIndex: Number.isFinite(Number(control?.weaponIndex))
29
+ ? Math.max(0, Math.floor(Number(control.weaponIndex)))
30
+ : null,
31
+ };
32
+ }
33
+
34
+ export function createAutoplayController2D(options = {}) {
35
+ return {
36
+ enabled: options.enabled === true,
37
+ frameCount: Math.max(0, Math.floor(finite(options.frameCount, 0))),
38
+ lastReasonCode: keyName(options.lastReasonCode, 'autoplay_idle'),
39
+ lastControl: options.lastControl ? normalizeAutoplayControl(options.lastControl) : null,
40
+ buildControl: typeof options.buildControl === 'function' ? options.buildControl : null,
41
+ };
42
+ }
43
+
44
+ export function setAutoplayEnabled2D(controller, enabled = true) {
45
+ if (!controller || typeof controller !== 'object') {
46
+ throw new Error('autoplay controller is required.');
47
+ }
48
+ controller.enabled = enabled === true;
49
+ controller.lastReasonCode = controller.enabled ? 'autoplay_enabled' : 'autoplay_disabled';
50
+ return {
51
+ ok: true,
52
+ enabled: controller.enabled,
53
+ reasonCode: controller.lastReasonCode,
54
+ };
55
+ }
56
+
57
+ export function toggleAutoplayEnabled2D(controller) {
58
+ if (!controller || typeof controller !== 'object') {
59
+ throw new Error('autoplay controller is required.');
60
+ }
61
+ return setAutoplayEnabled2D(controller, controller.enabled !== true);
62
+ }
63
+
64
+ export function resolveAutoplayControl2D(controller, context = {}, buildControl = null) {
65
+ if (!controller || typeof controller !== 'object') {
66
+ throw new Error('autoplay controller is required.');
67
+ }
68
+ if (controller.enabled !== true) {
69
+ controller.lastReasonCode = 'autoplay_disabled';
70
+ return {
71
+ ok: true,
72
+ enabled: false,
73
+ reasonCode: controller.lastReasonCode,
74
+ frameCount: controller.frameCount,
75
+ control: null,
76
+ };
77
+ }
78
+
79
+ const builder = typeof buildControl === 'function' ? buildControl : controller.buildControl;
80
+ if (typeof builder !== 'function') {
81
+ controller.lastReasonCode = 'autoplay_builder_missing';
82
+ return {
83
+ ok: false,
84
+ enabled: true,
85
+ reasonCode: controller.lastReasonCode,
86
+ frameCount: controller.frameCount,
87
+ control: null,
88
+ };
89
+ }
90
+
91
+ try {
92
+ controller.frameCount += 1;
93
+ const control = normalizeAutoplayControl(builder(context, controller) || {});
94
+ controller.lastControl = control;
95
+ controller.lastReasonCode = 'autoplay_control_ready';
96
+ return {
97
+ ok: true,
98
+ enabled: true,
99
+ reasonCode: controller.lastReasonCode,
100
+ frameCount: controller.frameCount,
101
+ control,
102
+ };
103
+ } catch (error) {
104
+ controller.lastReasonCode = 'autoplay_builder_failed';
105
+ return {
106
+ ok: false,
107
+ enabled: true,
108
+ reasonCode: controller.lastReasonCode,
109
+ frameCount: controller.frameCount,
110
+ control: controller.lastControl,
111
+ error: String(error && error.message ? error.message : error),
112
+ };
113
+ }
114
+ }
115
+
116
+ export function createRenderTargetExportState2D(options = {}) {
117
+ return {
118
+ queued: options.queued === true,
119
+ path: keyName(options.path, 'aura-export.png'),
120
+ lastExportReason: keyName(options.lastExportReason, 'draw2d_export_idle'),
121
+ lastExportOk: options.lastExportOk !== false,
122
+ exportCount: Math.max(0, Math.floor(finite(options.exportCount, 0))),
123
+ };
124
+ }
125
+
126
+ export function queueRenderTargetExport2D(exportState, options = {}) {
127
+ if (!exportState || typeof exportState !== 'object') {
128
+ throw new Error('render target export state is required.');
129
+ }
130
+ exportState.queued = true;
131
+ exportState.path = keyName(options.path, exportState.path || 'aura-export.png');
132
+ return {
133
+ ok: true,
134
+ queued: true,
135
+ path: exportState.path,
136
+ reasonCode: 'draw2d_render_target_export_queued',
137
+ };
138
+ }
139
+
140
+ export function flushRenderTargetExport2D(exportState, renderTarget, aura, options = {}) {
141
+ if (!exportState || typeof exportState !== 'object') {
142
+ throw new Error('render target export state is required.');
143
+ }
144
+
145
+ const path = keyName(options.path, exportState.path || 'aura-export.png');
146
+ exportState.path = path;
147
+
148
+ if (exportState.queued !== true) {
149
+ return {
150
+ ok: true,
151
+ exported: false,
152
+ queued: false,
153
+ path,
154
+ reasonCode: 'draw2d_render_target_export_not_queued',
155
+ };
156
+ }
157
+
158
+ exportState.queued = false;
159
+
160
+ if (!renderTarget || typeof renderTarget !== 'object') {
161
+ exportState.lastExportReason = 'invalid_export_target';
162
+ exportState.lastExportOk = false;
163
+ return {
164
+ ok: false,
165
+ exported: false,
166
+ queued: false,
167
+ path,
168
+ reasonCode: exportState.lastExportReason,
169
+ };
170
+ }
171
+
172
+ if (!aura?.draw2d || typeof aura.draw2d.exportRenderTarget !== 'function') {
173
+ exportState.lastExportReason = 'draw2d_export_unavailable';
174
+ exportState.lastExportOk = false;
175
+ return {
176
+ ok: false,
177
+ exported: false,
178
+ queued: false,
179
+ path,
180
+ reasonCode: exportState.lastExportReason,
181
+ };
182
+ }
183
+
184
+ try {
185
+ const result = aura.draw2d.exportRenderTarget(renderTarget, path) || null;
186
+ const reasonCode = keyName(result?.reasonCode, 'draw2d_render_target_export_queued');
187
+ const ok = result?.ok !== false
188
+ && reasonCode !== 'unsupported_export_format'
189
+ && reasonCode !== 'invalid_export_target';
190
+ exportState.lastExportReason = reasonCode;
191
+ exportState.lastExportOk = ok;
192
+ if (ok) {
193
+ exportState.exportCount += 1;
194
+ }
195
+ return {
196
+ ok,
197
+ exported: ok,
198
+ queued: false,
199
+ path: keyName(result?.path, path),
200
+ reasonCode,
201
+ result,
202
+ };
203
+ } catch (error) {
204
+ exportState.lastExportReason = 'draw2d_render_target_export_failed';
205
+ exportState.lastExportOk = false;
206
+ return {
207
+ ok: false,
208
+ exported: false,
209
+ queued: false,
210
+ path,
211
+ reasonCode: exportState.lastExportReason,
212
+ error: String(error && error.message ? error.message : error),
213
+ };
214
+ }
215
+ }