mirror of
https://github.com/motajs/template.git
synced 2026-10-03 17:40:19 +08:00
refactor: Delete global loading & hook object
This commit is contained in:
parent
7c155be5ce
commit
d8fa4b8507
@ -1,4 +1,3 @@
|
||||
export * from './load';
|
||||
export * from './material';
|
||||
export * from './save';
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
export const iconNames: string[] = [
|
||||
'floor',
|
||||
'lv',
|
||||
'hpmax',
|
||||
'hp',
|
||||
'atk',
|
||||
'def',
|
||||
'mdef',
|
||||
'money',
|
||||
'exp',
|
||||
'up',
|
||||
'book',
|
||||
'fly',
|
||||
'toolbox',
|
||||
'keyboard',
|
||||
'shop',
|
||||
'save',
|
||||
'load',
|
||||
'settings',
|
||||
'play',
|
||||
'pause',
|
||||
'stop',
|
||||
'speedDown',
|
||||
'speedUp',
|
||||
'rewind',
|
||||
'equipbox',
|
||||
'mana',
|
||||
'skill',
|
||||
'btn1',
|
||||
'btn2',
|
||||
'btn3',
|
||||
'btn4',
|
||||
'btn5',
|
||||
'btn6',
|
||||
'btn7',
|
||||
'btn8'
|
||||
];
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './data';
|
||||
export * from './loader';
|
||||
export * from './types';
|
||||
@ -1,515 +0,0 @@
|
||||
import {
|
||||
ILoadManager,
|
||||
LoadDataType,
|
||||
ILoadTask,
|
||||
LoadTask,
|
||||
ILoadTaskProcessor
|
||||
} from '@motajs/loader';
|
||||
import {
|
||||
CompressedUsage,
|
||||
CustomLoadFunc,
|
||||
ICompressedMotaAssetsData,
|
||||
ICompressedMotaAssetsLoadList,
|
||||
IMotaAssetsLoader
|
||||
} from './types';
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
LoadAudioProcessor,
|
||||
LoadFontProcessor,
|
||||
LoadImageProcessor,
|
||||
LoadTextProcessor,
|
||||
LoadZipProcessor
|
||||
} from '@user/data-base';
|
||||
import { IMotaAudioContext, ISoundPlayer } from '@motajs/audio';
|
||||
import { loading } from '@user/data-base';
|
||||
import { ITextureManager } from '../material';
|
||||
import { ITextureSplitter, Texture, TextureRowSplitter } from '@motajs/render';
|
||||
import { iconNames } from './data';
|
||||
import { IMotaDataLoader } from '@user/data-base';
|
||||
|
||||
export class MotaAssetsLoader implements IMotaAssetsLoader {
|
||||
/** 当前是否正在进行加载 */
|
||||
loading: boolean = false;
|
||||
/** 当前加载工作是否已经完成 */
|
||||
loaded: boolean = false;
|
||||
|
||||
readonly imageProcessor: ILoadTaskProcessor<LoadDataType.Blob, ImageBitmap>;
|
||||
readonly audioProcessor: ILoadTaskProcessor<
|
||||
LoadDataType.Uint8Array,
|
||||
AudioBuffer | null
|
||||
>;
|
||||
readonly fontProcessor: ILoadTaskProcessor<
|
||||
LoadDataType.ArrayBuffer,
|
||||
FontFace
|
||||
>;
|
||||
readonly textProcessor: ILoadTaskProcessor<LoadDataType.Text, string>;
|
||||
readonly zipProcessor: ILoadTaskProcessor<LoadDataType.ArrayBuffer, JSZip>;
|
||||
|
||||
/** 素材索引 */
|
||||
private materialsCounter: number = 0;
|
||||
/** 贴图行分割器,用于处理遗留 `icons.png` */
|
||||
private readonly rowSplitter: ITextureSplitter<number>;
|
||||
|
||||
constructor(
|
||||
readonly progress: ILoadManager,
|
||||
private readonly dataLoader: IMotaDataLoader,
|
||||
private readonly ac: IMotaAudioContext,
|
||||
private readonly sounds: ISoundPlayer<SoundIds>,
|
||||
private readonly materials: ITextureManager
|
||||
) {
|
||||
this.imageProcessor = new LoadImageProcessor();
|
||||
this.audioProcessor = new LoadAudioProcessor(ac);
|
||||
this.fontProcessor = new LoadFontProcessor();
|
||||
this.textProcessor = new LoadTextProcessor();
|
||||
this.zipProcessor = new LoadZipProcessor();
|
||||
this.rowSplitter = new TextureRowSplitter();
|
||||
}
|
||||
|
||||
//#region 其他处理
|
||||
|
||||
private splitMaterialIcons(image: ImageBitmap) {
|
||||
const tex = new Texture(image);
|
||||
const splitted = [...this.rowSplitter.split(tex, 32)];
|
||||
for (let i = 0; i < splitted.length; i++) {
|
||||
const name = iconNames[i] ? `icon-${iconNames[i]}` : `icons-${i}`;
|
||||
// todo: 早晚删除 icons.png
|
||||
const index = this.materialsCounter++;
|
||||
this.materials.imageStore.addTexture(index, splitted[i]);
|
||||
this.materials.imageStore.alias(index, name);
|
||||
}
|
||||
}
|
||||
|
||||
//#region 加载后处理
|
||||
|
||||
/**
|
||||
* 当字体加载完成后的操作
|
||||
* @param font 字体名称
|
||||
* @param fontFace 字体 `FontFace` 对象
|
||||
*/
|
||||
private fontLoaded(font: string, fontFace: FontFace) {
|
||||
const suffix = font.lastIndexOf('.');
|
||||
const family = font.slice(0, suffix);
|
||||
fontFace.family = family;
|
||||
document.fonts.add(fontFace);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片加载完成后的操作
|
||||
* @param name 图片名称
|
||||
* @param image 图片的 `ImageBitmap`
|
||||
*/
|
||||
private customImagesLoaded(name: ImageIds, image: ImageBitmap) {
|
||||
core.material.images.images[name] = image;
|
||||
this.materials.addImage(image, {
|
||||
index: this.materialsCounter++,
|
||||
alias: name
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 音效加载完成后的操作
|
||||
* @param name 音效名称
|
||||
* @param buffer 音效解析完毕的 `AudioBuffer`
|
||||
*/
|
||||
private soundLoaded(name: SoundIds, buffer: AudioBuffer | null) {
|
||||
if (buffer) {
|
||||
this.sounds.add(name, buffer);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 tileset 加载完成后的操作
|
||||
* @param name tileset 名称
|
||||
* @param image 图片 `ImageBitmap`
|
||||
*/
|
||||
private tilesetLoaded(name: string, image: ImageBitmap) {
|
||||
core.material.images.tilesets[name] = image;
|
||||
// this.materials.addTileset(image, {
|
||||
// index: this.materialsCounter++,
|
||||
// alias: name
|
||||
// });
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当自动元件加载完成后的操作
|
||||
* @param autotiles 自动元件存储对象
|
||||
* @param name 自动元件名称
|
||||
* @param image 自动元件的 `ImageBitmap`
|
||||
*/
|
||||
private autotileLoaded(
|
||||
autotiles: Partial<Record<AllIdsOf<'autotile'>, ImageBitmap>>,
|
||||
name: AllIdsOf<'autotile'>,
|
||||
image: ImageBitmap
|
||||
) {
|
||||
autotiles[name] = image;
|
||||
loading.addAutotileLoaded();
|
||||
loading.onAutotileLoaded(autotiles);
|
||||
core.material.images.autotile[name] = image;
|
||||
// const num = icon.autotile[name];
|
||||
// this.materials.addAutotile(image, {
|
||||
// id: name,
|
||||
// num,
|
||||
// cls: 'autotile'
|
||||
// });
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当素材加载完成后的操作
|
||||
* @param name 素材名称
|
||||
* @param image 素材 `ImageBitmap`
|
||||
*/
|
||||
private materialLoaded(name: string, image: ImageBitmap) {
|
||||
core.material.images[
|
||||
name.slice(0, -4) as SelectKey<MaterialImages, ImageBitmap>
|
||||
] = image;
|
||||
if (name === 'icons.png') {
|
||||
this.splitMaterialIcons(image);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当动画加载完成后的操作
|
||||
* @param animation 动画内容
|
||||
*/
|
||||
private animationLoaded(animation: string) {
|
||||
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
const rows = animation.split('@@@~~~###~~~@@@');
|
||||
rows.forEach((value, i) => {
|
||||
const id = data.main.animates[i];
|
||||
if (value.length === 0) {
|
||||
throw new Error(`Cannot find animate: '${id}'`);
|
||||
}
|
||||
core.material.animates[id] = core.loader._loadAnimate(value);
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 加载流程
|
||||
|
||||
/**
|
||||
* 开发时的加载流程
|
||||
*/
|
||||
private developingLoad() {
|
||||
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
const icon = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
// font
|
||||
data.main.fonts.forEach(font => {
|
||||
const url = `project/fonts/${font}`;
|
||||
const task = new LoadTask<LoadDataType.ArrayBuffer, FontFace>({
|
||||
url,
|
||||
identifier: `@system-font/${font}`,
|
||||
dataType: LoadDataType.ArrayBuffer,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.fontProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
task.setProcessor(this.fontProcessor);
|
||||
this.addCustomLoadTask(task, data => this.fontLoaded(font, data));
|
||||
});
|
||||
|
||||
// image
|
||||
data.main.images.forEach(image => {
|
||||
const url = `project/images/${image}`;
|
||||
const task = new LoadTask<LoadDataType.Blob, ImageBitmap>({
|
||||
url,
|
||||
identifier: `@system-image/${image}`,
|
||||
dataType: LoadDataType.Blob,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.imageProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
this.addCustomLoadTask(task, data =>
|
||||
this.customImagesLoaded(image, data)
|
||||
);
|
||||
});
|
||||
|
||||
// sound
|
||||
data.main.sounds.forEach(sound => {
|
||||
const url = `project/sounds/${sound}`;
|
||||
const task = new LoadTask<
|
||||
LoadDataType.Uint8Array,
|
||||
AudioBuffer | null
|
||||
>({
|
||||
url,
|
||||
identifier: `@system-sound/${sound}`,
|
||||
dataType: LoadDataType.Uint8Array,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.audioProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
this.addCustomLoadTask(task, data => this.soundLoaded(sound, data));
|
||||
});
|
||||
|
||||
// tileset
|
||||
data.main.tilesets.forEach(tileset => {
|
||||
const url = `project/tilesets/${tileset}`;
|
||||
const task = new LoadTask<LoadDataType.Blob, ImageBitmap>({
|
||||
url,
|
||||
identifier: `@system-tileset/${tileset}`,
|
||||
dataType: LoadDataType.Blob,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.imageProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
this.addCustomLoadTask(task, data =>
|
||||
this.tilesetLoaded(tileset, data)
|
||||
);
|
||||
});
|
||||
|
||||
// autotile
|
||||
const autotiles: Partial<Record<AllIdsOf<'autotile'>, ImageBitmap>> =
|
||||
{};
|
||||
Object.keys(icon.autotile).forEach(key => {
|
||||
const url = `project/autotiles/${key}.png`;
|
||||
const task = new LoadTask<LoadDataType.Blob, ImageBitmap>({
|
||||
url,
|
||||
identifier: `@system-autotile/${key}`,
|
||||
dataType: LoadDataType.Blob,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.imageProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
this.addCustomLoadTask(task, data =>
|
||||
this.autotileLoaded(
|
||||
autotiles,
|
||||
key as AllIdsOf<'autotile'>,
|
||||
data
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// material
|
||||
const materialImages = core.materials.slice() as SelectKey<
|
||||
MaterialImages,
|
||||
ImageBitmap
|
||||
>[];
|
||||
materialImages.push('keyboard');
|
||||
materialImages
|
||||
.map(v => `${v}.png`)
|
||||
.forEach(materialName => {
|
||||
const url = `project/materials/${materialName}`;
|
||||
const task = new LoadTask<LoadDataType.Blob, ImageBitmap>({
|
||||
url,
|
||||
identifier: `@system-material/${materialName}`,
|
||||
dataType: LoadDataType.Blob,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.imageProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
this.addCustomLoadTask(task, data =>
|
||||
this.materialLoaded(materialName, data)
|
||||
);
|
||||
});
|
||||
|
||||
// animate
|
||||
const animatesUrl = `all/__all_animates__?v=${main.version}&id=${data.main.animates.join(',')}`;
|
||||
const animateTask = new LoadTask<LoadDataType.Text, string>({
|
||||
url: animatesUrl,
|
||||
identifier: '@system-animates',
|
||||
dataType: LoadDataType.Text,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.textProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
this.addCustomLoadTask(animateTask, data => this.animationLoaded(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 `JSZip` 读取方式
|
||||
* @param type 加载类型
|
||||
*/
|
||||
private getZipOutputType(type: LoadDataType): JSZip.OutputType {
|
||||
switch (type) {
|
||||
case LoadDataType.Text:
|
||||
case LoadDataType.JSON:
|
||||
return 'string';
|
||||
case LoadDataType.ArrayBuffer:
|
||||
return 'arraybuffer';
|
||||
case LoadDataType.Blob:
|
||||
return 'blob';
|
||||
case LoadDataType.Uint8Array:
|
||||
return 'uint8array';
|
||||
default:
|
||||
return 'uint8array';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据应用方式获取其所在文件夹
|
||||
* @param usage 压缩内容的应用方式
|
||||
*/
|
||||
private getZipFolderByUsage(usage: CompressedUsage): string {
|
||||
switch (usage) {
|
||||
case CompressedUsage.Image:
|
||||
return 'image';
|
||||
case CompressedUsage.Tileset:
|
||||
return 'tileset';
|
||||
case CompressedUsage.Autotile:
|
||||
return 'autotile';
|
||||
case CompressedUsage.Material:
|
||||
return 'material';
|
||||
case CompressedUsage.Font:
|
||||
return 'font';
|
||||
case CompressedUsage.Sound:
|
||||
return 'sound';
|
||||
case CompressedUsage.Animate:
|
||||
return 'animate';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理压缩文件
|
||||
* @param name 文件名称
|
||||
* @param value 文件内容
|
||||
* @param usage 文件的应用方式
|
||||
*/
|
||||
private async processZipFile(
|
||||
name: string,
|
||||
value: unknown,
|
||||
usage: CompressedUsage
|
||||
) {
|
||||
switch (usage) {
|
||||
case CompressedUsage.Image: {
|
||||
const image = await createImageBitmap(value as Blob);
|
||||
await this.customImagesLoaded(name as ImageIds, image);
|
||||
break;
|
||||
}
|
||||
case CompressedUsage.Tileset: {
|
||||
const image = await createImageBitmap(value as Blob);
|
||||
await this.tilesetLoaded(name, image);
|
||||
break;
|
||||
}
|
||||
case CompressedUsage.Material: {
|
||||
const image = await createImageBitmap(value as Blob);
|
||||
await this.materialLoaded(name, image);
|
||||
break;
|
||||
}
|
||||
case CompressedUsage.Font: {
|
||||
const fontFace = new FontFace(
|
||||
name.slice(0, -4),
|
||||
value as ArrayBuffer
|
||||
);
|
||||
await fontFace.load();
|
||||
await this.fontLoaded(name, fontFace);
|
||||
break;
|
||||
}
|
||||
case CompressedUsage.Sound: {
|
||||
const buffer = await this.ac.decodeToAudioBuffer(
|
||||
value as Uint8Array<ArrayBuffer>
|
||||
);
|
||||
await this.soundLoaded(name as SoundIds, buffer);
|
||||
break;
|
||||
}
|
||||
case CompressedUsage.Animate: {
|
||||
await this.animationLoaded(value as string);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个压缩包
|
||||
* @param list 当前压缩包中包含的内容
|
||||
* @param zip 压缩包
|
||||
*/
|
||||
private async handleZip(list: ICompressedMotaAssetsData[], zip: JSZip) {
|
||||
const autotiles: Partial<Record<AllIdsOf<'autotile'>, ImageBitmap>> =
|
||||
{};
|
||||
const materialImages = core.materials.slice() as SelectKey<
|
||||
MaterialImages,
|
||||
ImageBitmap
|
||||
>[];
|
||||
materialImages.push('keyboard');
|
||||
|
||||
const promises = list.map(async item => {
|
||||
const { readAs, name, usage } = item;
|
||||
const folder = this.getZipFolderByUsage(usage);
|
||||
const file = zip.file(`${folder}/${name}`);
|
||||
if (!file) return;
|
||||
const value = await file.async(this.getZipOutputType(readAs));
|
||||
|
||||
if (usage === CompressedUsage.Autotile) {
|
||||
const image = await createImageBitmap(value as Blob);
|
||||
await this.autotileLoaded(
|
||||
autotiles,
|
||||
name.slice(0, -4) as AllIdsOf<'autotile'>,
|
||||
image
|
||||
);
|
||||
}
|
||||
|
||||
await this.processZipFile(name, value, usage);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 游戏中加载(压缩后)
|
||||
*/
|
||||
private async playingLoad() {
|
||||
const loadListTask = new LoadTask<
|
||||
LoadDataType.JSON,
|
||||
ICompressedMotaAssetsLoadList
|
||||
>({
|
||||
url: `loadList.json`,
|
||||
dataType: LoadDataType.JSON,
|
||||
identifier: '@system-loadList',
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.dataLoader.jsonProcessor,
|
||||
progress: { onProgress() {} }
|
||||
});
|
||||
|
||||
loadListTask.start();
|
||||
const loadList = await loadListTask.loaded();
|
||||
|
||||
const zipTask = new LoadTask<LoadDataType.ArrayBuffer, JSZip>({
|
||||
url: loadList.file,
|
||||
identifier: `@system-zip/${loadList.file}`,
|
||||
dataType: LoadDataType.ArrayBuffer,
|
||||
// @ts-expect-error 需要重构
|
||||
processor: this.zipProcessor,
|
||||
progress: this.progress
|
||||
});
|
||||
|
||||
this.addCustomLoadTask(zipTask, zip => {
|
||||
return this.handleZip(loadList.content, zip);
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 对外接口
|
||||
|
||||
initSystemLoadTask(): void {
|
||||
if (import.meta.env.DEV) {
|
||||
this.developingLoad();
|
||||
} else {
|
||||
this.playingLoad();
|
||||
}
|
||||
}
|
||||
|
||||
addCustomLoadTask<R>(
|
||||
task: ILoadTask<LoadDataType, R>,
|
||||
onLoaded: CustomLoadFunc<R>
|
||||
): Promise<R> {
|
||||
return this.dataLoader.addCustomLoadTask(task, onLoaded);
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.loading = true;
|
||||
this.loaded = false;
|
||||
await this.dataLoader.load();
|
||||
this.loading = false;
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
import {
|
||||
ILoadManager,
|
||||
ILoadTask,
|
||||
ILoadTaskProcessor,
|
||||
LoadDataType
|
||||
} from '@motajs/loader';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
export type CustomLoadFunc<R> = (data: R) => Promise<void>;
|
||||
|
||||
export const enum CompressedUsage {
|
||||
// ---- 系统加载内容,不可更改
|
||||
Font,
|
||||
Image,
|
||||
Sound,
|
||||
Tileset,
|
||||
Autotile,
|
||||
Material,
|
||||
Animate
|
||||
}
|
||||
|
||||
export interface ICompressedMotaAssetsData {
|
||||
/** 此内容的名称 */
|
||||
readonly name: string;
|
||||
/** 此内容应该由什么方式读取 */
|
||||
readonly readAs: LoadDataType;
|
||||
/** 此内容的应用方式 */
|
||||
readonly usage: CompressedUsage;
|
||||
}
|
||||
|
||||
export interface ICompressedMotaAssetsLoadList {
|
||||
/** 压缩文件名称 */
|
||||
readonly file: string;
|
||||
/** 压缩包所包含的内容 */
|
||||
readonly content: ICompressedMotaAssetsData[];
|
||||
}
|
||||
|
||||
export interface IMotaAssetsLoader {
|
||||
/** 加载进度对象 */
|
||||
readonly progress: ILoadManager;
|
||||
/** 当前是否正在加载 */
|
||||
readonly loading: boolean;
|
||||
/** 当前是否已经加载完毕 */
|
||||
readonly loaded: boolean;
|
||||
|
||||
/** 图片处理器 */
|
||||
readonly imageProcessor: ILoadTaskProcessor<LoadDataType.Blob, ImageBitmap>;
|
||||
/** 音频处理器 */
|
||||
readonly audioProcessor: ILoadTaskProcessor<
|
||||
LoadDataType.Uint8Array,
|
||||
AudioBuffer | null
|
||||
>;
|
||||
/** 字体处理器 */
|
||||
readonly fontProcessor: ILoadTaskProcessor<
|
||||
LoadDataType.ArrayBuffer,
|
||||
FontFace
|
||||
>;
|
||||
/** 文字处理器 */
|
||||
readonly textProcessor: ILoadTaskProcessor<LoadDataType.Text, string>;
|
||||
/** `zip` 压缩包处理器 */
|
||||
readonly zipProcessor: ILoadTaskProcessor<LoadDataType.ArrayBuffer, JSZip>;
|
||||
|
||||
/**
|
||||
* 初始化系统加载任务
|
||||
*/
|
||||
initSystemLoadTask(): void;
|
||||
|
||||
/**
|
||||
* 添加自定义加载任务
|
||||
* @param task 自定义加载任务
|
||||
* @param onLoad 当任务加载完成时执行
|
||||
* @returns 一个 `Promise`,当添加的任务加载完毕,且 `onLoad` 返回的 `Promise` 兑现后兑现
|
||||
*/
|
||||
addCustomLoadTask<R>(
|
||||
task: ILoadTask<LoadDataType, R>,
|
||||
onLoad: CustomLoadFunc<R>
|
||||
): Promise<R>;
|
||||
|
||||
/**
|
||||
* 开始所有加载任务的加载工作
|
||||
* @returns 一个 `Promise`,当所有加载任务加载完成后兑现
|
||||
*/
|
||||
load(): Promise<void>;
|
||||
}
|
||||
@ -11,9 +11,7 @@ import {
|
||||
import { IRenderTreeRoot, MotaRenderer } from '@motajs/render';
|
||||
import {
|
||||
ITextureManager,
|
||||
IAutotileProcessor,
|
||||
TextureManager,
|
||||
AutotileProcessor,
|
||||
ISaveSystem,
|
||||
SaveSystem
|
||||
} from '@user/client-base';
|
||||
@ -38,8 +36,6 @@ import {
|
||||
MAIN_WIDTH,
|
||||
VARIATOR_DEBUG_SPEED
|
||||
} from './shared';
|
||||
import { loading } from '@user/data-base';
|
||||
import { fallbackLoad } from './fallback/load';
|
||||
import { WebLoadStarter } from '@motajs/loader';
|
||||
|
||||
export class ClientCore extends CoreState implements IClientCore {
|
||||
@ -90,10 +86,10 @@ export class ClientCore extends CoreState implements IClientCore {
|
||||
//#endregion
|
||||
|
||||
// 兼容层
|
||||
loading.once('loaded', () => {
|
||||
fallbackLoad(this.materials);
|
||||
loading.emit('assetBuilt');
|
||||
});
|
||||
// loading.once('loaded', () => {
|
||||
// fallbackLoad(this.materials);
|
||||
// loading.emit('assetBuilt');
|
||||
// });
|
||||
|
||||
//#region 渲染系统
|
||||
|
||||
@ -126,9 +122,9 @@ export class ClientCore extends CoreState implements IClientCore {
|
||||
this.mainMapExtension = new MapExtensionManager(this.mainMapRenderer);
|
||||
|
||||
// 兼容层
|
||||
loading.once('assetBuilt', () => {
|
||||
this.initMapExtensions();
|
||||
});
|
||||
// loading.once('assetBuilt', () => {
|
||||
// this.initMapExtensions();
|
||||
// });
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import { loading } from '@user/data-base';
|
||||
import { patchAll } from './fallback';
|
||||
import { createGameRenderer, createRender } from './render';
|
||||
import { createRender } from './render';
|
||||
|
||||
export function create() {
|
||||
patchAll();
|
||||
createRender();
|
||||
loading.once('coreInit', () => {
|
||||
createGameRenderer();
|
||||
});
|
||||
}
|
||||
|
||||
export * from './action';
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import { Font } from '@motajs/render';
|
||||
import { defineComponent } from 'vue';
|
||||
import { DEFAULT_FONT, MAIN_HEIGHT, MAIN_WIDTH } from '../shared';
|
||||
import { hook } from '@user/data-base';
|
||||
import { createElements } from '@user/client-base';
|
||||
import { mainRenderer } from './renderer';
|
||||
import { createUI } from './ui';
|
||||
import { createAction } from './action';
|
||||
import { sceneController } from './scene';
|
||||
import { GameTitleUI } from './ui/title';
|
||||
import { createWeather } from './weather';
|
||||
import { createApp } from './renderer';
|
||||
import { LoadSceneUI } from './ui/load';
|
||||
@ -34,10 +32,10 @@ export function createRender() {
|
||||
createAction();
|
||||
createWeather();
|
||||
|
||||
hook.on('restart', () => {
|
||||
sceneController.closeAll();
|
||||
sceneController.open(GameTitleUI, {});
|
||||
});
|
||||
// hook.on('restart', () => {
|
||||
// sceneController.closeAll();
|
||||
// sceneController.open(GameTitleUI, {});
|
||||
// });
|
||||
|
||||
Font.setDefaults(DEFAULT_FONT);
|
||||
}
|
||||
|
||||
@ -23,7 +23,6 @@ import { transitioned } from '../use';
|
||||
import { cosh, CurveMode, linear } from '@motajs/animate';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { sleep } from '@motajs/common';
|
||||
import { loading } from '@user/data-base';
|
||||
import { GameTitleUI } from './title';
|
||||
import { client } from '../../core';
|
||||
|
||||
@ -62,7 +61,6 @@ export const LoadScene = defineComponent<ILoadProps>(props => {
|
||||
];
|
||||
|
||||
const loadEnd = async () => {
|
||||
loading.emit('loaded');
|
||||
alpha.set(0);
|
||||
await sleep(400);
|
||||
props.controller.closeAll();
|
||||
|
||||
@ -26,7 +26,6 @@ import {
|
||||
} from './statusBar';
|
||||
import { ReplayingStatus } from './toolbar';
|
||||
import { state } from '@user/data-state';
|
||||
import { hook } from '@user/data-base';
|
||||
import { mainUIController } from './controller';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { using } from '../renderer';
|
||||
@ -138,14 +137,19 @@ const MainScene = defineComponent(() => {
|
||||
locked.value = core.status.lockControl;
|
||||
};
|
||||
|
||||
// 监听状态栏更新事件
|
||||
hook.on('statusBarUpdate', updateStatus);
|
||||
hook.on('statusBarUpdate', updateDataFallback);
|
||||
// 保留函数调用,避免被识别为未使用函数而报错
|
||||
// 后续修改后需要删除
|
||||
updateDataFallback();
|
||||
updateStatus();
|
||||
|
||||
onUnmounted(() => {
|
||||
hook.off('statusBarUpdate', updateStatus);
|
||||
hook.off('statusBarUpdate', updateDataFallback);
|
||||
});
|
||||
// 监听状态栏更新事件
|
||||
// hook.on('statusBarUpdate', updateStatus);
|
||||
// hook.on('statusBarUpdate', updateDataFallback);
|
||||
|
||||
// onUnmounted(() => {
|
||||
// hook.off('statusBarUpdate', updateStatus);
|
||||
// hook.off('statusBarUpdate', updateDataFallback);
|
||||
// });
|
||||
|
||||
//#region sprite 渲染
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
import { logger } from '@motajs/common';
|
||||
import { IRenderItem, IRenderTreeRoot } from '@motajs/render';
|
||||
import { Hotkey, gameKey } from '@motajs/system';
|
||||
import { loading } from '@user/data-base';
|
||||
import {
|
||||
ComponentInternalInstance,
|
||||
getCurrentInstance,
|
||||
@ -65,18 +64,6 @@ export function onOrientationChange(hook: OrientationHook) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 当游戏加载完成时执行函数,如果调用此函数时游戏已经加载,那么会立刻调用传入的钩子函数
|
||||
* @param hook 当游戏加载完成时执行的函数
|
||||
*/
|
||||
export function onLoaded(hook: () => void) {
|
||||
if (!loading.loaded) {
|
||||
loading.once('loaded', hook);
|
||||
} else {
|
||||
hook();
|
||||
}
|
||||
}
|
||||
|
||||
type KeyUsing = [Hotkey, symbol];
|
||||
|
||||
/**
|
||||
|
||||
@ -1,273 +0,0 @@
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { IDamageEnemy, IEnemyCollection } from '@motajs/types';
|
||||
|
||||
// ----- 加载事件
|
||||
interface GameLoadEvent {
|
||||
/** 当核心脚本加载完毕时触发 */
|
||||
coreLoaded: [];
|
||||
/** 当自动元件加载完毕后触发 */
|
||||
autotileLoaded: [];
|
||||
/** 当核心类初始化完毕后触发 */
|
||||
coreInit: [];
|
||||
/** 当所有启动必要资源加载完毕后触发 */
|
||||
loaded: [];
|
||||
/** 当资源构建完毕后触发,后续需要用新的加载系统替代 */
|
||||
assetBuilt: [];
|
||||
/** 当客户端(渲染端)和数据端都挂载完毕后触发 */
|
||||
registered: [];
|
||||
/** 当数据端挂载完毕后触发 */
|
||||
dataRegistered: [];
|
||||
/** 当客户端(渲染端)挂载完毕后触发 */
|
||||
clientRegistered: [];
|
||||
}
|
||||
|
||||
class GameLoading extends EventEmitter<GameLoadEvent> {
|
||||
private autotileLoaded: number = 0;
|
||||
private autotileNum?: number;
|
||||
private autotileListened: boolean = false;
|
||||
loaded: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.once('coreInit', () => {
|
||||
this.autotileNum = Object.keys(core.material.icons.autotile).length;
|
||||
});
|
||||
this.once('loaded', () => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
addAutotileLoaded() {
|
||||
this.autotileLoaded++;
|
||||
if (this.autotileLoaded === this.autotileNum) {
|
||||
this.emit('autotileLoaded');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当自动元件加载完毕时
|
||||
* @param autotiles 自动元件数组
|
||||
*/
|
||||
onAutotileLoaded(
|
||||
autotiles: Partial<Record<AllIdsOf<'autotile'>, ImageBitmap>>
|
||||
) {
|
||||
if (this.autotileListened) return;
|
||||
this.autotileListened = true;
|
||||
this.on('autotileLoaded', () => {
|
||||
const keys = Object.keys(
|
||||
core.material.icons.autotile
|
||||
) as AllIdsOf<'autotile'>[];
|
||||
|
||||
keys.forEach(v => {
|
||||
core.material.images.autotile[v] = autotiles[v]!;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
core.maps._makeAutotileEdges();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const loading = new GameLoading();
|
||||
if (typeof main !== 'undefined') {
|
||||
main.loading = loading;
|
||||
}
|
||||
|
||||
let clientRegistered = false;
|
||||
let dataRegistered = false;
|
||||
|
||||
function checkRegistered() {
|
||||
if (
|
||||
typeof main !== 'undefined' &&
|
||||
(main.replayChecking || main.mode === 'editor')
|
||||
) {
|
||||
clientRegistered = true;
|
||||
}
|
||||
if (clientRegistered && dataRegistered) {
|
||||
loading.emit('registered');
|
||||
}
|
||||
}
|
||||
|
||||
loading.once('clientRegistered', () => {
|
||||
clientRegistered = true;
|
||||
checkRegistered();
|
||||
});
|
||||
loading.once('dataRegistered', () => {
|
||||
dataRegistered = true;
|
||||
checkRegistered();
|
||||
});
|
||||
|
||||
export interface GameEvent {
|
||||
/** 当游戏初始化时触发,Emitted in libs/events.js resetGame. */
|
||||
reset: [];
|
||||
/** 当游戏挂载完毕后触发,Emitted in src/App.vue setup. */
|
||||
mounted: [];
|
||||
/** 当状态栏更新时触发,Emitted in plugin/game/ui.ts updateStatusBar_update */
|
||||
statusBarUpdate: [];
|
||||
/** 当客户端(渲染端)加载完毕后触发,Emitted in core/index.ts */
|
||||
renderLoaded: [];
|
||||
/** 当捡拾道具后触发,Emitted in libs/events.js getItem */
|
||||
afterGetItem: [
|
||||
itemId: AllIdsOf<'items'>,
|
||||
x: number,
|
||||
y: number,
|
||||
isGentleClick: boolean
|
||||
];
|
||||
/** 当开门后触发,Emitted in libs/events.js _openDoor_animate */
|
||||
afterOpenDoor: [doorId: AllIdsOf<'animates'>, x: number, y: number];
|
||||
/** 当楼层切换后触发,Emitted in project/functions.js afterChangeFloor */
|
||||
afterChangeFloor: [floorId: FloorIds];
|
||||
/** 勇士每移动一步时触发,Emitted in project/functions.js moveOneStep */
|
||||
moveOneStep: [x: number, y: number, floorId: FloorIds];
|
||||
/** 战斗后触发,Emitted in src/game/enemy/battle.ts afterBattle */
|
||||
afterBattle: [enemy: IDamageEnemy, x?: number, y?: number];
|
||||
/** 楼层切换中触发,具体时刻是楼层切换的正中间,刚刚执行完切换,Emitted in libs/events.js changingFloor */
|
||||
changingFloor: [floorId: FloorIds, heroLoc: Loc];
|
||||
/** 当某一个图块被设置时触发,Emitted in libs/maps.js setBlock */
|
||||
setBlock: [
|
||||
x: number,
|
||||
y: number,
|
||||
floorId: FloorIds,
|
||||
newBlock: AllNumbers,
|
||||
oldBlock: AllNumbers
|
||||
];
|
||||
/** 当怪物信息被解析时触发,Emitted in game/enemy/damage.ts */
|
||||
enemyExtract: [col: IEnemyCollection];
|
||||
/** 当从游戏中回到游戏标题界面时触发,Emitted in lib/events.js restart */
|
||||
restart: [];
|
||||
/** 当设置背景或前景图块时触发,Emitted in lib/maps.js setBgFgBlock */
|
||||
setBgFgBlock: [
|
||||
name: 'bg' | 'fg' | 'bg2' | 'fg2',
|
||||
number: AllNumbers,
|
||||
x: number,
|
||||
y: number,
|
||||
floorId: FloorIds
|
||||
];
|
||||
/** 当录像播放在暂停和播放状态间切换时触发,Emitted in lib/control.js */
|
||||
replayStatus: [replaying: boolean];
|
||||
/** 当加载存档时触发,Emitted in project/functions.js */
|
||||
loadData: [];
|
||||
/** 当重设 `core.status.hero` 时触发 */
|
||||
resetHero: [hero: any];
|
||||
}
|
||||
|
||||
export const hook = new EventEmitter<GameEvent>();
|
||||
|
||||
interface ListenerEvent {
|
||||
// block
|
||||
hoverBlock: [block: Block, ev: MouseEvent];
|
||||
leaveBlock: [block: Block, ev: MouseEvent, leaveGame: boolean];
|
||||
clickBlock: [block: Block, ev: MouseEvent];
|
||||
// mouse
|
||||
mouseMove: [ev: MouseEvent];
|
||||
}
|
||||
|
||||
class GameListener extends EventEmitter<ListenerEvent> {
|
||||
static num: number = 0;
|
||||
|
||||
num: number = GameListener.num++;
|
||||
|
||||
mouseX: number = -1;
|
||||
mouseY: number = -1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (typeof main === 'undefined' || main.replayChecking) return;
|
||||
if (typeof window !== 'undefined' && window.core) {
|
||||
this.init();
|
||||
} else {
|
||||
loading.once('coreInit', () => {
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
// ----- block
|
||||
// const data = core.canvas.data.canvas;
|
||||
// const getBlockLoc = (px: number, py: number, size: number) => {
|
||||
// return [
|
||||
// Math.floor(((px * 32) / size + core.bigmap.offsetX) / 32),
|
||||
// Math.floor(((py * 32) / size + core.bigmap.offsetY) / 32)
|
||||
// ];
|
||||
// };
|
||||
// // hover & leave & mouseMove
|
||||
// data.addEventListener('mousemove', e => {
|
||||
// if (
|
||||
// core.status.lockControl ||
|
||||
// !core.isPlaying() ||
|
||||
// !core.status.floorId
|
||||
// )
|
||||
// return;
|
||||
// this.emit('mouseMove', e);
|
||||
// const {
|
||||
// x: px,
|
||||
// y: py,
|
||||
// size
|
||||
// } = core.actions._getClickLoc(e.offsetX, e.offsetY);
|
||||
// const [bx, by] = getBlockLoc(px, py, size);
|
||||
// const blocks = core.getMapBlocksObj();
|
||||
// if (this.mouseX !== bx || this.mouseY !== by) {
|
||||
// const lastBlock = blocks[`${this.mouseX},${this.mouseY}`];
|
||||
// const block = blocks[`${bx},${by}`];
|
||||
// if (lastBlock) {
|
||||
// this.emit('leaveBlock', lastBlock, e, false);
|
||||
// }
|
||||
// if (block) {
|
||||
// this.emit('hoverBlock', block, e);
|
||||
// this.mouseX = bx;
|
||||
// this.mouseY = by;
|
||||
// } else {
|
||||
// this.mouseX = -1;
|
||||
// this.mouseY = -1;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// data.addEventListener('mouseleave', e => {
|
||||
// if (
|
||||
// core.status.lockControl ||
|
||||
// !core.isPlaying() ||
|
||||
// !core.status.floorId
|
||||
// )
|
||||
// return;
|
||||
// const blocks = core.getMapBlocksObj();
|
||||
// const lastBlock = blocks[`${this.mouseX},${this.mouseY}`];
|
||||
// if (lastBlock) {
|
||||
// this.emit('leaveBlock', lastBlock, e, true);
|
||||
// }
|
||||
// this.mouseX = -1;
|
||||
// this.mouseY = -1;
|
||||
// });
|
||||
// // click
|
||||
// data.addEventListener('click', e => {
|
||||
// if (
|
||||
// core.status.lockControl ||
|
||||
// !core.isPlaying() ||
|
||||
// !core.status.floorId
|
||||
// )
|
||||
// return;
|
||||
// const {
|
||||
// x: px,
|
||||
// y: py,
|
||||
// size
|
||||
// } = core.actions._getClickLoc(e.offsetX, e.offsetY);
|
||||
// const [bx, by] = getBlockLoc(px, py, size);
|
||||
// const blocks = core.getMapBlocksObj();
|
||||
// const block = blocks[`${bx},${by}`];
|
||||
// if (block) {
|
||||
// this.emit('clickBlock', block, e);
|
||||
// }
|
||||
// });
|
||||
// ----- mouse
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export const gameListener = new GameListener();
|
||||
|
||||
declare global {
|
||||
interface Main {
|
||||
loading: GameLoading;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
export * from './enemy';
|
||||
export * from './flag';
|
||||
export * from './hero';
|
||||
export * from './load';
|
||||
export * from './map';
|
||||
|
||||
export * from './game';
|
||||
export * from './types';
|
||||
|
||||
@ -11,10 +11,9 @@ import * as ClientModules from '@user/client-modules';
|
||||
import * as MutateAnimate from 'mutate-animate';
|
||||
import * as Vue from 'vue';
|
||||
import * as Lodash from 'lodash-es';
|
||||
import { hook, loading } from '@user/data-base';
|
||||
|
||||
export function create() {
|
||||
loading.once('registered', createModule);
|
||||
// loading.once('registered', createModule);
|
||||
|
||||
Mota.register('@motajs/client', Client);
|
||||
Mota.register('@motajs/client-base', ClientBase);
|
||||
@ -29,14 +28,14 @@ export function create() {
|
||||
Mota.register('Vue', Vue);
|
||||
Mota.register('Lodash', Lodash);
|
||||
|
||||
loading.emit('clientRegistered');
|
||||
// loading.emit('clientRegistered');
|
||||
}
|
||||
|
||||
async function createModule() {
|
||||
ClientModules.create();
|
||||
LegacyUI.create();
|
||||
// async function createModule() {
|
||||
// ClientModules.create();
|
||||
// LegacyUI.create();
|
||||
|
||||
await import('ant-design-vue/dist/antd.dark.css');
|
||||
main.renderLoaded = true;
|
||||
hook.emit('renderLoaded');
|
||||
}
|
||||
// await import('ant-design-vue/dist/antd.dark.css');
|
||||
// main.renderLoaded = true;
|
||||
// hook.emit('renderLoaded');
|
||||
// }
|
||||
|
||||
@ -13,6 +13,4 @@ export function create() {
|
||||
Mota.register('@user/data-base', DataBase);
|
||||
Mota.register('@user/data-system', DataSystem);
|
||||
Mota.register('@user/data-state', DataState);
|
||||
|
||||
DataBase.loading.emit('dataRegistered');
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { createMota } from './mota';
|
||||
import { create } from './create';
|
||||
import { loading } from '@user/data-base';
|
||||
import { Patch } from '@motajs/legacy-common';
|
||||
import { logger } from '@motajs/common';
|
||||
|
||||
export function createData() {
|
||||
@ -14,10 +12,6 @@ export function createData() {
|
||||
`此时录像验证中可以看到完整正确的报错栈。调试完毕后,记得将它重新设为 false`
|
||||
);
|
||||
}
|
||||
|
||||
loading.once('coreInit', () => {
|
||||
Patch.patchAll();
|
||||
});
|
||||
}
|
||||
|
||||
export * from './mota';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user