import { stat } from 'node:fs/promises' import { build } from 'esbuild' const bundled = await build({ bundle: true, entryPoints: ['src/core/particleResources.ts'], format: 'esm', platform: 'node', write: false, }) const module = await import( `data:text/javascript;base64,${Buffer.from(bundled.outputFiles[0].text).toString('base64')}` ) const expectedNames = Array.from( { length: 15 }, (_, index) => `yanhua_${String(index + 1).padStart(2, '0')}.png`, ) if (module.DEFAULT_SEQUENCE_FPS !== 15) { throw new Error('默认序列帧播放帧率必须为 15 FPS') } if (JSON.stringify(module.DEFAULT_SEQUENCE_FRAME_NAMES) !== JSON.stringify(expectedNames)) { throw new Error('默认序列帧顺序不正确') } const resource = { imageMode: 'sequence', sequenceFrames: [], textureName: 'star', textureFolder: '', previewUrl: '/images/star.png', } if (!module.ensureDefaultSequenceFrames(resource)) { throw new Error('序列帧模式应自动填充默认资源') } if (resource.sequenceFrames.length !== expectedNames.length) { throw new Error('默认序列帧数量不正确') } if (resource.textureName !== 'yanhua' || resource.textureFolder !== '') { throw new Error('默认序列帧资源名称或子文件夹不正确') } for (const [index, frame] of resource.sequenceFrames.entries()) { const expectedName = expectedNames[index] if (frame.name !== expectedName || !frame.previewUrl.endsWith(`/yanhua/${expectedName}`)) { throw new Error(`默认序列帧路径错误: ${frame.name}`) } await stat(`public/images/frame-sequence/yanhua/${expectedName}`) } const existingFrames = [{ name: 'custom.png', previewUrl: '/custom.png', texture: null }] const customResource = { imageMode: 'sequence', sequenceFrames: existingFrames, textureName: 'custom', textureFolder: 'custom/', previewUrl: '/custom.png', } if (module.ensureDefaultSequenceFrames(customResource)) { throw new Error('已有自定义序列帧时不应被默认资源覆盖') } if (customResource.sequenceFrames !== existingFrames) { throw new Error('已有自定义序列帧引用不应变化') } const fixedResource = { imageMode: 'fixed', sequenceFrames: [], textureName: 'star', textureFolder: '', previewUrl: '/images/star.png', } if (module.ensureDefaultSequenceFrames(fixedResource)) { throw new Error('固定图片模式不应填充序列帧') } const playbackResource = { imageMode: 'sequence', sequencePlayback: 'loop-forward', sequenceStart: 'random', sequenceFps: 10, sequenceFrames: Array.from({ length: 4 }, (_, index) => ({ name: `${index}.png`, previewUrl: '', texture: null })), } const fixedStart = module.imageSequenceStartOffset({ ...playbackResource, sequenceStart: 'first' }, 42, 7) if (fixedStart !== 0) throw new Error('固定首帧不应产生随机偏移') const randomStart = module.imageSequenceStartOffset(playbackResource, 42, 7) if (randomStart !== module.imageSequenceStartOffset(playbackResource, 42, 7) || randomStart < 0 || randomStart >= 4) { throw new Error('随机起始帧必须确定且位于序列范围内') } const variants = new Set(Array.from({ length: 32 }, (_, spawnId) => module.imageSequenceStartOffset(playbackResource, 42, spawnId))) if (variants.size < 2) throw new Error('随机起始帧未在不同粒子间产生变化') if (module.imageFrameState(playbackResource, 0).index !== 0) throw new Error('固定首帧应从第 0 帧开始') if (module.imageFrameState(playbackResource, 0, 3).index !== 3) throw new Error('正向播放应从随机偏移帧开始') const reverseResource = { ...playbackResource, sequencePlayback: 'loop-reverse' } if (module.imageFrameState(reverseResource, 0, 3).index !== 0) throw new Error('反向播放应从随机偏移帧反向开始') const onceResource = { ...playbackResource, sequencePlayback: 'once-hide' } if (module.imageFrameState(onceResource, 0.3, 2).visible) throw new Error('单次隐藏应按偏移后的时长结束') console.log('默认序列帧校验通过:15 FPS、yanhua 顺序、资源路径、固定/随机起始帧与四种播放方式')