refactor: 接口与进程通讯优化

This commit is contained in:
unanmed 2024-01-24 21:32:49 +08:00
parent b407e7b80a
commit 5119227a41
41 changed files with 506 additions and 181 deletions

View File

@ -7,4 +7,5 @@ public/project/floors/*.js
public/project/maps.js
public/_server/**/*.js
script/**/*.js
public/editor.html
public/editor.html
keyCodes.ts

View File

@ -2317,7 +2317,7 @@ control.prototype._doSL_load = function (id, callback) {
1
)[0];
if (!main.replayChecking) {
mota.ui.fixed.closeByName('start');
Mota.require('var', 'fixedUi').closeByName('start');
}
if (core.isPlaying() && !core.status.gameOver) {
core.control.autosave(0);
@ -2334,7 +2334,7 @@ control.prototype._doSL_load = function (id, callback) {
null,
function (data) {
if (!main.replayChecking && data) {
mota.ui.fixed.closeByName('start');
Mota.require('var', 'fixedUi').closeByName('start');
}
if (id == 'autoSave' && data != null) {
core.saves.autosave.data = data;
@ -3012,9 +3012,6 @@ control.prototype.removeSwitch = function (x, y, floorId, name) {
////// 锁定状态栏,常常用于事件处理 //////
control.prototype.lockControl = function () {
core.status.lockControl = true;
if (!main.replayChecking) {
// mota.plugin.fixed.showFixed.value = false;
}
};
////// 解锁状态栏 //////
@ -3342,23 +3339,24 @@ control.prototype.screenFlash = function (
});
};
// todo: deprecate playBgm, pauseBgm, resumeBgm, triggerBgm
////// 播放背景音乐 //////
control.prototype.playBgm = function (bgm, startTime) {
bgm = core.getMappedName(bgm);
if (main.mode !== 'play') return;
mota.bgm.play(bgm, startTime);
Mota.require('var', 'bgm').play(bgm, startTime);
};
////// 暂停背景音乐的播放 //////
control.prototype.pauseBgm = function () {
if (main.mode !== 'play') return;
mota.bgm.pause();
Mota.require('var', 'bgm').pause();
};
////// 恢复背景音乐的播放 //////
control.prototype.resumeBgm = function (resumeTime) {
if (main.mode !== 'play') return;
mota.bgm.resume();
Mota.require('var', 'bgm').resume();
};
////// 更改背景音乐的播放 //////
@ -3371,6 +3369,7 @@ control.prototype.triggerBgm = function () {
core.setLocalStorage('bgmStatus', core.musicStatus.bgmStatus);
};
// todo: deprecate playSound, stopSound, getPlayingSounds
////// 播放音频 //////
control.prototype.playSound = function (sound, pitch, callback) {
sound = core.getMappedName(sound);
@ -3380,19 +3379,19 @@ control.prototype.playSound = function (sound, pitch, callback) {
!core.material.sounds[sound]
)
return;
mota.sound.play(sound, callback);
Mota.require('var', 'sound').play(sound, callback);
};
////// 停止所有音频 //////
control.prototype.stopSound = function (id) {
if (typeof id === 'number') mota.sound.stop(id);
else mota.sound.stopAll();
if (typeof id === 'number') Mota.require('var', 'sound').stop(id);
else Mota.require('var', 'sound').stopAll();
};
////// 获得当前正在播放的所有指定音效的id列表 //////
control.prototype.getPlayingSounds = function (name) {
name = core.getMappedName(name);
return mota.sound.getPlaying(name);
return Mota.require('var', 'sound').getPlaying(name);
};
////// 检查bgm状态 //////

View File

@ -26,7 +26,7 @@ events.prototype.startGame = function (hard, seed, route, callback) {
hard = hard || '';
core.dom.gameGroup.style.display = 'block';
if (!main.replayChecking) {
mota.ui.fixed.closeByName('start');
Mota.require('var', 'fixedUi').closeByName('start');
}
if (main.mode != 'play') return;
@ -471,7 +471,12 @@ events.prototype.openDoor = function (x, y, needKey, callback) {
core.removeBlock(x, y);
setTimeout(function () {
core.status.replay.animate = false;
mota.game.hook.emit('afterOpenDoor', block.event.id, x, y);
Mota.require('var', 'hook').emit(
'afterOpenDoor',
block.event.id,
x,
y
);
if (callback) callback();
}, 1); // +1是为了录像检测系统
} else {
@ -556,7 +561,7 @@ events.prototype._openDoor_animate = function (block, x, y, callback) {
core.maps._removeBlockFromMap(core.status.floorId, block);
if (!locked) core.unlockControl();
core.status.replay.animate = false;
mota.game.hook.emit('afterOpenDoor', block.event.id, x, y);
Mota.require('var', 'hook').emit('afterOpenDoor', block.event.id, x, y);
if (callback) callback();
};
@ -636,7 +641,7 @@ events.prototype.getItem = function (id, num, x, y, isGentleClick, callback) {
itemHint.push(id);
}
mota.game.hook.emit('afterGetItem', id, x, y, isGentleClick);
Mota.require('var', 'hook').emit('afterGetItem', id, x, y, isGentleClick);
if (callback) callback();
};

View File

@ -2551,8 +2551,11 @@ maps.prototype._drawThumbnail_realDrawTempCanvas = function (
blocks,
options
) {
options.ctx.imageSmoothingEnabled = core.getLocalStorage(
'antiAliasing',
// todo: storage获取方式优化
const storage = mota.storage;
const s = storage.get(storage.fromAuthor('AncTe', 'setting'));
options.ctx.imageSmoothingEnabled = !s.getValue(
'screen.antiAliasing',
true
);
// 缩略图:背景

View File

@ -51,7 +51,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
else core.showStatusBar();
if (main.mode === 'play' && !main.replayChecking) {
mota.plugin.fly.splitArea();
mota.game.hook.emit('reset');
Mota.require('var', 'hook').emit('reset');
} else {
flags.autoSkill ??= true;
}

View File

@ -1168,14 +1168,14 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
"name": "系统设置",
"canUseItemEffect": "true",
"text": "内含所有系统设置项",
"useItemEffect": "if (!core.isReplaying()) mota.ui.main.open('settings');"
"useItemEffect": "if (!core.isReplaying()) Mota.require('var', 'mainUi').open('settings');"
},
"I560": {
"cls": "constants",
"name": "百科全书",
"canUseItemEffect": "true",
"text": "一个包含游戏中所有功能详细说明的百科全书,可以查看游戏中所有的功能",
"useItemEffect": "if (!core.isReplaying()) mota.ui.main.open('desc');"
"useItemEffect": "if (!core.isReplaying()) Mota.require('var', 'mainUi').open('desc');"
},
"I565": {
"cls": "constants",
@ -1233,7 +1233,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
"cls": "constants",
"name": "成就",
"canUseItemEffect": "true",
"useItemEffect": "mota.ui.main.open('achievement');",
"useItemEffect": "Mota.require('var', 'mainUi')open('achievement');",
"text": "可以查看成就"
}
}

View File

@ -70,3 +70,5 @@ export class BgmController extends ResourceController<HTMLAudioElement> {
return this.list[`bgms.${id}`];
}
}
export const bgm = new BgmController();

View File

@ -215,3 +215,5 @@ export class SoundController extends ResourceController<
}
}
}
export const sound = new SoundController();

View File

@ -1,15 +1,24 @@
import { BgmController } from './audio/bgm';
import { SoundController } from './audio/sound';
import { BgmController, bgm } from './audio/bgm';
import { SoundController, sound } from './audio/sound';
import { EventEmitter } from './common/eventEmitter';
import { loading, readyAllResource } from './loader/load';
import { ResourceStore, ResourceType } from './loader/resource';
import {
ResourceStore,
ResourceType,
resource,
zipResource
} from './loader/resource';
import { UiController } from './main/custom/ui';
import { GameEvent, hook } from './main/game';
import { fixedUi, mainUi } from './main/init/ui';
import { GameEvent, gameListener, hook } from './main/game';
import { GameStorage } from './main/storage';
import { resolvePlugin } from './plugin';
// import { resolvePlugin } from './plugin';
import './main/init/';
import './main/custom/toolbar';
import { fixedUi, mainUi } from './main/init/ui';
import { gameKey } from './main/init/hotkey';
import { mainSetting, settingStorage } from './main/setting';
import { isMobile } from '../plugin/use';
import { KeyCode } from '@/plugin/keyCodes';
interface AncTePlugin {
pop: ReturnType<typeof import('../plugin/pop').default>;
@ -33,42 +42,54 @@ interface AncTePlugin {
frag: ReturnType<typeof import('../plugin/fx/frag').default>;
}
export interface Mota {
sound: SoundController;
/** 游戏资源 */
resource: ResourceStore<Exclude<ResourceType, 'zip'>>;
zipResource: ResourceStore<'zip'>;
bgm: BgmController;
plugin: AncTePlugin;
game: {
hook: EventEmitter<GameEvent>;
storage: typeof GameStorage;
};
ui: {
main: UiController;
fixed: UiController;
};
}
// export interface Mota {
// sound: SoundController;
// /** 游戏资源 */
// resource: ResourceStore<Exclude<ResourceType, 'zip'>>;
// zipResource: ResourceStore<'zip'>;
// bgm: BgmController;
// plugin: AncTePlugin;
// hook: EventEmitter<GameEvent>;
// classes: {
// storage: typeof GameStorage;
// };
// ui: {
// main: UiController;
// fixed: UiController;
// };
// }
function ready() {
window.mota = {
bgm: new BgmController(),
resource: new ResourceStore(),
zipResource: new ResourceStore(),
sound: new SoundController(),
// @ts-ignore
plugin: {},
game: {
hook,
storage: GameStorage
},
ui: {
main: mainUi,
fixed: fixedUi
}
};
// window.mota = {
// bgm: new BgmController(),
// resource: new ResourceStore(),
// zipResource: new ResourceStore(),
// sound: new SoundController(),
// // @ts-ignore
// plugin: {},
// hook,
// storage: GameStorage,
// ui: {
// main: mainUi,
// fixed: fixedUi
// }
// };
readyAllResource();
loading.once('coreInit', resolvePlugin);
// loading.once('coreInit', resolvePlugin);
}
ready();
Mota.register('var', 'mainUi', mainUi);
Mota.register('var', 'fixedUi', fixedUi);
Mota.register('var', 'hook', hook);
Mota.register('var', 'gameListener', gameListener);
Mota.register('var', 'bgm', bgm);
Mota.register('var', 'sound', sound);
Mota.register('var', 'gameKey', gameKey);
Mota.register('var', 'loading', loading);
Mota.register('var', 'mainSetting', mainSetting);
Mota.register('var', 'KeyCode', KeyCode);
Mota.register('var', 'resource', resource);
Mota.register('var', 'zipResource', zipResource);
Mota.register('var', 'settingStorage', settingStorage);

View File

@ -1,6 +1,11 @@
import resource from '@/data/resource.json';
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
import { Resource, getTypeByResource } from './resource';
import {
Resource,
getTypeByResource,
zipResource,
resource as res
} from './resource';
interface GameLoadEvent extends EmitableEvent {
coreLoaded: () => void;
@ -19,9 +24,9 @@ export function readyAllResource() {
info.resource.forEach(v => {
const type = getTypeByResource(v);
if (type === 'zip') {
mota.zipResource.set(v, new Resource(v, 'zip'));
zipResource.set(v, new Resource(v, 'zip'));
} else {
mota.resource.set(v, new Resource(v, type));
res.set(v, new Resource(v, type));
}
});
}
@ -35,13 +40,13 @@ export function readyAllResource() {
loadData.forEach(v => {
const type = getTypeByResource(v);
if (type !== 'zip') {
mota.resource.set(v, new Resource(v, type));
res.set(v, new Resource(v, type));
}
});
mota.resource.forEach(v => v.active());
res.forEach(v => v.active());
loading.once('coreInit', () => {
const animates = new Resource('__all_animates__', 'text');
mota.resource.set('__all_animates__', animates);
res.set('__all_animates__', animates);
animates.active();
});
}

View File

@ -54,7 +54,7 @@ export class Resource<
protected onLoadStart(v?: ResourceData[T]) {
if (this.format === 'bgm') {
// bgm 单独处理,因为它可以边播放边加载
mota.bgm.add(this.uri, v!);
Mota.require('var', 'bgm').add(this.uri, v!);
}
}
@ -63,7 +63,7 @@ export class Resource<
if (this.type === 'fonts') {
document.fonts.add(new FontFace(this.name, v as ArrayBuffer));
} else if (this.type === 'sounds') {
mota.sound.add(this.uri, v as ArrayBuffer);
Mota.require('var', 'sound').add(this.uri, v as ArrayBuffer);
} else if (this.type === 'images') {
const name = `${this.name}${this.ext}` as ImageIds;
loading.on(
@ -111,7 +111,7 @@ export class Resource<
const id = `${base}.${name}`;
const type = getTypeByResource(id) as NonZipResource;
const format = getZipFormatByType(type);
mota.resource.set(
resource.set(
id,
new Resource(id, type).setData(file.async(format))
);
@ -377,3 +377,6 @@ export function getZipFormatByType(type: ResourceType): 'arraybuffer' | 'text' {
if (type === 'text' || type === 'json') return 'text';
else return 'arraybuffer';
}
export const resource = new ResourceStore();
export const zipResource = new ResourceStore();

View File

@ -34,6 +34,7 @@ const closeFixed = () => {
});
};
// todo: 应当在这里实现查看临界与特殊属性的功能
export let hovered: Block | null;
gameListener.on('hoverBlock', block => {

View File

@ -1,3 +1,2 @@
import './fixed';
import './hotkey';
import './keyboard';

View File

@ -3,7 +3,7 @@ import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
import { loading } from '../loader/load';
import { hook } from './game';
import { GameStorage } from './storage';
import { triggerFullscreen } from '@/plugin/utils';
import { has, triggerFullscreen } from '@/plugin/utils';
import { createSettingComponents } from './init/settings';
export interface SettingComponentProps {
@ -46,6 +46,8 @@ export interface SettingDisplayInfo {
const COM = createSettingComponents();
export class MotaSetting extends EventEmitter<SettingEvent> {
static noStorage: string[] = [];
readonly list: Record<string, MotaSettingItem> = {};
/**
@ -145,6 +147,31 @@ export class MotaSetting extends EventEmitter<SettingEvent> {
this.emit('valueChange', key, old, value);
}
/**
* MotaSetting实例undefined
* @param key
*/
getValue(key: string): boolean | number | undefined;
/**
* MotaSetting实例defaultValue
* @param key
* @param defaultValue
*/
getValue<T extends boolean | number>(key: string, defaultValue: T): T;
getValue<T extends boolean | number>(
key: string,
defaultValue?: T
): T | undefined {
const setting = this.getSetting(key);
if (!has(setting) && !has(defaultValue)) return void 0;
if (setting instanceof MotaSetting) {
if (has(setting)) return defaultValue;
return void 0;
} else {
return has(setting) ? (setting.value as T) : (defaultValue as T);
}
}
/**
*
* @param key
@ -277,8 +304,10 @@ export class SettingDisplayer extends EventEmitter<SettingDisplayerEvent> {
// todo: 优化存储方式
export const mainSetting = new MotaSetting();
// 添加不参与全局存储的设置
MotaSetting.noStorage.push('action.autoSkill', 'screen.fullscreen');
interface SettingStorage {
export interface SettingStorage {
showHalo: boolean;
frag: boolean;
itemDetail: boolean;
@ -298,8 +327,14 @@ const storage = new GameStorage<SettingStorage>(
GameStorage.fromAuthor('AncTe', 'setting')
);
export { storage as settingStorage };
// ----- 监听设置修改
mainSetting.on('valueChange', (key, n, o) => {
if (!MotaSetting.noStorage.includes(key)) {
storage.setValue(key, n);
}
const [root, setting] = key.split('.');
if (root === 'screen') {
@ -323,25 +358,23 @@ function handleScreenSetting<T extends number | boolean>(
triggerFullscreen(n as boolean);
} else if (key === 'halo') {
// 光环
core.setLocalStorage('showHalo', n);
// core.setLocalStorage('showHalo', n);
} else if (key === 'frag') {
// 打怪特效
core.setLocalStorage('frag', n);
// core.setLocalStorage('frag', n);
} else if (key === 'itemDetail') {
// 宝石血瓶显伤
core.setLocalStorage('itemDetail', n);
// core.setLocalStorage('itemDetail', n);
} else if (key === 'heroDetail') {
// 勇士显伤
core.setLocalStorage('heroDetail', n);
// core.setLocalStorage('heroDetail', n);
core.drawHero();
// storage.setValue('heroDetail', n as boolean);
} else if (key === 'transition') {
// 界面动画
core.setLocalStorage('transition', n);
// transition.value = n as boolean;
// core.setLocalStorage('transition', n);
} else if (key === 'antiAlias') {
// 抗锯齿
core.setLocalStorage('antiAlias', n);
// core.setLocalStorage('antiAlias', n);
for (const canvas of core.dom.gameCanvas) {
if (core.domStyle.hdCanvas.includes(canvas.id)) continue;
if (n) {
@ -352,12 +385,12 @@ function handleScreenSetting<T extends number | boolean>(
}
} else if (key === 'fontSize') {
// 字体大小
core.setLocalStorage('fontSize', n);
// core.setLocalStorage('fontSize', n);
root.style.fontSize = `${n}px`;
} else if (key === 'smoothView') {
core.setLocalStorage('smoothView', n);
// core.setLocalStorage('smoothView', n);
} else if (key === 'criticalGem') {
core.setLocalStorage('criticalGem', n);
// core.setLocalStorage('criticalGem', n);
}
}
@ -371,7 +404,7 @@ function handleActionSetting<T extends number | boolean>(
flags.autoSkill = n;
} else if (key === 'fixed') {
// 定点查看
core.setLocalStorage('fixed', n);
// core.setLocalStorage('fixed', n);
}
}
@ -382,10 +415,10 @@ function handleUtilsSetting<T extends number | boolean>(
) {
if (key === 'betterLoad') {
// 加载优化
core.setLocalStorage('betterLoad', n);
// core.setLocalStorage('betterLoad', n);
} else if (key === 'autoScale') {
// 自动放缩
core.setLocalStorage('autoScale', n);
// core.setLocalStorage('autoScale', n);
}
}

View File

@ -84,3 +84,6 @@ export class GameStorage<T> {
window.addEventListener('unload', () => {
GameStorage.list.forEach(v => v.write());
});
window.addEventListener('blur', () => {
GameStorage.list.forEach(v => v.write());
});

View File

@ -1,54 +1,54 @@
import pop from '@/plugin/pop';
import use from '@/plugin/use';
import animate from '@/plugin/animateController';
import utils from '@/plugin/utils';
import status from '@/plugin/ui/statusBar';
import fly from '@/plugin/ui/fly';
import chase from '@/plugin/chase/chase';
import webglUtils from '@/plugin/webgl/utils';
import shadow from '@/plugin/shadow/shadow';
import gameShadow from '@/plugin/shadow/gameShadow';
import achievement from '@/plugin/ui/achievement';
import completion, { floors } from '@/plugin/completion';
import path from '@/plugin/fx/path';
import gameCanvas from '@/plugin/fx/gameCanvas';
import noise from '@/plugin/fx/noise';
import smooth from '@/plugin/fx/smoothView';
import frag from '@/plugin/fx/frag';
import { Mota } from '.';
// import pop from '@/plugin/pop';
// import use from '@/plugin/use';
// import animate from '@/plugin/animateController';
// import utils from '@/plugin/utils';
// import status from '@/plugin/ui/statusBar';
// import fly from '@/plugin/ui/fly';
// import chase from '@/plugin/chase/chase';
// import webglUtils from '@/plugin/webgl/utils';
// import shadow from '@/plugin/shadow/shadow';
// import gameShadow from '@/plugin/shadow/gameShadow';
// import achievement from '@/plugin/ui/achievement';
// import completion, { floors } from '@/plugin/completion';
// import path from '@/plugin/fx/path';
// import gameCanvas from '@/plugin/fx/gameCanvas';
// import noise from '@/plugin/fx/noise';
// import smooth from '@/plugin/fx/smoothView';
// import frag from '@/plugin/fx/frag';
// import { Mota } from '.';
// todo: 将插件更改为注册形式,分为渲染进程和游戏进程两部分,同时分配优先级
// // todo: 将插件更改为注册形式,分为渲染进程和游戏进程两部分,同时分配优先级
export function resolvePlugin() {
const toForward: [keyof Mota['plugin'], any][] = [
['pop', pop()],
['use', use()],
['animate', animate()],
['utils', utils()],
['status', status()],
['fly', fly()],
['chase', chase()],
['webglUtils', webglUtils()],
['shadow', shadow()],
['gameShadow', gameShadow()],
['achievement', achievement()],
['completion', completion()],
['path', path()],
['gameCanvas', gameCanvas()],
['noise', noise()],
['smooth', smooth()],
['frag', frag()]
];
// export function resolvePlugin() {
// const toForward: [keyof Mota['plugin'], any][] = [
// ['pop', pop()],
// ['use', use()],
// ['animate', animate()],
// ['utils', utils()],
// ['status', status()],
// ['fly', fly()],
// ['chase', chase()],
// ['webglUtils', webglUtils()],
// ['shadow', shadow()],
// ['gameShadow', gameShadow()],
// ['achievement', achievement()],
// ['completion', completion()],
// ['path', path()],
// ['gameCanvas', gameCanvas()],
// ['noise', noise()],
// ['smooth', smooth()],
// ['frag', frag()]
// ];
for (const [key, obj] of toForward) {
mota.plugin[key] = obj;
}
// for (const [key, obj] of toForward) {
// mota.plugin[key] = obj;
// }
// 完成度相关
Object.values(floors).forEach((v, i) => {
const from = core.floorIds.indexOf(v[0]);
const to = core.floorIds.indexOf(v[1]);
const all = core.floorIds.slice(from, to + 1);
floors[i + 1] = all;
});
}
// // 完成度相关
// Object.values(floors).forEach((v, i) => {
// const from = core.floorIds.indexOf(v[0]);
// const to = core.floorIds.indexOf(v[1]);
// const all = core.floorIds.slice(from, to + 1);
// floors[i + 1] = all;
// });
// }

1
src/game/index.ts Normal file
View File

@ -0,0 +1 @@
import './system';

228
src/game/system.ts Normal file
View File

@ -0,0 +1,228 @@
import type { AudioPlayer } from '@/core/audio/audio';
import type { BgmController } from '@/core/audio/bgm';
import type { SoundController, SoundEffect } from '@/core/audio/sound';
import type { Disposable } from '@/core/common/disposable';
import type {
EventEmitter,
IndexedEventEmitter
} from '@/core/common/eventEmitter';
import type { loading } from '@/core/loader/load';
import type {
Resource,
ResourceStore,
ResourceType,
ZippedResource
} from '@/core/loader/resource';
import type { Hotkey } from '@/core/main/custom/hotkey';
import type { Keyboard } from '@/core/main/custom/keyboard';
import type { CustomToolbar } from '@/core/main/custom/toolbar';
import type { Focus, GameUi, UiController } from '@/core/main/custom/ui';
import type { gameListener, hook } from '@/core/main/game';
import type {
MotaSetting,
SettingDisplayer,
SettingStorage
} from '@/core/main/setting';
import type { GameStorage } from '@/core/main/storage';
import type { DamageEnemy, EnemyCollection } from '@/plugin/game/enemy/damage';
import type { enemySpecials } from '@/plugin/game/enemy/special';
import type { Range } from '@/plugin/game/range';
import type { KeyCode } from '@/plugin/keyCodes';
interface ClassInterface {
// 渲染进程与游戏进程通用
EventEmitter: typeof EventEmitter;
IndexedEventEmitter: typeof IndexedEventEmitter;
Disposable: typeof Disposable;
// 定义于渲染进程录像中会进行polyfill但是不执行任何内容
GameStorage: typeof GameStorage;
MotaSetting: typeof MotaSetting;
SettingDisplayer: typeof SettingDisplayer;
Resource: typeof Resource;
ZippedResource: typeof ZippedResource;
ResourceStore: typeof ResourceStore;
Focus: typeof Focus;
GameUi: typeof GameUi;
UiController: typeof UiController;
Hotkey: typeof Hotkey;
Keyboard: typeof Keyboard;
CustomToolbar: typeof CustomToolbar;
AudioPlayer: typeof AudioPlayer;
SoundEffect: typeof SoundEffect;
SoundController: typeof SoundController;
BgmController: typeof BgmController;
// todo: 放到插件 ShaderEffect: typeof ShaderEffect;
// 定义于游戏进程,渲染进程依然可用
Range: typeof Range;
EnemyCollection: typeof EnemyCollection;
DamageEnemy: typeof DamageEnemy;
}
interface FunctionInterface {
// 定义于渲染进程录像中会进行polyfill但是不执行任何内容
readyAllResource(): void;
// 定义于游戏进程,渲染进程依然可用
// todo
}
interface VariableInterface {
// 定义于渲染进程录像中会进行polyfill
loading: typeof loading;
hook: typeof hook;
gameListener: typeof gameListener;
mainSetting: MotaSetting;
gameKey: Hotkey;
mainUi: UiController;
fixedUi: UiController;
KeyCode: typeof KeyCode;
// isMobile: boolean;
bgm: BgmController;
sound: SoundController;
resource: ResourceStore<Exclude<ResourceType, 'zip'>>;
zipResource: ResourceStore<'zip'>;
settingStorage: GameStorage<SettingStorage>;
// 定义于游戏进程,渲染进程依然可用
haloSpecials: number[];
enemySpecials: typeof enemySpecials;
}
interface SystemInterfaceMap {
class: ClassInterface;
fn: FunctionInterface;
var: VariableInterface;
}
type InterfaceType = keyof SystemInterfaceMap;
export interface IMota {
/**
*
* @param type
* @param key
*/
require<T extends InterfaceType, K extends keyof SystemInterfaceMap[T]>(
type: T,
key: K
): SystemInterfaceMap[T][K];
/**
*
* @param type
* @param key
*/
require(type: InterfaceType, key: string): any;
/**
*
* @param type
*/
requireAll<T extends InterfaceType>(type: T): SystemInterfaceMap[T];
/**
*
* @param type
* @param key
* @param data
*/
register<T extends InterfaceType, K extends keyof SystemInterfaceMap[T]>(
type: T,
key: K,
data: SystemInterfaceMap[T][K]
): void;
/**
*
* @param type
* @param key
* @param data
*/
register(type: InterfaceType, key: string, data: any): void;
}
/**
* Mota
*/
class Mota {
private static classes: Record<string, any> = {};
private static functions: Record<string, any> = {};
private static variables: Record<string, any> = {};
constructor() {
throw new Error(`System interface class cannot be constructed.`);
}
/**
*
* @param type
* @param key
*/
static require<
T extends InterfaceType,
K extends keyof SystemInterfaceMap[T]
>(type: T, key: K): SystemInterfaceMap[T][K];
/**
*
* @param type
* @param key
*/
static require(type: InterfaceType, key: string): any;
static require(type: InterfaceType, key: string): any {
const data = this.getByType(type)[key];
if (!!data) return data;
else {
throw new Error(
`Cannot resolve require: type='${type}',key='${key}'`
);
}
}
/**
*
* @param type
*/
static requireAll<T extends InterfaceType>(type: T): SystemInterfaceMap[T] {
return this.getByType(type) as SystemInterfaceMap[T];
}
/**
*
* @param type
* @param key
* @param data
*/
static register<
T extends InterfaceType,
K extends keyof SystemInterfaceMap[T]
>(type: T, key: K, data: SystemInterfaceMap[T][K]): void;
/**
*
* @param type
* @param key
* @param data
*/
static register(type: InterfaceType, key: string, data: any): void;
static register(type: InterfaceType, key: string, data: any) {
const obj = this.getByType(type);
if (key in obj) {
console.warn(
`重复的样板接口注册: type='${type}', key='${key}',已将其覆盖`
);
}
obj[key] = data;
}
private static getByType(type: InterfaceType) {
return type === 'class'
? this.classes
: type === 'fn'
? this.functions
: this.variables;
}
}
declare global {
interface Window {
Mota: IMota;
}
}
window.Mota = Mota;

View File

@ -1,12 +1,11 @@
import { createApp } from 'vue';
import App from './App.vue';
import App2 from './App2.vue';
import './styles.less';
import 'ant-design-vue/dist/antd.dark.css';
import './game/index';
import './core/index';
createApp(App).mount('#root');
// createApp(App2).mount('#root2');
main.init('play');
main.listen();

View File

@ -11,7 +11,9 @@ const potionItems: AllIdsOf<'items'>[] = [
'I491'
];
mota.game.hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
const hook = Mota.require('var', 'hook');
hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
// 获得一个道具后触发的事件
// itemId获得的道具IDx和y是该道具所在的坐标
// isGentleClick是否是轻按触发的
@ -45,7 +47,7 @@ mota.game.hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
if (todo.length > 0) core.insertAction(todo, x, y);
});
mota.game.hook.on('afterOpenDoor', (doorId, x, y) => {
hook.on('afterOpenDoor', (doorId, x, y) => {
// 开一个门后触发的事件
const todo: any[] = [];

View File

@ -227,7 +227,7 @@ export function getSkillConsume(skill) {
export function openTree() {
if (main.replayChecking) return;
mota.ui.main.open('skillTree');
Mota.require('var', 'mainUi').open('skillTree');
}
/**

View File

@ -2,6 +2,8 @@
export {};
(function () {
const { mainUi, fixedUi } = Mota.requireAll('var');
if (main.replayChecking)
return (core.plugin.gameUi = {
openItemShop: () => 0,
@ -10,7 +12,7 @@ export {};
function openItemShop(itemShopId) {
if (!core.isReplaying()) {
mota.ui.main.open('shop', {
Mota.require('var', 'mainUi').open('shop', {
shopId: itemShopId
});
}
@ -23,19 +25,19 @@ export {};
}
ui.prototype.drawBook = function () {
if (!core.isReplaying()) return mota.ui.main.open('book');
if (!core.isReplaying()) return mainUi.open('book');
};
ui.prototype._drawToolbox = function () {
if (!core.isReplaying()) return mota.ui.main.open('toolbox');
if (!core.isReplaying()) return mainUi.open('toolbox');
};
ui.prototype._drawEquipbox = function () {
if (!core.isReplaying()) return mota.ui.main.open('equipbox');
if (!core.isReplaying()) return mainUi.open('equipbox');
};
ui.prototype.drawFly = function () {
if (!core.isReplaying()) return mota.ui.main.open('fly');
if (!core.isReplaying()) return mainUi.open('fly');
};
control.prototype.updateStatusBar_update = function () {
@ -48,15 +50,15 @@ export {};
core.control.noAutoEvents = true;
// 更新vue状态栏
updateVueStatusBar();
mota.game.hook.emit('statusBarUpdate');
Mota.require('var', 'hook').emit('statusBarUpdate');
};
// todo: 多个状态栏分离与控制
control.prototype.showStatusBar = function () {
if (main.mode == 'editor') return;
core.removeFlag('hideStatusBar');
if (!mota.ui.fixed.hasName('statusBar')) {
mota.ui.fixed.open('statusBar');
if (!fixedUi.hasName('statusBar')) {
fixedUi.open('statusBar');
}
core.dom.tools.hard.style.display = 'block';
core.dom.toolBar.style.display = 'block';
@ -68,7 +70,7 @@ export {};
// 如果原本就是隐藏的,则先显示
if (!core.domStyle.showStatusBar) this.showStatusBar();
if (core.isReplaying()) showToolbox = true;
mota.ui.fixed.closeByName('statusBar');
fixedUi.closeByName('statusBar');
var toolItems = core.dom.tools;
core.setFlag('hideStatusBar', true);
@ -87,7 +89,7 @@ export {};
function openSkill() {
if (core.isReplaying()) return;
mota.ui.main.open('skill');
mainUi.open('skill');
}
core.plugin.gameUi = {

View File

@ -8,7 +8,7 @@
* Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
* But these are "more general", as they should work across browsers & OS`s.
*/
export const enum KeyCode {
export enum KeyCode {
DependsOnKbLayout = -1,
/**

View File

@ -51,7 +51,9 @@ export function completeAchievement(type: AchievementType, index: number) {
if (type === 'explore' && !Object.values(achiDict).includes(index)) {
checkCompletionAchievement();
}
mota.ui.fixed.open('completeAchi', { complete: `${type},${index}` });
Mota.require('var', 'fixedUi').open('completeAchi', {
complete: `${type},${index}`
});
}
/**

View File

@ -361,7 +361,7 @@ type Forward<T> = {
type ForwardKeys<T> = keyof Forward<T>;
declare const mota: import('../core/index').Mota;
declare const Mota: import('../game/system').IMota;
interface Window {
mota: import('../core/index').Mota;
Mota: import('../game/system').IMota;
}

View File

@ -176,7 +176,7 @@ function getAllAchievements(type: AchievementType): ResolvedAchievement[] {
}
function exit() {
mota.ui.main.close(props.num);
Mota.require('var', 'mainUi').close(props.num);
}
</script>

View File

@ -32,6 +32,7 @@ import { computed, onMounted, ref } from 'vue';
import Colomn from '../components/colomn.vue';
import bgm from '../data/bgm.json';
import { splitText } from '../plugin/utils';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -55,7 +56,7 @@ const content = computed(() => {
const name = computed(() => list[selected.value]!.name);
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
function select(id: BgmIds) {

View File

@ -45,11 +45,11 @@ import Scroll from '../components/scroll.vue';
import { getDamageColor, has, keycode } from '../plugin/utils';
import BookDetail from './bookDetail.vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { KeyCode } from '../plugin/keyCodes';
import { ToShowEnemy, detailInfo } from '../plugin/ui/book';
import { getDetailedEnemy } from '../plugin/ui/fixed';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -118,8 +118,8 @@ async function show() {
* 退出怪物手册
*/
async function exit() {
const hold = mota.ui.main.holdOn();
mota.ui.main.close(props.num);
const hold = mainUi.holdOn();
mainUi.close(props.num);
if (core.events.recoverEvents(core.status.event.interval)) {
return;
} else if (has(core.status.event.ui)) {

View File

@ -10,6 +10,7 @@ import { Animation, hyper, sleep } from 'mutate-animate';
import { onMounted } from 'vue';
import { has } from '../plugin/utils';
import { GameUi } from '@/core/main/custom/ui';
import { fixedUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -56,7 +57,7 @@ onMounted(async () => {
}
if (time >= 4050) {
mota.ui.fixed.close(props.num);
fixedUi.close(props.num);
ani.ticker.destroy();
}

View File

@ -28,6 +28,7 @@ import {
totalPoint
} from '../plugin/ui/achievement';
import { GameUi } from '@/core/main/custom/ui';
import { fixedUi } from '@/core/main/init/ui';
const height = window.innerHeight;
@ -62,7 +63,7 @@ onMounted(async () => {
now.value = Math.floor(nowPoint + point * ratio);
});
await sleep(4600);
mota.ui.fixed.close(props.num);
fixedUi.close(props.num);
});
</script>

View File

@ -24,6 +24,7 @@ import { splitText } from '../plugin/utils';
import Colomn from '../components/colomn.vue';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -35,7 +36,7 @@ type DescKey = keyof typeof desc;
const selected = ref(Object.keys(desc)[0] as DescKey);
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
const content = computed(() => {

View File

@ -197,6 +197,7 @@ import { hyper } from 'mutate-animate';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { getStatusLabel } from '../plugin/utils';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -316,7 +317,7 @@ function changeSort() {
}
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
function clickList(i: number) {
@ -434,10 +435,10 @@ function dragout(e: Event) {
}
function toTool() {
mota.ui.main.holdOn();
mainUi.holdOn();
exit();
nextTick(() => {
mota.ui.main.open('toolbox');
mainUi.open('toolbox');
});
}

View File

@ -14,6 +14,7 @@ import BookDetail from './bookDetail.vue';
import { detailInfo } from '../plugin/ui/book';
import { hovered } from '@/core/main/init/fixed';
import { GameUi } from '@/core/main/custom/ui';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -41,7 +42,7 @@ if (hovered) {
}
function close() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
</script>

View File

@ -102,6 +102,7 @@ import { downloadCanvasImage, tip } from '../plugin/utils';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { createChangable } from '@/plugin/ui/common';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -158,7 +159,7 @@ let thumbCtx: CanvasRenderingContext2D;
let downloadMode = false;
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
const title = computed(() => {

View File

@ -57,6 +57,7 @@ import Box from '../components/box.vue';
import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue';
import { GameUi } from '@/core/main/custom/ui';
import { fixedUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -99,7 +100,7 @@ const info = reactive<MarkedEnemy>({
const hidden = ref(false);
watch(hidden, n => {
if (n) mota.ui.fixed.close(props.num);
if (n) fixedUi.close(props.num);
});
watch(enemy.update, update);

View File

@ -85,6 +85,7 @@ import { sleep } from 'mutate-animate';
import { KeyCode } from '../plugin/keyCodes';
import { gameKey } from '@/core/main/init/hotkey';
import { GameUi } from '@/core/main/custom/ui';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
info?: MotaSetting;
@ -143,7 +144,7 @@ function click(key: string, index: number, item: MotaSettingItem) {
}
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
gameKey.use(props.ui.symbol);

View File

@ -174,6 +174,7 @@ import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -306,7 +307,7 @@ gameKey
function exit() {
if (bought) core.status.route.push('closeShop');
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
onMounted(async () => {

View File

@ -21,6 +21,7 @@ import { computed, ref } from 'vue';
import skills from '../data/skill.json';
import { has } from '../plugin/utils';
import Column from '../components/colomn.vue';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -64,7 +65,7 @@ const content = computed(() => {
});
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
</script>

View File

@ -82,6 +82,7 @@ import { isMobile } from '../plugin/use';
import { sleep } from 'mutate-animate';
import { gameKey } from '@/core/main/init/hotkey';
import { GameUi } from '@/core/main/custom/ui';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -179,7 +180,7 @@ const level = computed(() => {
});
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
function resize() {

View File

@ -72,6 +72,7 @@ import { loading } from '../core/loader/load';
import { isMobile } from '../plugin/use';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -155,7 +156,7 @@ async function clickStartButton(id: string) {
}
if (id === 'replay') core.chooseReplayFile();
if (id === 'achievement') {
mota.ui.main.open('achievement');
mainUi.open('achievement');
}
}

View File

@ -119,6 +119,7 @@ import { message } from 'ant-design-vue';
import { KeyCode } from '../plugin/keyCodes';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/init/hotkey';
import { mainUi } from '@/core/main/init/ui';
const props = defineProps<{
num: number;
@ -172,17 +173,17 @@ async function select(id: ShowItemIds, nouse: boolean = false) {
}
function exit() {
mota.ui.main.close(props.num);
mainUi.close(props.num);
}
function use(id: ShowItemIds) {
if (id === 'none') return;
if (core.canUseItem(id)) {
const hold = mota.ui.main.holdOn();
const hold = mainUi.holdOn();
exit();
nextTick(() => {
core.useItem(id, false, () => {
if (mota.ui.main.stack.length === 0) {
if (mainUi.stack.length === 0) {
hold.end();
}
});
@ -196,10 +197,10 @@ function use(id: ShowItemIds) {
}
async function toEquip() {
mota.ui.main.holdOn();
mainUi.holdOn();
exit();
nextTick(() => {
mota.ui.main.open('equipbox');
mainUi.open('equipbox');
});
}