Skip to content

Commit

Permalink
fix load from asset-library
Browse files Browse the repository at this point in the history
  • Loading branch information
nighca committed Dec 20, 2024
1 parent 94f3509 commit 44b9f1c
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 57 deletions.
3 changes: 1 addition & 2 deletions spx-gui/src/models/animation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ describe('Animation', () => {
// id should be not null and not empty
expect(id).not.toBeNull()

const exportedId = animation.export({
basePath: '',
const exportedId = animation.export('', {
includeId: false,
sounds: project.sounds
})[0].builder_id
Expand Down
12 changes: 9 additions & 3 deletions spx-gui/src/models/animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export type RawAnimationConfig = {
onPlay?: ActionConfig
}

export type AnimationExportLoadOptions = {
sounds: Sound[]
includeId?: boolean
}

export class Animation extends Disposable {
id: string

Expand Down Expand Up @@ -127,7 +132,7 @@ export class Animation extends Disposable {
anitype
}: RawAnimationConfig,
costumes: Costume[],
sounds: Sound[]
{ sounds, includeId = true }: AnimationExportLoadOptions
): [animation: Animation, animationCostumeNames: string[]] {
frameFrom = frameFrom ?? from
frameTo = frameTo ?? to
Expand All @@ -149,7 +154,7 @@ export class Animation extends Disposable {
else soundId = sound.id
}
const animation = new Animation(name, {
id,
id: includeId ? id : undefined,
duration,
sound: soundId
})
Expand All @@ -165,7 +170,8 @@ export class Animation extends Disposable {

export(
/** Path of directory which contains the sprite's config file */
{ basePath, sounds, includeId = true }: { basePath: string; includeId?: boolean; sounds: Sound[] }
basePath: string,
{ sounds, includeId = true }: AnimationExportLoadOptions
): [RawAnimationConfig, RawCostumeConfig[], Files] {
const costumeConfigs: RawCostumeConfig[] = []
const files: Files = {}
Expand Down
17 changes: 14 additions & 3 deletions spx-gui/src/models/backdrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export type RawBackdropConfig = RawCostumeConfig

const backdropAssetPath = 'assets'

export type BackdropExportLoadOptions = {
includeId?: boolean
}

// Backdrop is almost the same as Costume
export class Backdrop extends Costume {
private stage: Stage | null = null
Expand Down Expand Up @@ -41,15 +45,22 @@ export class Backdrop extends Costume {
})
}

static load({ name, path, builder_id: id, ...inits }: RawBackdropConfig, files: Files) {
static load(
{ name, path, builder_id: id, ...inits }: RawBackdropConfig,
files: Files,
{ includeId = true }: BackdropExportLoadOptions = {}
) {
if (name == null) throw new Error(`name expected for backdrop`)
if (path == null) throw new Error(`path expected for backdrop ${name}`)
const file = files[resolve(backdropAssetPath, path)]
if (file == null) throw new Error(`file ${path} for backdrop ${name} not found`)
return new Backdrop(name, file, { ...inits, id })
return new Backdrop(name, file, {
...inits,
id: includeId ? id : undefined
})
}

export({ includeId = true }: { includeId?: boolean } = {}): [RawBackdropConfig, Files] {
export({ includeId = true }: BackdropExportLoadOptions = {}): [RawBackdropConfig, Files] {
return super.export({ basePath: backdropAssetPath, includeId })
}
}
18 changes: 11 additions & 7 deletions spx-gui/src/models/common/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type AssetModel<T extends AssetType = AssetType> = T extends AssetType.So

export async function sprite2Asset(sprite: Sprite): Promise<PartialAssetData> {
const { fileCollection, fileCollectionHash } = await saveFiles(
sprite.export({ includeId: false, sounds: [], includeCode: false }) // animation sound is not preserved when saving as assets
sprite.export({ sounds: [], includeId: false, includeCode: false }) // animation sound is not preserved when saving as assets
)
return {
displayName: sprite.name,
Expand All @@ -28,8 +28,12 @@ export async function sprite2Asset(sprite: Sprite): Promise<PartialAssetData> {
}

export async function asset2Sprite(assetData: PartialAssetData) {
const files = await getFiles(assetData.files)
const sprites = await Sprite.loadAll(files, [])
const files = getFiles(assetData.files)
const sprites = await Sprite.loadAll(files, {
sounds: [],
includeId: false,
includeCode: false
})
if (sprites.length === 0) throw new Error('no sprite loaded')
return sprites[0]
}
Expand All @@ -50,11 +54,11 @@ export async function backdrop2Asset(backdrop: Backdrop): Promise<PartialAssetDa
}

export async function asset2Backdrop(assetData: PartialAssetData) {
const files = await getFiles(assetData.files)
const files = getFiles(assetData.files)
const configFile = files[virtualBackdropConfigFileName]
if (configFile == null) throw new Error('no config file found')
const config = (await toConfig(configFile)) as BackdropInits
return Backdrop.load(config, files)
return Backdrop.load(config, files, { includeId: false })
}

export async function sound2Asset(sound: Sound): Promise<PartialAssetData> {
Expand All @@ -68,8 +72,8 @@ export async function sound2Asset(sound: Sound): Promise<PartialAssetData> {
}

export async function asset2Sound(assetData: PartialAssetData) {
const files = await getFiles(assetData.files)
const sounds = await Sound.loadAll(files)
const files = getFiles(assetData.files)
const sounds = await Sound.loadAll(files, { includeId: false })
if (sounds.length === 0) throw new Error('no sound loaded')
return sounds[0]
}
26 changes: 13 additions & 13 deletions spx-gui/src/models/costume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export type RawCostumeConfig = Omit<CostumeInits, 'id'> & {
path?: string
}

export type CostumeExportLoadOptions = {
/** Path of directory which contains the sprite's config file */
basePath: string
includeId?: boolean
namePrefix?: string
}

export class Costume {
id: string

Expand Down Expand Up @@ -141,26 +148,19 @@ export class Costume {
static load(
{ builder_id: id, name, path, ...inits }: RawCostumeConfig,
files: Files,
/** Path of directory which contains the sprite's config file */
basePath: string
{ basePath, includeId }: CostumeExportLoadOptions
) {
if (name == null) throw new Error(`name expected for costume`)
if (path == null) throw new Error(`path expected for costume ${name}`)
const file = files[resolve(basePath, path)]
if (file == null) throw new Error(`file ${path} for costume ${name} not found`)
return new Costume(name, file, { ...inits, id })
return new Costume(name, file, {
...inits,
id: includeId ? id : undefined
})
}

export({
basePath,
includeId = true,
namePrefix = ''
}: {
/** Path of directory which contains the sprite's config file */
basePath: string
includeId?: boolean
namePrefix?: string
}): [RawCostumeConfig, Files] {
export({ basePath, includeId = true, namePrefix = '' }: CostumeExportLoadOptions): [RawCostumeConfig, Files] {
const name = namePrefix + this.name
const filename = name + extname(this.img.name)
const config: RawCostumeConfig = {
Expand Down
2 changes: 1 addition & 1 deletion spx-gui/src/models/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export class Project extends Disposable {
} = config

const sounds = await Sound.loadAll(files)
const sprites = await Sprite.loadAll(files, sounds)
const sprites = await Sprite.loadAll(files, { sounds })

const widgets: RawWidgetConfig[] = []
const zorder: string[] = []
Expand Down
17 changes: 12 additions & 5 deletions spx-gui/src/models/sound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export type RawSoundConfig = Omit<SoundInits, 'id'> & {
export const soundAssetPath = 'assets/sounds'
export const soundConfigFileName = 'index.json'

export type SoundExportLoadOptions = {
includeId?: boolean
}

export class Sound extends Disposable {
id: string

Expand Down Expand Up @@ -70,23 +74,26 @@ export class Sound extends Disposable {
return new Sound(getSoundName(null, nameBase), adaptedFile, inits)
}

static async load(name: string, files: Files) {
static async load(name: string, files: Files, { includeId = true }: SoundExportLoadOptions = {}) {
const pathPrefix = join(soundAssetPath, name)
const configFile = files[join(pathPrefix, soundConfigFileName)]
if (configFile == null) return null
const { builder_id: id, path, ...inits } = (await toConfig(configFile)) as RawSoundConfig
if (path == null) throw new Error(`path expected for sound ${name}`)
const file = files[resolve(pathPrefix, path)]
if (file == null) throw new Error(`file ${path} for sound ${name} not found`)
return new Sound(name, file, { ...inits, id })
return new Sound(name, file, {
...inits,
id: includeId ? id : undefined
})
}

static async loadAll(files: Files) {
static async loadAll(files: Files, options?: SoundExportLoadOptions) {
const soundNames = listDirs(files, soundAssetPath)
const sounds = (
await Promise.all(
soundNames.map(async (soundName) => {
const sound = await Sound.load(soundName, files)
const sound = await Sound.load(soundName, files, options)
if (sound == null) console.warn('failed to load sound:', soundName)
return sound
})
Expand All @@ -96,7 +103,7 @@ export class Sound extends Disposable {
}

// config is included in files
export({ includeId = true }: { includeId?: boolean } = {}): Files {
export({ includeId = true }: SoundExportLoadOptions = {}): Files {
const filename = this.name + extname(this.file.name)
const config: RawSoundConfig = {
rate: this.rate,
Expand Down
4 changes: 2 additions & 2 deletions spx-gui/src/models/sprite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('Sprite', () => {
sprite.addAnimation(animation1)
sprite.addAnimation(animation2)
const exported = sprite.export({ sounds: [] })
const [loadedSprite] = await Sprite.loadAll(exported, [])
const [loadedSprite] = await Sprite.loadAll(exported, { sounds: [] })
expect(loadedSprite.costumes.map((c) => c.name)).toEqual(['costume1', 'costume2'])
expect(loadedSprite.animations.map((c) => c.name)).toEqual(['animation1', 'animation2'])
expect(loadedSprite.animations[0].costumes.map((c) => c.name)).toEqual(['costume3', 'costume4'])
Expand All @@ -35,7 +35,7 @@ describe('Sprite', () => {
expect(sprite.getAnimationBoundStates(animation.id)).toEqual([State.die, State.turn])

const exported = sprite.export({ sounds: [] })
const imported = await Sprite.loadAll(exported, [])
const imported = await Sprite.loadAll(exported, { sounds: [] })

expect(imported[0].getAnimationBoundStates(animation.id)).toEqual([State.die, State.turn])
})
Expand Down
44 changes: 23 additions & 21 deletions spx-gui/src/models/sprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ function getSpriteCodeFilePath(name: string) {

export const spriteConfigFileName = 'index.json'

export type SpriteExportLoadOptions = {
sounds: Sound[]
includeId?: boolean
includeCode?: boolean
}

export class Sprite extends Disposable {
id: string

Expand Down Expand Up @@ -286,7 +292,11 @@ export class Sprite extends Disposable {
})
}

static async load(name: string, files: Files, sounds: Sound[]) {
static async load(
name: string,
files: Files,
{ sounds, includeId = true, includeCode = true }: SpriteExportLoadOptions
) {
const pathPrefix = getSpriteAssetPath(name)
const configFile = files[join(pathPrefix, spriteConfigFileName)]
if (configFile == null) return null
Expand All @@ -303,13 +313,16 @@ export class Sprite extends Disposable {
animBindings,
...inits
} = (await toConfig(configFile)) as RawSpriteConfig
const codeFile = files[getSpriteCodeFilePath(name)]
const code = codeFile != null ? await toText(codeFile) : ''
let code = ''
if (includeCode) {
const codeFile = files[getSpriteCodeFilePath(name)]
if (codeFile != null) code = await toText(codeFile)
}

const costumes: Costume[] = []
if (costumeConfigs != null) {
for (const config of costumeConfigs) {
costumes.push(Costume.load(config, files, pathPrefix))
costumes.push(Costume.load(config, files, { basePath: pathPrefix, includeId }))
}
} else {
if (costumeSet != null) console.warn(`unsupported field: costumeSet for sprite ${name}`)
Expand All @@ -321,7 +334,7 @@ export class Sprite extends Disposable {
const animations: Animation[] = []
if (animationConfigs != null) {
for (const [name, config] of Object.entries(animationConfigs)) {
const [animation, animationCostumes] = Animation.load(name, config!, costumes, sounds)
const [animation, animationCostumes] = Animation.load(name, config!, costumes, { sounds, includeId })
animations.push(animation)
for (const c of animationCostumes) animationCostumeSet.add(c)
}
Expand All @@ -333,7 +346,7 @@ export class Sprite extends Disposable {

const sprite = new Sprite(name, code, {
...inits,
id,
id: includeId ? id : undefined,
costumeIndex: inits.costumeIndex ?? currentCostumeIndex,
animationBindings: {
[State.default]: animationNameToId(defaultAnimation),
Expand All @@ -354,12 +367,12 @@ export class Sprite extends Disposable {
return sprite
}

static async loadAll(files: Files, sounds: Sound[]) {
static async loadAll(files: Files, options: SpriteExportLoadOptions) {
const spriteNames = listDirs(files, spriteAssetPath)
const sprites = (
await Promise.all(
spriteNames.map(async (spriteName) => {
const sprite = await Sprite.load(spriteName, files, sounds)
const sprite = await Sprite.load(spriteName, files, options)
if (sprite == null) console.warn('failed to load sprite:', spriteName)
return sprite
})
Expand All @@ -368,15 +381,7 @@ export class Sprite extends Disposable {
return sprites
}

export({
includeCode = true,
includeId = true,
sounds
}: {
includeCode?: boolean
includeId?: boolean
sounds: Sound[]
}): Files {
export({ includeCode = true, includeId = true, sounds }: SpriteExportLoadOptions): Files {
const assetPath = getSpriteAssetPath(this.name)
const costumeConfigs: RawCostumeConfig[] = []
const files: Files = {}
Expand All @@ -387,10 +392,7 @@ export class Sprite extends Disposable {
}
const animationConfigs: Record<string, RawAnimationConfig> = {}
for (const a of this.animations) {
const [animationConfig, animationCostumesConfigs, animationFiles] = a.export({
basePath: assetPath,
sounds
})
const [animationConfig, animationCostumesConfigs, animationFiles] = a.export(assetPath, { sounds, includeId })
animationConfigs[a.name] = animationConfig
costumeConfigs.push(...animationCostumesConfigs)
Object.assign(files, animationFiles)
Expand Down

0 comments on commit 44b9f1c

Please sign in to comment.