@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.
- package/README.md +7 -0
- package/benchmarks/perf-thresholds.json +27 -0
- package/package.json +6 -1
- package/src/ai-guidance.mjs +302 -0
- package/src/asset-pack.mjs +2 -1
- package/src/authored-project.mjs +498 -2
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- package/src/build-contract/capabilities.mjs +87 -1
- package/src/build-contract/constants.mjs +1 -0
- package/src/build-contract.mjs +2 -0
- package/src/bundler.mjs +143 -13
- package/src/cli.mjs +681 -13
- package/src/commands/packs.mjs +741 -0
- package/src/commands/project-authoring.mjs +128 -1
- package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
- package/src/conformance/cases/core-runtime-cases.mjs +6 -2
- package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +1126 -10
- package/src/conformance-mobile.mjs +166 -0
- package/src/conformance.mjs +89 -30
- package/src/evidence-bundle.mjs +242 -0
- package/src/external-package-surface.mjs +1 -1
- package/src/headless-test/runtime-coordinator.mjs +186 -33
- package/src/headless-test.mjs +2 -0
- package/src/helpers/2d/index.mjs +183 -0
- package/src/helpers/index.mjs +26 -0
- package/src/helpers/starter-utils/adventure-objectives.js +102 -0
- package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
- package/src/helpers/starter-utils/animation-2d.js +337 -0
- package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
- package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
- package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
- package/src/helpers/starter-utils/avatar-3d.js +404 -0
- package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
- package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
- package/src/helpers/starter-utils/core.js +150 -0
- package/src/helpers/starter-utils/dialogue-2d.js +351 -0
- package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
- package/src/helpers/starter-utils/index.js +26 -0
- package/src/helpers/starter-utils/inventory-2d.js +268 -0
- package/src/helpers/starter-utils/journal-2d.js +267 -0
- package/src/helpers/starter-utils/platformer-3d.js +132 -0
- package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
- package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
- package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
- package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
- package/src/helpers/starter-utils/triggers.js +662 -0
- package/src/helpers/starter-utils/tween-2d.js +615 -0
- package/src/helpers/starter-utils/wave-director.js +101 -0
- package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
- package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
- package/src/mobile/android/build.mjs +606 -0
- package/src/mobile/android/host-artifact.mjs +280 -0
- package/src/mobile/ios/build.mjs +1323 -0
- package/src/mobile/ios/host-artifact.mjs +819 -0
- package/src/mobile/shared/capabilities.mjs +174 -0
- package/src/package-integrity.mjs +18 -4
- package/src/packs/catalog.mjs +259 -0
- package/src/perf-benchmark-runner.mjs +17 -12
- package/src/perf-benchmark.mjs +408 -4
- package/src/publish-command.mjs +434 -17
- package/src/publish-validation.mjs +22 -11
- package/src/replay-runtime.mjs +257 -0
- package/src/scaffold/config.mjs +2 -0
- package/src/scaffold/fs.mjs +8 -1
- package/src/scaffold/project-docs.mjs +101 -41
- package/src/scaffold.mjs +4 -0
- package/src/session-runtime.mjs +4 -3
- package/src/web-conformance.mjs +0 -36
- package/templates/create/2d/src/runtime/app.js +4 -0
- package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
- package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
- package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
- package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
- package/templates/create/2d-adventure/docs/design/loop.md +4 -3
- package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
- package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
- package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
- package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
- package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
- package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
- package/templates/create/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d/src/runtime/app.js +4 -0
- package/templates/create/3d/src/runtime/capabilities.js +5 -0
- package/templates/create/3d/src/runtime/materials.js +10 -0
- package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
- package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
- package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
- package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/blank/assets/splash/bg.webp +0 -0
- package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
- package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
- package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/blank/assets/splash/logoholo.webp +0 -0
- package/templates/create/blank/src/main.js +5 -1
- package/templates/create/blank/src/runtime/splash.js +305 -0
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +186 -12
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/shared/assets/splash/bg.webp +0 -0
- package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
- package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
- package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/shared/assets/splash/logoholo.webp +0 -0
- package/templates/create/shared/src/runtime/splash.js +305 -0
- package/templates/create/shared/src/runtime/ui-forms.js +552 -0
- package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
- package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
- package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
- package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
- package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
- package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
- package/templates/create/shared/src/starter-utils/index.js +15 -1
- package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
- package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
- package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
- package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
- package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
- package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
- package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
- package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
- package/templates/create/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +148 -7
- package/templates/skills/auramaxx/SKILL.md +46 -0
- package/templates/skills/auramaxx/project-requirements.md +68 -0
- package/templates/skills/auramaxx/starter-recipes.md +104 -0
- package/templates/skills/auramaxx/validation-checklist.md +49 -0
- package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/starter/assets/splash/bg.webp +0 -0
- package/templates/starter/assets/splash/boot-loop.wav +0 -0
- package/templates/starter/assets/splash/boot-sting.wav +0 -0
- package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/starter/assets/splash/logoholo.webp +0 -0
- package/templates/starter/src/main.js +4 -0
- package/templates/starter/src/runtime/splash.js +305 -0
- package/templates/skills/aurajs/SKILL.md +0 -96
- package/templates/skills/aurajs/api-contract-3d.md +0 -7
- 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
|
+
}
|