refactor: src/core/main 全部移入 monorepo

This commit is contained in:
unanmed 2025-03-05 21:16:37 +08:00
parent 9ec13e5314
commit 960dbd429a
79 changed files with 1872 additions and 3616 deletions

1
.gitignore vendored
View File

@ -49,3 +49,4 @@ script/people.ts
docs/
user.ts
.antlr
graph.svg

12
.madgerc Normal file
View File

@ -0,0 +1,12 @@
{
"fileExtensions": [
"ts",
"tsx"
],
"tsConfig": "./tsconfig.json",
"detectiveOptions": {
"ts": {
"skipTypeImports": true
}
}
}

View File

@ -63,6 +63,7 @@
"glob": "^11.0.1",
"globals": "^15.14.0",
"less": "^4.2.0",
"madge": "^8.0.0",
"postcss-preset-env": "^9.6.0",
"rollup": "^3.29.4",
"terser": "^5.31.6",

View File

@ -1 +1,2 @@
export * from './keyCodes';
export * from './types';

View File

@ -0,0 +1,4 @@
export interface ResponseBase {
code: number;
message: string;
}

View File

@ -0,0 +1,6 @@
{
"name": "@motajs/legacy-system",
"dependencies": {
"@motajs/system-action": "workspace:*"
}
}

View File

@ -0,0 +1,2 @@
export * from './misc';
export * from './storage';

View File

@ -1,5 +1,5 @@
import { Keyboard } from '../custom/keyboard';
import KeyboardUI from '@/panel/keyboard.vue';
import { Keyboard } from '@motajs/system-action';
import KeyboardUI from '../panel/keyboard.vue';
interface VirtualKeyProps {
keyboard: Keyboard;

View File

@ -4,6 +4,8 @@
"@motajs/render": "workspace:*",
"@motajs/common": "workspace:*",
"@motajs/client": "workspace:*",
"@motajs/client-base": "workspace:*"
"@motajs/client-base": "workspace:*",
"@motajs/legacy-system": "workspace:*",
"@motajs/system-action": "workspace:*"
}
}

View File

@ -0,0 +1,6 @@
export { default as Box } from './box.vue';
export { default as BoxAnimate } from './boxAnimate.vue';
export { default as Column } from './colomn.vue';
export { default as EnemyOne } from './enemyOne.vue';
export { default as Minimap } from './minimap.vue';
export { default as Scroll } from './scroll.vue';

View File

@ -8,7 +8,7 @@ import { requireUniqueSymbol } from '../utils';
import { MinimapDrawer, getArea } from '../tools/fly';
import { useDrag, useWheel } from '../use';
import { debounce } from 'lodash-es';
import { mainSetting } from '@/core/main/setting';
import { mainSetting } from '../preset/ui';
const props = defineProps<{
action?: boolean;
@ -168,6 +168,7 @@ onMounted(() => {
});
onUnmounted(() => {
const hook = Mota.require('var', 'hook');
hook.off('afterChangeFloor', onChange);
hook.off('afterBattle', afterBattle);
});

View File

@ -1,6 +1,5 @@
import { Component, shallowReactive } from 'vue';
import { EventEmitter } from '@motajs/legacy-common';
import { Hotkey } from './hotkey';
interface FocusEvent<T> {
focus: (before: T | null, after: T) => void;
@ -125,6 +124,7 @@ type UiVBind = Record<string, any>;
interface MountedVBind {
num: number;
ui: GameUi;
controller: UiController;
[x: string]: any;
}
@ -132,14 +132,12 @@ export class GameUi extends EventEmitter<GameUiEvent> {
static uiList: GameUi[] = [];
component: Component;
hotkey?: Hotkey;
id: string;
symbol: symbol = Symbol();
constructor(id: string, component: Component, hotkey?: Hotkey) {
constructor(id: string, component: Component) {
super();
this.component = component;
this.hotkey = hotkey;
this.id = id;
GameUi.uiList.push(this);
}
@ -273,6 +271,7 @@ export class UiController extends Focus<IndexedGameUi> {
const bind = {
num,
ui,
controller: this,
...(vBind ?? {})
};
const sui = ui.with(bind, vOn);

View File

@ -1,18 +1,10 @@
import BoxAnimate from '@/components/boxAnimate.vue';
import { EventEmitter } from 'eventemitter3';
import { logger } from '@motajs/common';
import { ResponseBase } from '@/core/interface';
import {
deleteWith,
ensureArray,
getIconHeight,
parseCss,
tip
} from '@motajs/legacy-ui';
import { deleteWith, ensureArray, parseCss, tip } from '@motajs/legacy-ui';
import { ResponseBase } from '@motajs/client-base';
import axios, { AxiosResponse, toFormData } from 'axios';
import { Component, VNode, h, shallowReactive } from 'vue';
import { VNode, h, shallowReactive } from 'vue';
// /* @__PURE__ */ import { id, password } from '../../../../user';
import { mainSetting } from '../setting';
type CSSObj = Partial<Record<CanParseCss, string>>;
@ -213,8 +205,8 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
* @param stroke
*/
color(fill?: string, stroke?: string) {
fill && (this.textColor = fill);
stroke && (this.strokeColor = stroke);
if (fill) this.textColor = fill;
if (stroke) this.strokeColor = stroke;
}
/**
@ -492,41 +484,3 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
this.specList[type] = fn;
}
}
// 图标类型
Danmaku.registerSpecContent('i', content => {
const height = getIconHeight(content as AllIds);
return h(BoxAnimate as Component, {
id: content,
noborder: true,
noAnimate: true,
width: 32,
height
});
});
if (import.meta.env.DEV) {
Danmaku.backend = `/danmaku`;
}
Mota.require('var', 'hook').once('reset', () => {
Danmaku.fetch();
});
// 勇士移动后显示弹幕
Mota.require('var', 'hook').on('moveOneStep', (x, y, floor) => {
const enabled = mainSetting.getValue('ui.danmaku', true);
if (!enabled) return;
const f = Danmaku.allInPos[floor];
if (f) {
const danmaku = f[`${x},${y}`];
if (danmaku) {
danmaku.forEach(v => {
setTimeout(() => {
v.show();
}, Math.random() * 1000);
});
}
}
});

View File

@ -1,7 +1,12 @@
export * as UI from './ui';
export * as Components from './components';
export * from './preset';
export * from './tools';
export * from './animateController';
export * from './controller';
export * from './danmaku';
export * from './mark';
export * from './setting';
export * from './use';
export * from './utils';

View File

@ -1,4 +1,4 @@
import { fixedUi } from '@/core/main/init/ui';
import { fixedUi } from './preset/ui';
import type { DamageEnemy } from '@/game/enemy/damage';
import { tip } from './utils';
import { ref, Ref } from 'vue';

View File

@ -0,0 +1,43 @@
import { Danmaku } from '../danmaku';
import { Component, h } from 'vue';
import { mainSetting } from './ui';
import { getIconHeight } from '../utils';
import { BoxAnimate } from '../components';
// 图标类型
Danmaku.registerSpecContent('i', content => {
const height = getIconHeight(content as AllIds);
return h(BoxAnimate as Component, {
id: content,
noborder: true,
noAnimate: true,
width: 32,
height
});
});
if (import.meta.env.DEV) {
Danmaku.backend = `/danmaku`;
}
Mota.require('var', 'hook').once('reset', () => {
Danmaku.fetch();
});
// 勇士移动后显示弹幕
Mota.require('var', 'hook').on('moveOneStep', (x, y, floor) => {
const enabled = mainSetting.getValue('ui.danmaku', true);
if (!enabled) return;
const f = Danmaku.allInPos[floor];
if (f) {
const danmaku = f[`${x},${y}`];
if (danmaku) {
danmaku.forEach(v => {
setTimeout(() => {
v.show();
}, Math.random() * 1000);
});
}
}
});

View File

@ -18,7 +18,7 @@ const showFixed = debounce((block: Block) => {
if (!enemy) return;
fixedUi.open(
'fixed',
{ enemy, close, loc: [cx, cy] },
{ enemy, close, loc: [cx, cy], hovered },
{ close: closeFixed }
);
}, 200);

View File

@ -1,5 +1,5 @@
import { KeyCode } from '@motajs/client-base';
import { gameKey, HotkeyJSON } from '../custom/hotkey';
import { gameKey, HotkeyJSON } from '@motajs/system-action';
import {
openDanmakuPoster,
tip,
@ -9,7 +9,7 @@ import {
} from '@motajs/legacy-ui';
import { hovered } from './fixed';
import { mainUi } from './ui';
import { GameStorage } from '../storage';
import { GameStorage } from '@motajs/legacy-system';
export const mainScope = Symbol.for('@key_main');

View File

@ -0,0 +1,6 @@
export * from './ui';
export * from './settings';
export * from './danmaku';
export * from './fixed';
export * from './hotkey';
export * from './keyboard';

View File

@ -1,5 +1,5 @@
import { KeyCode } from '@motajs/client-base';
import { Keyboard } from '../custom/keyboard';
import { Keyboard } from '@motajs/system-action';
const qweKey = new Keyboard('qwe'); // 字母键盘A-Z
const numKey = new Keyboard('num'); // 数字键盘1-0
@ -239,7 +239,7 @@ numKey
y: 0,
width: 45,
height: 45,
text: `<span style='font-size: 80%'>\$<br />4</span>`
text: `<span style='font-size: 80%'>$<br />4</span>`
})
.add({
key: KeyCode.Digit5,

View File

@ -1,7 +1,7 @@
import type { SettingComponent, SettingComponentProps } from '../setting';
import { Button, InputNumber, Radio } from 'ant-design-vue';
import { mainUi } from './ui';
import { gameKey } from '../custom/hotkey';
import { gameKey } from '@motajs/system-action';
interface Components {
Default: SettingComponent;
@ -28,7 +28,7 @@ export function createSettingComponents() {
return com;
}
function DefaultSetting(props: SettingComponentProps) {
function DefaultSetting(_props: SettingComponentProps) {
return (
<div>
<span> </span>
@ -149,7 +149,7 @@ function showSpecialSetting(id: string, vBind?: any) {
mainUi.open(id, vBind);
}
function HotkeySetting(props: SettingComponentProps) {
function HotkeySetting(_props: SettingComponentProps) {
return (
<div style="display: flex; justify-content: center">
<Button
@ -168,7 +168,7 @@ function HotkeySetting(props: SettingComponentProps) {
);
}
function ToolbarEditor(props: SettingComponentProps) {
function ToolbarEditor(_props: SettingComponentProps) {
return (
<div style="display: flex; justify-content: center">
<Button
@ -183,7 +183,7 @@ function ToolbarEditor(props: SettingComponentProps) {
);
}
function PerformanceSetting(props: SettingComponentProps) {
function PerformanceSetting(_props: SettingComponentProps) {
return (
<div style="display: flex; justify-content: center">
<Button

View File

@ -0,0 +1,364 @@
import { GameStorage, VirtualKey } from '@motajs/legacy-system';
import {
createSettingComponents,
GameUi,
isMobile,
MotaSetting,
triggerFullscreen,
UI,
UiController
} from '@motajs/legacy-ui';
import { bgmController, soundPlayer } from '@/module';
import settingsText from '@/data/settings.json';
//#region legacy-ui
export const mainUi = new UiController();
mainUi.register(
new GameUi('book', UI.Book),
new GameUi('toolbox', UI.Toolbox),
new GameUi('equipbox', UI.Equipbox),
new GameUi('settings', UI.Settings),
new GameUi('desc', UI.Desc),
new GameUi('skill', UI.Skill),
new GameUi('skillTree', UI.SkillTree),
new GameUi('fly', UI.Fly),
new GameUi('fixedDetail', UI.FixedDetail),
new GameUi('shop', UI.Shop),
new GameUi('achievement', UI.Achievement),
new GameUi('hotkey', UI.Hotkey),
new GameUi('toolEditor', UI.ToolEditor),
new GameUi('virtualKey', VirtualKey)
// todo: 把游戏主 div 加入到 mainUi 里面
);
mainUi.showAll();
export const fixedUi = new UiController(true);
fixedUi.register(
new GameUi('markedEnemy', UI.Marked),
new GameUi('fixed', UI.Fixed),
new GameUi('chapter', UI.Chapter),
new GameUi('completeAchi', UI.CompleteAchi),
new GameUi('start', UI.Start),
new GameUi('toolbar', UI.Toolbar),
new GameUi('load', UI.Load),
new GameUi('danmaku', UI.Danmaku),
new GameUi('danmakuEditor', UI.DanmakuEditor),
new GameUi('tips', UI.Tips)
);
fixedUi.showAll();
const hook = Mota.require('var', 'hook');
hook.once('mounted', () => {
const ui = document.getElementById('ui-main')!;
const fixed = document.getElementById('ui-fixed')!;
const blur = mainSetting.getSetting('screen.blur');
mainUi.on('start', () => {
ui.style.display = 'flex';
if (blur?.value) {
ui.style.backdropFilter = 'blur(5px)';
ui.style.backgroundColor = 'rgba(0,0,0,0.7333)';
} else {
ui.style.backdropFilter = 'none';
ui.style.backgroundColor = 'rgba(0,0,0,0.85)';
}
core.lockControl();
});
mainUi.on('end', noClosePanel => {
ui.style.display = 'none';
if (!noClosePanel) {
core.closePanel();
}
});
fixedUi.on('start', () => {
fixed.style.display = 'block';
});
fixedUi.on('end', () => {
fixed.style.display = 'none';
});
});
//#endregion
//#region legacy-setting
const COM = createSettingComponents();
export const mainSetting = new MotaSetting();
// 添加不参与全局存储的设置
MotaSetting.noStorage.push('action.autoSkill', 'screen.fullscreen');
const storage = new GameStorage(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') {
handleScreenSetting(setting, n, o);
} else if (root === 'action') {
handleActionSetting(setting, n, o);
} else if (root === 'audio') {
handleAudioSetting(setting, n, o);
} else if (root === 'ui') {
handleUiSetting(setting, n, o);
}
});
const root = document.getElementById('root') as HTMLDivElement;
function handleScreenSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'fullscreen') {
// 全屏
triggerFullscreen(n as boolean);
} else if (key === 'heroDetail') {
// 勇士显伤
core.drawHero();
} else if (key === 'fontSize') {
// 字体大小
root.style.fontSize = `${n}px`;
const absoluteSize = (n as number) * devicePixelRatio;
storage.setValue('@@absoluteFontSize', absoluteSize);
storage.write();
} else if (key === 'fontSizeStatus') {
// fontSize.value = n as number;
}
}
function handleActionSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'autoSkill') {
// 自动切换技能
const HeroSkill = Mota.require('module', 'Mechanism').HeroSkill;
HeroSkill.setAutoSkill(n as boolean);
core.status.route.push(`set:autoSkill:${n}`);
}
}
function handleAudioSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'bgmEnabled') {
bgmController.setEnabled(n as boolean);
core.checkBgm();
} else if (key === 'bgmVolume') {
bgmController.setVolume((n as number) / 100);
} else if (key === 'soundEnabled') {
soundPlayer.setEnabled(n as boolean);
} else if (key === 'soundVolume') {
soundPlayer.setVolume((n as number) / 100);
}
}
function handleUiSetting<T extends number | boolean>(key: string, n: T, _o: T) {
if (key === 'danmaku') {
if (n) {
fixedUi.open('danmaku');
} else {
fixedUi.closeByName('danmaku');
}
} else if (key === 'tips') {
if (n && core.isPlaying()) {
fixedUi.open('tips');
} else {
fixedUi.closeByName('tips');
}
}
}
// ----- 游戏的所有设置项
// todo: 虚拟键盘缩放,小地图楼传缩放
mainSetting
.register(
'screen',
'显示设置',
new MotaSetting()
.register('fullscreen', '全屏游戏', false, COM.Boolean)
.register('halo', '光环显示', true, COM.Boolean)
.register('itemDetail', '宝石血瓶显伤', true, COM.Boolean)
.register('heroDetail', '勇士显伤', false, COM.Boolean)
.register('transition', '界面动画', false, COM.Boolean)
.register('fontSize', '字体大小', 16, COM.Number, [2, 48, 1])
.register(
'fontSizeStatus',
'状态栏字体',
16,
COM.Number,
[10, 300, 10]
)
.register('smoothView', '平滑镜头', true, COM.Boolean)
.register('criticalGem', '临界显示方式', false, COM.Boolean)
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
.register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500])
.register('blur', '背景虚化', !isMobile, COM.Boolean)
)
.register(
'action',
'操作设置',
new MotaSetting()
.register('autoSkill', '自动切换技能', true, COM.Boolean)
.register('fixed', '定点查看', true, COM.Boolean)
.register('hotkey', '快捷键', false, COM.HotkeySetting)
.setDisplayFunc('hotkey', () => '')
)
.register(
'audio',
'音频设置',
new MotaSetting()
.register('bgmEnabled', '开启音乐', true, COM.Boolean)
.register('bgmVolume', '音乐音量', 80, COM.Number, [0, 100, 5])
.register('soundEnabled', '开启音效', true, COM.Boolean)
.register('soundVolume', '音效音量', 80, COM.Number, [0, 100, 5])
)
.register(
'utils',
'系统设置',
new MotaSetting()
.register('betterLoad', '优化加载', true, COM.Boolean)
.register('autoScale', '自动放缩', true, COM.Boolean)
)
.register(
'fx',
'特效设置',
new MotaSetting()
.register('paraLight', '野外阴影', true, COM.Boolean)
.register('frag', '打怪特效', true, COM.Boolean)
.register('portalParticle', '传送门特效', true, COM.Boolean)
)
.register(
'ui',
'ui设置',
new MotaSetting()
.register('mapScale', '小地图缩放', 100, COM.Number, [50, 1000, 50])
.setDisplayFunc('mapScale', value => `${value}%`)
.register('mapLazy', '小地图懒更新', false, COM.Boolean)
.register(
'bookScale',
'怪物手册缩放',
100,
COM.Number,
[10, 500, 10]
)
.setDisplayFunc('bookScale', value => `${value}%`)
.register('danmaku', '显示弹幕', true, COM.Boolean)
.register('danmakuSpeed', '弹幕速度', 60, COM.Number, [10, 1000, 5])
.register('tips', '小贴士', true, COM.Boolean)
);
const loading = Mota.require('var', 'loading');
loading.once('coreInit', () => {
mainSetting.reset({
'screen.fullscreen': !!document.fullscreenElement,
'screen.halo': !!storage.getValue('screen.showHalo', true),
'screen.itemDetail': !!storage.getValue('screen.itemDetail', true),
'screen.heroDetail': !!storage.getValue('screen.heroDetail', false),
'screen.transition': !!storage.getValue('screen.transition', false),
'screen.fontSize': storage.getValue(
'screen.fontSize',
isMobile ? 9 : 16
),
'screen.smoothView': !!storage.getValue('screen.smoothView', true),
'screen.criticalGem': !!storage.getValue('screen.criticalGem', false),
'screen.fontSizeStatus': storage.getValue('screen.fontSizeStatus', 100),
'action.fixed': !!storage.getValue('action.fixed', true),
'audio.bgmEnabled': !!storage.getValue('audio.bgmEnabled', true),
'audio.bgmVolume': storage.getValue('audio.bgmVolume', 80),
'audio.soundEnabled': !!storage.getValue('audio.soundEnabled', true),
'audio.soundVolume': storage.getValue('audio.soundVolume', 80),
'utils.betterLoad': !!storage.getValue('utils.betterLoad', true),
'utils.autoScale': !!storage.getValue('utils.autoScale', true),
'fx.paraLight': !!storage.getValue('fx.paraLight', true),
'fx.frag': !!storage.getValue('fx.frag', true),
'fx.portalParticle': !!storage.getValue('fx.portalParticle', true),
'ui.mapScale': storage.getValue(
'ui.mapScale',
isMobile ? 300 : Math.floor(window.innerWidth / 600) * 50
),
'ui.mapLazy': storage.getValue('ui.mapLazy', false),
'ui.toolbarScale': storage.getValue(
'ui.toolbarScale',
isMobile ? 50 : Math.floor((window.innerWidth / 1700) * 10) * 10
),
'ui.bookScale': storage.getValue('ui.bookScale', isMobile ? 100 : 80),
'ui.danmaku': storage.getValue('ui.danmaku', true),
'ui.danmakuSpeed': storage.getValue(
'ui.danmakuSpeed',
Math.floor(window.innerWidth / 30) * 5
),
'ui.tips': storage.getValue('ui.tips', true)
});
});
interface SettingTextData {
[x: string]: string[] | SettingTextData;
}
function getSettingText(obj: SettingTextData, key?: string) {
for (const [k, value] of Object.entries(obj)) {
const setKey = key ? key + '.' + k : k;
if (value instanceof Array) {
mainSetting.setDescription(setKey, value.join('\n'));
} else {
getSettingText(value, setKey);
}
}
}
getSettingText(settingsText);
mainSetting
.setDescription('audio.bgmEnabled', `是否开启背景音乐`)
.setDescription('audio.bgmVolume', `背景音乐的音量`)
.setDescription('audio.soundEnabled', `是否开启音效`)
.setDescription('audio.soundVolume', `音效的音量`)
.setDescription('ui.mapScale', `楼传小地图的缩放,百分比格式`)
.setDescription(
'ui.mapLazy',
`是否启用小地图懒更新模式,此模式下剩余怪物数量不会实时更新而变成切换地图后更新,打开小地图时出现卡顿可以尝试开启此设置`
)
.setDescription('ui.toolbarScale', `自定义工具栏的缩放比例`)
.setDescription(
'ui.bookScale',
`怪物手册界面中每个怪物框体的高度缩放,最小值限定为 20% 屏幕高度`
)
.setDescription('ui.danmaku', '是否显示弹幕')
.setDescription('ui.danmakuSpeed', '弹幕速度,刷新或开关弹幕显示后起效')
.setDescription('ui.tips', `是否在游戏画面右上角常亮显示小贴士`)
.setDescription('screen.fontSizeStatus', `修改状态栏的字体大小`)
.setDescription(
'screen.blur',
'打开任意ui界面时是否有背景虚化效果移动端打开后可能会有掉帧或者发热现象。关闭ui后生效'
)
.setDescription(
'fx.portalParticle',
'是否启用苍蓝之殿的传送门粒子特效,启用后可能对性能及设备发热有所影响'
);
function setFontSize() {
const absoluteSize = storage.getValue(
'@@absoluteFontSize',
16 * devicePixelRatio
);
const size = Math.round(absoluteSize / devicePixelRatio);
mainSetting.setValue('screen.fontSize', size);
}
setFontSize();
window.addEventListener('resize', () => {
setFontSize();
});

View File

@ -0,0 +1,308 @@
import { FunctionalComponent, reactive } from 'vue';
import { EventEmitter } from '@motajs/legacy-common';
import { has } from './utils';
import { createSettingComponents } from './preset';
const COM = createSettingComponents();
export interface SettingComponentProps {
item: MotaSettingItem;
setting: MotaSetting;
displayer: SettingDisplayer;
}
export type SettingComponent = FunctionalComponent<SettingComponentProps>;
type MotaSettingType = boolean | number | MotaSetting;
export interface MotaSettingItem<T extends MotaSettingType = MotaSettingType> {
name: string;
key: string;
value: T;
controller: SettingComponent;
description?: string;
defaults?: boolean | number;
step?: [number, number, number];
display?: (value: T) => string;
}
interface SettingEvent {
valueChange: <T extends boolean | number>(
key: string,
newValue: T,
oldValue: T
) => void;
}
export type SettingText = {
[key: string]: string[] | SettingText;
};
export interface SettingDisplayInfo {
item: MotaSettingItem | null;
list: Record<string, MotaSettingItem>;
text: string[];
}
export class MotaSetting extends EventEmitter<SettingEvent> {
static noStorage: string[] = [];
readonly list: Record<string, MotaSettingItem> = {};
/**
*
* @param setting
*/
reset(setting: Record<string, boolean | number>) {
for (const [key, value] of Object.entries(setting)) {
this.setValue(key, value);
}
}
/**
*
* @param key
* @param value
*/
register(
key: string,
name: string,
value: number,
com?: SettingComponent,
step?: [number, number, number]
): this;
/**
*
* @param key
* @param value
*/
register(
key: string,
name: string,
value: boolean | MotaSetting,
com?: SettingComponent
): this;
register(
key: string,
name: string,
value: MotaSettingType,
com: SettingComponent = COM.Default,
step: [number, number, number] = [0, 100, 1]
) {
const setting: MotaSettingItem = {
name,
value,
key,
controller: com
};
if (!(value instanceof MotaSetting)) setting.defaults = value;
if (typeof value === 'number') setting.step = step;
this.list[key] = setting;
return this;
}
/**
*
* @param key
*/
getSetting(key: string): Readonly<MotaSettingItem | null> {
const list = key.split('.');
return this.getSettingBy(list);
}
/**
*
* @param key
* @param value
*/
setValue(key: string, value: boolean | number) {
const setting = this.getSettingBy(key.split('.'));
if (typeof setting.value !== typeof value) {
throw new Error(
`Setting type mismatch on setting '${key}'.` +
`Expected: ${typeof setting.value}. Recieve: ${typeof value}`
);
}
const old = setting.value as boolean | number;
setting.value = value;
this.emit('valueChange', key, value, old);
}
/**
*
* @param key
* @param value
*/
addValue(key: string, value: number) {
const setting = this.getSettingBy(key.split('.'));
if (typeof setting.value !== 'number') {
throw new Error(
`Cannot execute addValue method on a non-number setting.` +
`Type expected: number. See: ${typeof setting.value}`
);
}
const old = setting.value as boolean | number;
setting.value += value;
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
* @param func
*/
setDisplayFunc(key: string, func: (value: MotaSettingType) => string) {
const setting = this.getSettingBy(key.split('.'));
setting.display = func;
return this;
}
/**
*
* @param key
* @param com
*/
setValueController(key: string, com: SettingComponent) {
const setting = this.getSettingBy(key.split('.'));
setting.controller = com;
return this;
}
/**
*
* @param key id
* @param desc
*/
setDescription(key: string, desc: string) {
const setting = this.getSettingBy(key.split('.'));
setting.description = desc;
return this;
}
private getSettingBy(list: string[]) {
let now: MotaSetting = this;
for (let i = 0; i < list.length - 1; i++) {
const item = now.list[list[i]].value;
if (!(item instanceof MotaSetting)) {
throw new Error(
`Cannot get setting. The parent isn't a MotaSetting instance.` +
`Key: '${list.join('.')}'. Reading: '${list[i]}'`
);
}
now = item;
}
return now.list[list.at(-1)!] ?? null;
}
}
interface SettingDisplayerEvent {
update: (stack: string[], display: SettingDisplayInfo[]) => void;
}
export class SettingDisplayer extends EventEmitter<SettingDisplayerEvent> {
setting: MotaSetting;
/** 选项选中栈 */
selectStack: string[] = [];
displayInfo: SettingDisplayInfo[] = reactive([]);
constructor(setting: MotaSetting) {
super();
this.setting = setting;
this.update();
}
/**
*
* @param key
*/
add(key: string) {
this.selectStack.push(...key.split('.'));
this.update();
}
/**
*
* @param index
*/
cut(index: number, noUpdate: boolean = false) {
this.selectStack.splice(index, Infinity);
if (!noUpdate) this.update();
}
update() {
const list = this.selectStack;
let now = this.setting;
this.displayInfo = [];
for (let i = 0; i < list.length - 1; i++) {
const item = now.list[list[i]].value;
if (!(item instanceof MotaSetting)) {
throw new Error(
`Cannot get setting. The parent isn't a MotaSetting instance.` +
`Key: '${list.join('.')}'. Reading: '${list[i + 1]}'`
);
}
this.displayInfo.push({
item: now.list[list[i]],
text: [],
list: now.list
});
now = item;
}
const last = now.list[list.at(-1)!];
if (last) {
const desc = last.description;
const text = desc ? desc.split('\n') : ['请选择设置'];
this.displayInfo.push({
item: last,
text,
list: now.list
});
if (last.value instanceof MotaSetting) {
this.displayInfo.push({
item: null,
text: ['请选择设置'],
list: (last.value as MotaSetting).list
});
}
} else {
this.displayInfo.push({
item: null,
text: ['请选择设置'],
list: this.setting.list
});
}
this.emit('update', this.selectStack, this.displayInfo);
}
}

View File

@ -1,4 +1,4 @@
import { mainSetting } from '@/core/main/setting';
import { mainSetting } from '../preset/ui';
import { downloadCanvasImage, has, tip } from '../utils';
type BFSFromString = `${FloorIds},${number},${number},${Dir}`;

View File

@ -46,10 +46,10 @@ import BookDetail from './bookDetail.vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { ToShowEnemy, detailInfo } from '../tools/book';
import { getDetailedEnemy } from '../tools/fixed';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { mainUi } from '@/core/main/init/ui';
import { mainSetting } from '@/core/main/setting';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui';
import { mainSetting } from '../preset/ui';
import { isMobile } from '../use';
const props = defineProps<{

View File

@ -83,7 +83,7 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import EnemyCritical from '../panel/enemyCritical.vue';
import EnemyTarget from '../panel/enemyTarget.vue';
import { detailInfo } from '../tools/book';
import { gameKey } from '@/core/main/custom/hotkey';
import { gameKey } from '@motajs/system-action';
const props = defineProps<{
fromBook?: boolean;

View File

@ -9,8 +9,8 @@
import { Animation, hyper, sleep } from 'mutate-animate';
import { onMounted } from 'vue';
import { has } from '../utils';
import { GameUi } from '@/core/main/custom/ui';
import { fixedUi } from '@/core/main/init/ui';
import { GameUi } from '../controller';
import { fixedUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -23,8 +23,8 @@ import { computed, onMounted, ref } from 'vue';
import Box from '../components/box.vue';
import list from '../data/achievement.json';
import { AchievementType, getNowPoint, totalPoint } from '../tools/achievement';
import { GameUi } from '@/core/main/custom/ui';
import { fixedUi } from '@/core/main/init/ui';
import { GameUi } from '../controller';
import { fixedUi } from '../preset/ui';
const height = window.innerHeight;

View File

@ -28,9 +28,9 @@
<script lang="ts" setup>
import { nextTick, onUnmounted, reactive, watch } from 'vue';
import { Danmaku } from '@/core/main/custom/danmaku';
import { Danmaku } from '../danmaku';
import { LikeFilled } from '@ant-design/icons-vue';
import { mainSetting } from '@/core/main/setting';
import { mainSetting } from '../preset/ui';
import { debounce } from 'lodash-es';
interface ElementMap {

View File

@ -158,17 +158,17 @@ import {
SendOutlined,
UpOutlined
} from '@ant-design/icons-vue';
import { Danmaku } from '@/core/main/custom/danmaku';
import { GameUi } from '@/core/main/custom/ui';
import { Danmaku } from '../danmaku';
import { GameUi } from '../controller';
import { sleep } from 'mutate-animate';
import { fixedUi } from '@/core/main/init/ui';
import { fixedUi } from '../preset/ui';
import { calStringSize, tip } from '../utils';
import { gameKey } from '@/core/main/custom/hotkey';
import { gameKey } from '@motajs/system-action';
import { isNil } from 'lodash-es';
import { stringifyCSS, parseCss, getIconHeight } from '../utils';
import { logger, LogLevel } from '@motajs/common';
import Scroll from '@/components/scroll.vue';
import BoxAnimate from '@/components/boxAnimate.vue';
import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue';
const props = defineProps<{
num: number;

View File

@ -22,9 +22,9 @@ import { computed, onUnmounted, ref } from 'vue';
import desc from '../data/desc.json';
import { splitText } from '../utils';
import Colomn from '../components/colomn.vue';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { mainUi } from '@/core/main/init/ui';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -186,10 +186,10 @@ import BoxAnimate from '../components/boxAnimate.vue';
import { has, tip, type } from '../utils';
import { cancelGlobalDrag, isMobile, useDrag } from '../use';
import { hyper } from 'mutate-animate';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { getStatusLabel } from '../utils';
import { mainUi } from '@/core/main/init/ui';
import { mainUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -31,7 +31,7 @@
<script lang="ts" setup>
import { onMounted, onUpdated, Ref, ref, watch } from 'vue';
import Box from '../components/box.vue';
import { GameUi } from '@/core/main/custom/ui';
import { GameUi } from '../controller';
import type { DamageEnemy, EnemyInfo } from '@/game/enemy/damage';
import { nextFrame } from '../utils';

View File

@ -12,9 +12,9 @@
import { getDetailedEnemy } from '../tools/fixed';
import BookDetail from './bookDetail.vue';
import { detailInfo } from '../tools/book';
import { hovered } from '@/core/main/init/fixed';
import { GameUi } from '@/core/main/custom/ui';
import { mainUi } from '@/core/main/init/ui';
import { hovered } from '../preset/fixed';
import { GameUi } from '../controller';
import { mainUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -99,12 +99,12 @@ import {
} from '@ant-design/icons-vue';
import { debounce } from 'lodash-es';
import { tip } from '../utils';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { createChangable } from '../tools/common';
import { mainUi } from '@/core/main/init/ui';
import { mainSetting } from '@/core/main/setting';
import { GameStorage } from '@/core/main/storage';
import { mainUi } from '../preset/ui';
import { mainSetting } from '../preset/ui';
import { GameStorage } from '@motajs/legacy-system';
const props = defineProps<{
num: number;

View File

@ -36,15 +36,15 @@
</template>
<script lang="ts" setup>
import { Hotkey } from '@/core/main/custom/hotkey';
import { GameUi } from '@/core/main/custom/ui';
import { Hotkey } from '@motajs/system-action';
import { GameUi } from '../controller';
import Column from '../components/colomn.vue';
import { mainUi } from '@/core/main/init/ui';
import { mainUi } from '../preset/ui';
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue';
import { KeyCode, KeyCodeUtils } from '@motajs/client-base';
import { generateBinary, keycode } from '../utils';
import { cloneDeep } from 'lodash-es';
import { gameKey } from '@/core/main/custom/hotkey';
import { gameKey } from '@motajs/system-action';
interface HotkeyKeys {
index: number;

View File

@ -31,10 +31,10 @@ import {
loadDefaultResource,
LoadTask
} from '@motajs/legacy-common';
import { GameUi } from '@/core/main/custom/ui';
import { GameUi } from '../controller';
import { formatSize } from '../utils';
import { logger } from '@motajs/common';
import { fixedUi } from '@/core/main/init/ui';
import { fixedUi } from '../preset/ui';
import { sleep } from 'mutate-animate';
const props = defineProps<{

View File

@ -56,8 +56,8 @@ import { MarkInfo, unmarkEnemy } from '../mark';
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';
import { GameUi } from '../controller';
import { fixedUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -73,20 +73,20 @@
<script lang="ts" setup>
import { computed, onUnmounted, ref, shallowRef } from 'vue';
import { mainSetting } from '../preset/ui';
import {
mainSetting,
MotaSetting,
MotaSettingItem,
SettingDisplayer,
SettingDisplayInfo
} from '@/core/main/setting';
} from '../setting';
import { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';
import { splitText } from '../utils';
import Scroll from '../components/scroll.vue';
import { isMobile } from '../use';
import { gameKey } from '@/core/main/custom/hotkey';
import { GameUi } from '@/core/main/custom/ui';
import { mainUi } from '@/core/main/init/ui';
import { gameKey } from '@motajs/system-action';
import { GameUi } from '../controller';
import { mainUi } from '../preset/ui';
const props = defineProps<{
info?: MotaSetting;

View File

@ -172,9 +172,9 @@ import {
import { splitText, tip } from '../utils';
import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { mainUi } from '@/core/main/init/ui';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -21,7 +21,7 @@ import { computed, ref } from 'vue';
import skills from '../data/skill.json';
import { has } from '../utils';
import Column from '../components/colomn.vue';
import { mainUi } from '@/core/main/init/ui';
import { mainUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -84,9 +84,9 @@ import Scroll from '../components/scroll.vue';
import { has, splitText, tip } from '../utils';
import { isMobile } from '../use';
import { sleep } from 'mutate-animate';
import { gameKey } from '@/core/main/custom/hotkey';
import { GameUi } from '@/core/main/custom/ui';
import { mainUi } from '@/core/main/init/ui';
import { gameKey } from '@motajs/system-action';
import { GameUi } from '../controller';
import { mainUi } from '../preset/ui';
import type { Chapter } from '@/plugin/game/skillTree';
const props = defineProps<{

View File

@ -67,11 +67,10 @@ import { sleep } from 'mutate-animate';
import { doByInterval } from '../utils';
import { triggerFullscreen } from '../utils';
import { isMobile } from '../use';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { mainUi } from '@/core/main/init/ui';
import { CustomToolbar } from '@/core/main/custom/toolbar';
import { mainSetting } from '@/core/main/setting';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui';
import { mainSetting } from '../preset/ui';
import { mat4 } from 'gl-matrix';
// todo:
import { bgmController } from '@/module';
@ -323,7 +322,6 @@ onMounted(async () => {
main = document.getElementById('start-main') as HTMLDivElement;
start = document.getElementById('start') as HTMLDivElement;
background = document.getElementById('background') as HTMLImageElement;
CustomToolbar.closeAll();
window.addEventListener('resize', resize);
resize();

View File

@ -1,586 +0,0 @@
<template>
<div id="tools">
<span class="button-text" @click="exit"><left-outlined /> 返回</span>
</div>
<div id="tool-editor">
<div id="tool-left">
<Scroll class="tool-list-scroll">
<div id="tool-list">
<div
v-for="(item, i) of list"
class="tool-list-item selectable"
:selected="i === selected"
@click="selected = i"
>
<span>{{ item.id }}</span>
<a-button
type="danger"
class="tool-list-delete"
@click.stop="deleteTool(item.id)"
>删除</a-button
>
</div>
<div id="tool-list-add">
<div
id="tool-add-div"
@click="addingTool = true"
v-if="!addingTool"
>
<PlusOutlined></PlusOutlined>&nbsp;&nbsp;
<span>新增工具栏</span>
</div>
<div v-else>
<a-input
style="
height: 100%;
font-size: 80%;
width: 100%;
"
v-model:value="addingToolId"
@blur="addTool"
></a-input>
</div>
</div>
</div>
</Scroll>
<a-divider
type="vertical"
dashed
v-if="isMobile"
style="height: 100%; border-color: #ddd4"
></a-divider>
<div id="tool-preview" v-if="!!bar && isMobile">
<div id="tool-preview-container">
<div class="tool-preview-item" v-for="item of bar.items">
<component
:is="CustomToolbar.info[item.type].show as any"
:item="item"
:toolbar="bar"
></component>
</div>
</div>
</div>
</div>
<a-divider
class="divider"
dashed
style="border-color: #ddd4"
:type="isMobile ? 'horizontal' : 'vertical'"
></a-divider>
<div id="tool-info">
<div id="tool-detail" v-if="!!bar">
<Scroll class="tool-item-list-scroll">
<div class="tool-item-list">
<div
class="tool-item-list-item"
v-for="item of bar.items"
>
<div
class="tool-item-header"
:folded="!unfolded[item.id]"
@click="triggerFold(item.id)"
>
<div
style="
display: flex;
flex-direction: row;
width: 100%;
"
>
<span
class="tool-fold"
:folded="!unfolded[item.id]"
>
<RightOutlined></RightOutlined>
</span>
<span class="tool-name">
<span
v-if="editId !== item.id"
style="cursor: text"
@click.stop="editName(item.id)"
>
{{ item.id }}
</span>
<span v-else>
<a-input
@blur="editNameSuccess(item.id)"
@click.stop=""
v-model:value="editValue"
class="tool-name-edit"
></a-input>
</span>
</span>
</div>
<a-button
type="danger"
class="tool-item-delete"
@click.stop="deleteItem(item)"
>删除</a-button
>
</div>
<div v-if="!!unfolded[item.id]">
<component
:is="CustomToolbar.info[item.type].editor"
:item="item"
:toolbar="bar"
></component>
</div>
</div>
<div id="tool-item-add">
<div
id="tool-item-add-div"
v-if="!addingItem"
@click="addingItem = true"
>
<PlusOutlined></PlusOutlined>&nbsp;&nbsp;
<span>新增工具</span>
</div>
<div id="tool-item-add-type" v-else>
<span>工具类型</span>
<a-select
v-model:value="addingType"
style="
width: 120px;
height: 100%;
font-size: 80%;
background-color: #222;
"
>
<a-select-option
v-for="(
info, type
) of CustomToolbar.info"
:value="type"
>{{ info.name }}</a-select-option
>
</a-select>
<a-button
type="primary"
style="font-size: 80%; height: 100%"
@click="addItem(true)"
>确定</a-button
>
<a-button
@click="addItem(false)"
style="
background-color: #222;
font-size: 80%;
height: 100%;
"
>取消</a-button
>
</div>
</div>
</div>
</Scroll>
</div>
<a-divider dashed v-if="!isMobile"></a-divider>
<div id="tool-preview" v-if="!!bar && !isMobile">
<div id="tool-preview-container">
<div class="tool-preview-item" v-for="item of bar.items">
<component
:is="CustomToolbar.info[item.type].show as any"
:item="item"
:toolbar="bar"
></component>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { CustomToolbar } from '@/core/main/custom/toolbar';
import { GameUi } from '@/core/main/custom/ui';
import { computed, onUnmounted, reactive, ref } from 'vue';
import {
PlusOutlined,
RightOutlined,
LeftOutlined
} from '@ant-design/icons-vue';
import { mainUi } from '@/core/main/init/ui';
import { isMobile } from '../use';
import Scroll from '../components/scroll.vue';
import { deleteWith, tip } from '../utils';
import { Modal } from 'ant-design-vue';
import { mainSetting } from '@/core/main/setting';
import { ToolbarItemType } from '@/core/main/init/toolbar';
const props = defineProps<{
ui: GameUi;
num: number;
}>();
const scale = mainSetting.getValue('ui.toolbarScale', 100) / 100;
const list = CustomToolbar.list;
const selected = ref(0);
const bar = computed(() => list[selected.value]);
const unfolded = reactive<Record<string, boolean>>({});
const editId = ref<string>();
const editValue = ref<string>();
//
const addingItem = ref(false);
const addingType = ref<ToolbarItemType>('item');
//
const addingTool = ref(false);
const addingToolId = ref('');
/**
* 编辑名称
*/
function editName(id: string) {
editId.value = id;
editValue.value = id;
}
/**
* 编辑名称完成
*/
function editNameSuccess(id: string) {
if (bar.value.items.some(v => v.id === editId.value)) {
tip('error', '名称重复!');
} else {
const item = bar.value.items.find(v => v.id === id)!;
item.id = editValue.value!;
}
editId.value = void 0;
editValue.value = void 0;
}
/**
* 删除自定义工具
*/
function deleteItem(item: any) {
Modal.confirm({
title: '确定要删除这个自定义工具吗?',
onOk() {
deleteWith(bar.value.items, item);
},
onCancel() {}
});
}
function addItem(add: boolean) {
if (add) {
bar.value.add(
// @ts-ignore
CustomToolbar.info[addingType.value].onCreate({
id: `tool-item-${bar.value.items.length}`,
type: addingType.value
})
);
}
addingItem.value = false;
addingType.value = 'item';
}
/**
* 更改折叠
*/
function triggerFold(id: string) {
unfolded[id] = !unfolded[id];
}
function exit() {
mainUi.close(props.num);
}
function deleteTool(id: string) {
Modal.confirm({
title: '确定要删除这个自定义工具栏吗?',
onOk() {
const index = CustomToolbar.list.findIndex(v => v.id === id);
if (index !== -1) {
CustomToolbar.list[index].closeAll();
CustomToolbar.list.splice(index, 1);
}
selected.value = 0;
},
onCancel() {}
});
}
function addTool() {
if (addingToolId.value === '') {
addingToolId.value = '';
addingTool.value = false;
return;
}
if (CustomToolbar.list.some(v => v.id === addingToolId.value)) {
tip('error', '工具栏名称重复!');
return;
} else {
const bar = new CustomToolbar(addingToolId.value);
}
addingToolId.value = '';
addingTool.value = false;
}
onUnmounted(() => {
CustomToolbar.save();
});
</script>
<style lang="less" scoped>
#tools {
width: 100%;
font-family: 'normal';
font-size: 3.2vh;
height: 5vh;
position: fixed;
left: 10vw;
top: 5vh;
}
#tool-editor {
width: 70%;
height: 70%;
display: flex;
justify-content: center;
align-items: center;
font-family: 'normal';
font-size: 150%;
user-select: none;
align-self: center;
}
.tool-list-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.tool-list-scroll {
height: 100%;
width: 100%;
}
#tool-left {
flex-basis: 30%;
display: flex;
height: 100%;
}
#tool-list {
display: flex;
flex-direction: column;
height: 100%;
}
#tool-list-add {
margin-top: 5%;
padding: 1% 3% 1% 3%;
#tool-add-div {
display: flex;
align-items: center;
flex-direction: row;
cursor: pointer;
padding: 1% 4%;
transition: background-color linear 0.1s;
border-radius: 5px;
text-overflow: ellipsis;
}
#tool-add-div:hover {
background-color: rgba(39, 251, 209, 0.316);
}
#tool-add-div:active {
background-color: rgba(39, 251, 209, 0.202);
}
}
.tool-list-delete {
font-size: 80%;
display: flex;
align-items: center;
}
#tool-info {
display: flex;
flex-direction: column;
height: 100%;
width: 70%;
}
#tool-detail {
height: 60%;
.tool-item-header {
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
}
.tool-item-header[folded='false'] {
border-bottom: 0.5px solid #888;
}
.tool-item-list-scroll {
height: 100%;
width: 100%;
}
.tool-item-list {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.tool-item-list-item {
border: 1px solid #ddd8;
margin-bottom: 3%;
background-color: #222;
padding-left: 2%;
}
.tool-fold {
::v-deep(span) {
transition: all 0.2s ease;
}
}
.tool-fold[folded='false'] {
::v-deep(span) {
transform: rotate(90deg);
}
}
.tool-name {
margin-left: 3%;
width: 40%;
display: flex;
.tool-name-edit {
width: 100%;
font-size: 100%;
height: 100%;
background-color: #000;
}
}
.tool-item-delete {
height: 100%;
justify-self: end;
font-size: 80%;
padding: 2px 15px;
}
#tool-item-add-div {
padding: 1% 3% 1% 3%;
display: flex;
align-items: center;
flex-direction: row;
cursor: pointer;
padding: 1% 4%;
transition: background-color linear 0.1s;
border-radius: 5px;
text-overflow: ellipsis;
}
#tool-item-add-div:hover {
background-color: rgba(39, 251, 209, 0.316);
}
#tool-item-add-div:active {
background-color: rgba(39, 251, 209, 0.202);
}
#tool-item-add-type {
display: flex;
align-items: center;
flex-direction: row;
padding: 1% 4%;
justify-content: space-between;
border-radius: 5px;
background-color: rgba(39, 251, 209, 0.316);
}
}
#tool-preview {
height: 40%;
display: flex;
justify-content: center;
align-items: flex-start;
#tool-preview-container {
width: 90%;
display: flex;
flex-direction: row;
border: 2px solid #ddd9;
background-color: #0009;
flex-wrap: wrap;
}
.tool-preview-item {
display: flex;
margin: v-bind('5 * scale + "px"');
min-width: v-bind('50 * scale + "px"');
min-height: v-bind('50 * scale + "px"');
background-color: #222;
border: 1.5px solid #ddd8;
justify-content: center;
align-items: center;
transition: all 0.1s linear;
::v-deep(*) {
pointer-events: none;
}
}
}
.divider {
height: 100%;
}
@media screen and (max-width: 600px) {
#tool-editor {
padding-top: 15%;
flex-direction: column;
width: 100%;
height: 100%;
}
#tool-left {
width: 100%;
flex-basis: 40%;
max-height: 40vh;
display: flex;
flex-direction: row;
.tool-list-scroll {
height: 100%;
flex-basis: 50%;
}
#tool-preview {
flex-basis: 50%;
height: 100%;
}
}
#tool-info {
width: 100%;
flex-basis: 60%;
#tool-detail {
height: 100%;
}
}
.divider {
height: auto;
width: 100%;
}
}
</style>

View File

@ -1,138 +0,0 @@
<template>
<Box
class="toolbar-container"
:dragable="true"
:resizable="true"
v-model:left="box.x"
v-model:top="box.y"
v-model:height="box.height"
v-model:width="box.width"
:key="num"
>
<div class="toolbar">
<div
class="toolbar-item"
v-for="item of bar.items"
@click.stop="click"
:noaction="!!item.noDefaultAction"
>
<component
:is="CustomToolbar.info[item.type].show as any"
:item="item"
:toolbar="bar"
></component>
</div>
</div>
</Box>
</template>
<script lang="ts" setup>
import Box from '../components/box.vue';
import { CustomToolbar } from '@/core/main/custom/toolbar';
import { GameUi } from '@/core/main/custom/ui';
import { mainSetting } from '@/core/main/setting';
import { onUnmounted, reactive, watch } from 'vue';
interface BoxData {
x: number;
y: number;
width: number;
height: number;
}
const props = defineProps<{
num: number;
ui: GameUi;
bar: CustomToolbar;
}>();
const bar = props.bar;
const scale = mainSetting.getValue('ui.toolbarScale', 100) / 100;
const box = reactive<BoxData>({
x: bar.x,
y: bar.y,
width: bar.width,
height: bar.height
});
watch(box, ({ x, y, width, height }) => {
bar.x = x;
bar.y = y;
bar.width = width;
bar.height = height;
});
function click() {
// pass
}
function posChange() {
box.x = bar.x;
box.y = bar.y;
box.width = bar.width;
box.height = bar.height;
}
bar.on('posChange', posChange);
onUnmounted(() => {
bar.off('posChange', posChange);
});
</script>
<style lang="less" scoped>
.toolbar-container {
background-color: #0009;
padding: v-bind('scale * 5 + "px"');
}
.toolbar {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
font-size: 150%;
}
.toolbar-item {
display: flex;
margin: v-bind('scale * 5 + "px"');
min-width: v-bind('scale * 50 + "px"');
min-height: v-bind('scale * 50 + "px"');
background-color: #222;
border: 1.5px solid #ddd8;
justify-content: center;
align-items: center;
transition: all 0.1s linear;
}
.toolbar-item[noaction='false'] {
cursor: pointer;
}
.toolbar-item::v-deep(> *) {
height: 100%;
min-width: v-bind('scale * 50 + "px"');
display: flex;
justify-content: center;
align-items: center;
text-align: center;
text-overflow: clip;
text-wrap: nowrap;
overflow: hidden;
}
.toolbar-item::v-deep(.button-text)[active='true'] {
color: gold;
background-color: #555;
}
.toolbar-item:hover[noaction='false'] {
background-color: #444;
}
.toolbar-item:active[noaction='false'] {
background-color: #333;
}
</style>

View File

@ -116,9 +116,9 @@ import { isMobile } from '../use';
import { type, has } from '../utils';
import { hyper } from 'mutate-animate';
import { message } from 'ant-design-vue';
import { GameUi } from '@/core/main/custom/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { mainUi } from '@/core/main/init/ui';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui';
const props = defineProps<{
num: number;

View File

@ -6,9 +6,8 @@ import { Ref, ref } from 'vue';
import { EVENT_KEY_CODE_MAP, KeyCode } from '@motajs/client-base';
import axios from 'axios';
import { decompressFromBase64 } from 'lz-string';
import { Keyboard, KeyboardEmits } from '@/core/main/custom/keyboard';
import { fixedUi, mainUi } from '@/core/main/init/ui';
import { isAssist } from '@/core/main/custom/hotkey';
import { Keyboard, KeyboardEmits, isAssist } from '@motajs/system-action';
import { fixedUi, mainUi } from './preset/ui';
import { logger } from '@motajs/common';
type CanParseCss = keyof {

View File

@ -4,6 +4,7 @@
"@motajs/common": "workspace:*",
"@motajs/render-core": "workspace:*",
"@motajs/render-elements": "workspace:*",
"@motajs/render-style": "workspace:*"
"@motajs/render-style": "workspace:*",
"@motajs/system-action": "workspace:*"
}
}

View File

@ -1,7 +1,6 @@
import { Animation, Ticker, Transition } from 'mutate-animate';
import { ERenderItemEvent, RenderItem } from '@motajs/render-core';
// todo: 改为 monorepo
import { gameKey, Hotkey } from '@/core/main/custom/hotkey';
import { gameKey, Hotkey } from '@motajs/system-action';
import { onMounted, onUnmounted } from 'vue';
import EventEmitter from 'eventemitter3';

View File

@ -0,0 +1,6 @@
{
"name": "@motajs/system-action",
"dependencies": {
"@motajs/common": "workspace:*"
}
}

View File

@ -0,0 +1,2 @@
export * from './hotkey';
export * from './keyboard';

View File

@ -105,10 +105,10 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
this.scopeStack.push(symbol);
const ev: Listener<VirtualKeyEmitFn>[] = [];
this.onEmitKey[symbol] = ev;
// @ts-ignore
// @ts-expect-error 无法推导
this.events = ev;
this.emit('scopeCreate', symbol);
if (!!last) {
if (last) {
this.scopeAssist[symbol] = this.assist;
}
this.assist = 0;
@ -133,7 +133,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
if (!symbol) return;
this.scope = symbol;
this.assist = this.scopeAssist[symbol];
// @ts-ignore
// @ts-expect-error 无法推导
this.events = this.onEmitKey[symbol];
}
@ -189,7 +189,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
}
}
export function generateKeyboardEvent(key: KeyCode, assist: number) {
export function generateKeyboardEvent(_key: KeyCode, assist: number) {
const { ctrl, alt, shift } = unwarpBinary(assist);
const ev = new KeyboardEvent('keyup', {
ctrlKey: ctrl,

View File

@ -1,6 +1,7 @@
{
"name": "@motajs/system",
"dependencies": {
"@motajs/system-ui": "workspace:*"
"@motajs/system-ui": "workspace:*",
"@motajs/system-action": "workspace:*"
}
}

View File

@ -1 +1,2 @@
export * as Action from '@motajs/system-action';
export * as UI from '@motajs/system-ui';

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
import { mainUi, fixedUi } from './core/main/init/ui';
import { mainUi, fixedUi } from '@motajs/legacy-ui';
onMounted(() => {
const { hook } = Mota.requireAll('var');

View File

@ -1,35 +1,12 @@
// todo: 这个引入不加会报错,应该是循环引用导致的
import '@/plugin/utils';
import { Focus, GameUi, UiController } from './main/custom/ui';
import { GameStorage } from './main/storage';
import * as LegacyUI from '@motajs/legacy-ui';
import * as LegacySystem from '@motajs/legacy-system';
import './main/init/';
import './main/custom/toolbar';
import { fixedUi, mainUi } from './main/init/ui';
import {
MotaSetting,
SettingDisplayer,
mainSetting,
settingStorage
} from './main/setting';
import { KeyCode } from '@motajs/client-base';
import '@/plugin';
import './package';
import { CustomToolbar } from './main/custom/toolbar';
import {
Hotkey,
checkAssist,
isAssist,
unwarpBinary,
gameKey
} from './main/custom/hotkey';
import { Keyboard, generateKeyboardEvent } from './main/custom/keyboard';
import './main/layout';
import { createSettingComponents } from './main/init/settings';
import {
createToolbarComponents,
createToolbarEditorComponents
} from './main/init/toolbar';
import { VirtualKey } from './main/init/misc';
import * as System from '@motajs/system';
import { UI } from '@motajs/legacy-ui';
import Box from '@/components/box.vue';
import BoxAnimate from '@/components/boxAnimate.vue';
@ -41,47 +18,20 @@ import EnemySpecial from '@/panel/enemySpecial.vue';
import EnemyTarget from '@/panel/enemyTarget.vue';
import KeyboardPanel from '@/panel/keyboard.vue';
import { logger } from '@motajs/common';
import { Danmaku } from './main/custom/danmaku';
import * as Shadow from './fx/shadow';
import { Render } from '@motajs/client';
import { HeroKeyMover } from './main/action/move';
import { HeroKeyMover } from '../module/action/move';
import * as Animation from 'mutate-animate';
import '@/module';
// ----- 类注册
Mota.register('class', 'CustomToolbar', CustomToolbar);
Mota.register('class', 'Focus', Focus);
Mota.register('class', 'GameStorage', GameStorage);
Mota.register('class', 'GameUi', GameUi);
Mota.register('class', 'Hotkey', Hotkey);
Mota.register('class', 'Keyboard', Keyboard);
Mota.register('class', 'MotaSetting', MotaSetting);
Mota.register('class', 'SettingDisplayer', SettingDisplayer);
Mota.register('class', 'UiController', UiController);
Mota.register('class', 'Danmaku', Danmaku);
// ----- 函数注册
Mota.register('fn', 'unwrapBinary', unwarpBinary);
Mota.register('fn', 'checkAssist', checkAssist);
Mota.register('fn', 'isAssist', isAssist);
Mota.register('fn', 'generateKeyboardEvent', generateKeyboardEvent);
// ----- 变量注册
Mota.register('var', 'mainUi', mainUi);
Mota.register('var', 'fixedUi', fixedUi);
Mota.register('var', 'gameKey', gameKey);
Mota.register('var', 'mainSetting', mainSetting);
Mota.register('var', 'KeyCode', KeyCode);
Mota.register('var', 'settingStorage', settingStorage);
Mota.register('var', 'status', status);
Mota.register('var', 'logger', logger);
// ----- 模块注册
Mota.register('module', 'CustomComponents', {
createSettingComponents,
createToolbarComponents,
createToolbarEditorComponents
});
Mota.register('module', 'MiscComponents', {
VirtualKey
});
Mota.register('module', 'LegacyUI', LegacyUI);
Mota.register('module', 'System', System);
Mota.register('module', 'LegacySystem', LegacySystem);
Mota.register('module', 'RenderUtils', utils);
Mota.register('module', 'UI', UI);
Mota.register('module', 'UIComponents', {

View File

@ -1,21 +0,0 @@
export interface Undoable<T> {
stack: T[];
redoStack: T[];
/**
*
*/
undo(): T | undefined;
/**
*
*/
redo(): T | undefined;
}
export interface ResponseBase {
code: number;
message: string;
}
export type CSSObj = Partial<Record<CanParseCss, string>>;

View File

@ -1,386 +0,0 @@
import { EventEmitter } from '@motajs/legacy-common';
import { deleteWith, has } from '@motajs/legacy-ui';
import { Component, nextTick, reactive, shallowReactive } from 'vue';
import { fixedUi } from '../init/ui';
import { GameStorage } from '../storage';
import type {
CustomToolbarComponent,
MiscToolbar,
SettableItemData,
ToolbarItemBase,
ToolbarItemMap,
ToolbarItemType
} from '../init/toolbar';
interface CustomToolbarEvent {
add: (item: ValueOf<ToolbarItemMap>) => void;
delete: (item: ValueOf<ToolbarItemMap>) => void;
set: (id: string, data: Partial<SettableItemData>) => void;
emit: (id: string, item: ValueOf<ToolbarItemMap>) => void;
posChange: (bar: CustomToolbar) => void;
}
interface ToolbarSaveData {
x: number;
y: number;
w: number;
h: number;
items: ValueOf<ToolbarItemMap>[];
}
type ToolItemEmitFn<T extends ToolbarItemType> = (
this: CustomToolbar,
id: string,
item: ToolbarItemMap[T]
) => boolean;
interface RegisteredCustomToolInfo {
name: string;
onEmit: ToolItemEmitFn<ToolbarItemType>;
show: CustomToolbarComponent;
editor: CustomToolbarComponent;
onCreate: (item: any) => ToolbarItemBase<ToolbarItemType>;
}
type MiscEmitFn = (
id: string,
toolbar: CustomToolbar,
item: MiscToolbar
) => void;
type ActivedFn = (info: MiscInfo) => boolean;
interface MiscInfo {
id: string;
name: string;
emit: MiscEmitFn;
display: () => Component;
activable?: boolean;
actived?: ActivedFn;
}
interface Misc {
info: Record<string, MiscInfo>;
/**
*
* @param id id
* @param name
* @param emit
* @param display
* @param activable
*/
register(
this: Misc,
id: string,
name: string,
emit: MiscEmitFn,
display: () => Component
): void;
/**
*
* @param id id
* @param activable
* @param actived
*/
bindActivable(id: string, activable: boolean, actived?: ActivedFn): void;
/**
*
* @param id dragdrag杂项工具的工具栏
*/
requestRefresh(id?: string): void;
}
const toolbarStorage = new GameStorage<Record<string, ToolbarSaveData>>(
GameStorage.fromAuthor('AncTe', 'toolbar')
);
const misc: Misc = {
info: {},
register(id, name, emit, display) {
this.info[id] = { id, name, emit, display };
},
bindActivable(id, activable, actived) {
this.info[id].activable = activable;
this.info[id].actived = actived;
},
requestRefresh(id) {
if (id) {
CustomToolbar.list.forEach(v => {
if (
v.items.some(v => {
return v.type === 'misc' && v.items?.includes(id);
})
) {
v.refresh();
}
});
} else {
CustomToolbar.list.forEach(v => {
if (v.items.some(v => v.type === 'misc')) {
v.refresh();
}
});
}
}
};
export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
static num: number = 0;
static list: CustomToolbar[] = shallowReactive([]);
static info: Record<string, RegisteredCustomToolInfo> = {};
static misc: Misc = misc;
items: ValueOf<ToolbarItemMap>[] = reactive([]);
num: number = CustomToolbar.num++;
id: string;
// ----- size
x: number = 300;
y: number = 300;
width: number = 300;
height: number = 70;
// ----- other
assistKey: number = 0;
showIds: number[] = [];
constructor(id: string, noshow: boolean = false) {
super();
this.id = id;
// 按比例设置初始大小
const setting = Mota.require('var', 'mainSetting');
const scale = setting.getValue('ui.toolbarScale', 100) / 100;
this.width *= scale;
this.height *= scale;
this.x *= scale;
this.y *= scale;
if (!noshow) this.show();
CustomToolbar.list.push(this);
}
/**
*
* @param item
*/
add<K extends ToolbarItemType>(item: ToolbarItemMap[K]) {
const index = this.items.findIndex(v => v.id === item.id);
if (index !== -1) {
console.warn(`添加了id重复的自定义工具已将其覆盖`);
this.items[index] = item;
} else {
this.items.push(item);
}
this.emit('add', item);
return this;
}
/**
*
* @param id id
*/
delete(id: string) {
const index = this.items.findIndex(v => v.id === id);
if (index === -1) return;
const item = this.items[index];
this.items.splice(index, 1);
this.emit('delete', item);
return this;
}
/**
*
* @param id id
* @param item
*/
set<T extends ToolbarItemType>(
id: string,
item: Partial<SettableItemData<T>>
) {
const toSet = this.items.find(v => v.id === id);
if (!toSet) return;
Object.assign(toSet, item);
this.emit('set', id, item);
return this;
}
/**
*
* @param id id
*/
emitTool(id: string) {
const item = this.items.find(v => v.id === id);
if (!item) return this;
this.emit('emit', id, item);
const info = CustomToolbar.info[item.type];
if (!info) {
console.warn(`触发了未知的自定义工具类型:'${item.type}'`);
return this;
}
const success = info.onEmit.call(this, id, item);
if (!success) {
console.warn(`触发自定义工具失败id:'${id}',type:${item.type}`);
}
return this;
}
/**
*
*/
refresh(reopen: boolean = false) {
if (reopen && this.showIds.length > 0) {
this.closeAll();
nextTick(() => {
this.show();
});
} else {
const items = this.items.splice(0);
nextTick(() => {
this.items.push(...items);
});
}
return this;
}
setPos(x?: number, y?: number) {
has(x) && (this.x = x);
has(y) && (this.y = y);
this.emit('posChange', this);
}
setSize(width?: number, height?: number) {
has(width) && (this.width = width);
has(height) && (this.height = height);
this.emit('posChange', this);
}
/**
*
* @param multi
*/
show(multi: boolean = false) {
if (
!multi &&
this.showIds.some(v => fixedUi.stack.some(vv => vv.num === v))
) {
return -1;
}
const id = fixedUi.open('toolbar', { bar: this });
this.showIds.push(id);
return id;
}
/**
*
* @param id id
*/
close(id: number) {
fixedUi.close(id);
deleteWith(this.showIds, id);
}
/**
*
*/
closeAll() {
this.showIds.forEach(v => fixedUi.close(v));
this.showIds = [];
}
static get(id: string) {
return this.list.find(v => v.id === id);
}
/**
*
* @param type
* @param name
* @param onEmit
* @param show
* @param editor
* @param onCreate
*/
static register<K extends ToolbarItemType>(
type: K,
name: string,
onEmit: ToolItemEmitFn<K>,
show: CustomToolbarComponent<K>,
editor: CustomToolbarComponent<K>,
onCreate: (item: any) => ToolbarItemMap[K]
) {
if (type in this.info) {
console.warn(`已存在名为'${type}'的自定义工具类型,已将其覆盖!`);
}
const info: RegisteredCustomToolInfo = {
name,
onEmit: onEmit as ToolItemEmitFn<ToolbarItemType>,
show: show as CustomToolbarComponent,
editor: editor as CustomToolbarComponent,
// @ts-ignore
onCreate
};
this.info[type] = info;
}
static save() {
toolbarStorage.clear();
const setting = Mota.require('var', 'mainSetting');
const scale = setting.getValue('ui.toolbarScale', 100) / 100;
this.list.forEach(v => {
const toSave: ToolbarSaveData = {
x: v.x,
y: v.y,
w: v.width / scale,
h: v.height / scale,
items: []
};
v.items.forEach(v => {
toSave.items.push(v);
});
toolbarStorage.setValue(v.id, toSave);
});
toolbarStorage.write();
}
static load() {
toolbarStorage.read();
for (const [key, value] of Object.entries(toolbarStorage.data)) {
const bar = this.get(key) ?? new CustomToolbar(key);
bar.x = value!.x;
bar.y = value!.y;
bar.width = value!.w;
bar.height = value!.h;
for (const item of value!.items) {
bar.add(item);
}
}
}
static refreshAll(reopen: boolean = false): void {
CustomToolbar.list.forEach(v => v.refresh(reopen));
}
static showAll(): number[] {
return CustomToolbar.list.map(v => v.show());
}
static closeAll() {
this.list.forEach(v => v.closeAll());
}
}
Mota.require('var', 'loading').once('coreInit', () => {
CustomToolbar.load();
CustomToolbar.closeAll();
window.addEventListener('beforeunload', e => {
CustomToolbar.save();
});
window.addEventListener('blur', () => {
CustomToolbar.save();
});
});
Mota.require('var', 'hook').on('reset', () => {
CustomToolbar.showAll();
});

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,74 +0,0 @@
import { UI } from '@motajs/legacy-ui';
import * as MiscUI from './misc';
import { GameUi, UiController } from '../custom/ui';
import { mainSetting } from '../setting';
export const mainUi = new UiController();
mainUi.register(
new GameUi('book', UI.Book),
new GameUi('toolbox', UI.Toolbox),
new GameUi('equipbox', UI.Equipbox),
new GameUi('settings', UI.Settings),
new GameUi('desc', UI.Desc),
new GameUi('skill', UI.Skill),
new GameUi('skillTree', UI.SkillTree),
new GameUi('fly', UI.Fly),
new GameUi('fixedDetail', UI.FixedDetail),
new GameUi('shop', UI.Shop),
new GameUi('achievement', UI.Achievement),
new GameUi('bgm', UI.BgmList),
new GameUi('hotkey', UI.Hotkey),
new GameUi('toolEditor', UI.ToolEditor),
new GameUi('virtualKey', MiscUI.VirtualKey)
// todo: 把游戏主 div 加入到 mainUi 里面
);
mainUi.showAll();
export const fixedUi = new UiController(true);
fixedUi.register(
new GameUi('markedEnemy', UI.Marked),
new GameUi('fixed', UI.Fixed),
new GameUi('chapter', UI.Chapter),
new GameUi('completeAchi', UI.CompleteAchi),
new GameUi('start', UI.Start),
new GameUi('toolbar', UI.Toolbar),
new GameUi('load', UI.Load),
new GameUi('danmaku', UI.Danmaku),
new GameUi('danmakuEditor', UI.DanmakuEditor),
new GameUi('tips', UI.Tips)
);
fixedUi.showAll();
const hook = Mota.require('var', 'hook');
hook.once('mounted', () => {
const ui = document.getElementById('ui-main')!;
const fixed = document.getElementById('ui-fixed')!;
const blur = mainSetting.getSetting('screen.blur');
mainUi.on('start', () => {
ui.style.display = 'flex';
if (blur?.value) {
ui.style.backdropFilter = 'blur(5px)';
ui.style.backgroundColor = 'rgba(0,0,0,0.7333)';
} else {
ui.style.backdropFilter = 'none';
ui.style.backgroundColor = 'rgba(0,0,0,0.85)';
}
core.lockControl();
});
mainUi.on('end', noClosePanel => {
ui.style.display = 'none';
if (!noClosePanel) {
try {
core.closePanel();
} catch {}
}
});
fixedUi.on('start', () => {
fixed.style.display = 'block';
});
fixedUi.on('end', () => {
fixed.style.display = 'none';
});
});

View File

@ -1,574 +0,0 @@
import { FunctionalComponent, reactive } from 'vue';
import { EventEmitter } from '@motajs/legacy-common';
import { GameStorage } from './storage';
import { has, triggerFullscreen, isMobile } from '@motajs/legacy-ui';
import { createSettingComponents } from './init/settings';
import settingsText from '@/data/settings.json';
import { CustomToolbar } from './custom/toolbar';
import { fixedUi } from './init/ui';
import { bgmController, soundPlayer } from '@/module';
export interface SettingComponentProps {
item: MotaSettingItem;
setting: MotaSetting;
displayer: SettingDisplayer;
}
export type SettingComponent = FunctionalComponent<SettingComponentProps>;
type MotaSettingType = boolean | number | MotaSetting;
export interface MotaSettingItem<T extends MotaSettingType = MotaSettingType> {
name: string;
key: string;
value: T;
controller: SettingComponent;
description?: string;
defaults?: boolean | number;
step?: [number, number, number];
display?: (value: T) => string;
}
interface SettingEvent {
valueChange: <T extends boolean | number>(
key: string,
newValue: T,
oldValue: T
) => void;
}
export type SettingText = {
[key: string]: string[] | SettingText;
};
export interface SettingDisplayInfo {
item: MotaSettingItem | null;
list: Record<string, MotaSettingItem>;
text: string[];
}
const COM = createSettingComponents();
export class MotaSetting extends EventEmitter<SettingEvent> {
static noStorage: string[] = [];
readonly list: Record<string, MotaSettingItem> = {};
/**
*
* @param setting
*/
reset(setting: Record<string, boolean | number>) {
for (const [key, value] of Object.entries(setting)) {
this.setValue(key, value);
}
}
/**
*
* @param key
* @param value
*/
register(
key: string,
name: string,
value: number,
com?: SettingComponent,
step?: [number, number, number]
): this;
/**
*
* @param key
* @param value
*/
register(
key: string,
name: string,
value: boolean | MotaSetting,
com?: SettingComponent
): this;
register(
key: string,
name: string,
value: MotaSettingType,
com: SettingComponent = COM.Default,
step: [number, number, number] = [0, 100, 1]
) {
const setting: MotaSettingItem = {
name,
value,
key,
controller: com
};
if (!(value instanceof MotaSetting)) setting.defaults = value;
if (typeof value === 'number') setting.step = step;
this.list[key] = setting;
return this;
}
/**
*
* @param key
*/
getSetting(key: string): Readonly<MotaSettingItem | null> {
const list = key.split('.');
return this.getSettingBy(list);
}
/**
*
* @param key
* @param value
*/
setValue(key: string, value: boolean | number) {
const setting = this.getSettingBy(key.split('.'));
if (typeof setting.value !== typeof value) {
throw new Error(
`Setting type mismatch on setting '${key}'.` +
`Expected: ${typeof setting.value}. Recieve: ${typeof value}`
);
}
const old = setting.value as boolean | number;
setting.value = value;
this.emit('valueChange', key, value, old);
}
/**
*
* @param key
* @param value
*/
addValue(key: string, value: number) {
const setting = this.getSettingBy(key.split('.'));
if (typeof setting.value !== 'number') {
throw new Error(
`Cannot execute addValue method on a non-number setting.` +
`Type expected: number. See: ${typeof setting.value}`
);
}
const old = setting.value as boolean | number;
setting.value += value;
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
* @param func
*/
setDisplayFunc(key: string, func: (value: MotaSettingType) => string) {
const setting = this.getSettingBy(key.split('.'));
setting.display = func;
return this;
}
/**
*
* @param key
* @param com
*/
setValueController(key: string, com: SettingComponent) {
const setting = this.getSettingBy(key.split('.'));
setting.controller = com;
return this;
}
/**
*
* @param key id
* @param desc
*/
setDescription(key: string, desc: string) {
const setting = this.getSettingBy(key.split('.'));
setting.description = desc;
return this;
}
private getSettingBy(list: string[]) {
let now: MotaSetting = this;
for (let i = 0; i < list.length - 1; i++) {
const item = now.list[list[i]].value;
if (!(item instanceof MotaSetting)) {
throw new Error(
`Cannot get setting. The parent isn't a MotaSetting instance.` +
`Key: '${list.join('.')}'. Reading: '${list[i]}'`
);
}
now = item;
}
return now.list[list.at(-1)!] ?? null;
}
}
interface SettingDisplayerEvent {
update: (stack: string[], display: SettingDisplayInfo[]) => void;
}
export class SettingDisplayer extends EventEmitter<SettingDisplayerEvent> {
setting: MotaSetting;
/** 选项选中栈 */
selectStack: string[] = [];
displayInfo: SettingDisplayInfo[] = reactive([]);
constructor(setting: MotaSetting) {
super();
this.setting = setting;
this.update();
}
/**
*
* @param key
*/
add(key: string) {
this.selectStack.push(...key.split('.'));
this.update();
}
/**
*
* @param index
*/
cut(index: number, noUpdate: boolean = false) {
this.selectStack.splice(index, Infinity);
if (!noUpdate) this.update();
}
update() {
const list = this.selectStack;
let now = this.setting;
this.displayInfo = [];
for (let i = 0; i < list.length - 1; i++) {
const item = now.list[list[i]].value;
if (!(item instanceof MotaSetting)) {
throw new Error(
`Cannot get setting. The parent isn't a MotaSetting instance.` +
`Key: '${list.join('.')}'. Reading: '${list[i + 1]}'`
);
}
this.displayInfo.push({
item: now.list[list[i]],
text: [],
list: now.list
});
now = item;
}
const last = now.list[list.at(-1)!];
if (last) {
const desc = last.description;
const text = desc ? desc.split('\n') : ['请选择设置'];
this.displayInfo.push({
item: last,
text,
list: now.list
});
if (last.value instanceof MotaSetting) {
this.displayInfo.push({
item: null,
text: ['请选择设置'],
list: (last.value as MotaSetting).list
});
}
} else {
this.displayInfo.push({
item: null,
text: ['请选择设置'],
list: this.setting.list
});
}
this.emit('update', this.selectStack, this.displayInfo);
}
}
export const mainSetting = new MotaSetting();
// 添加不参与全局存储的设置
MotaSetting.noStorage.push('action.autoSkill', 'screen.fullscreen');
const storage = new GameStorage(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') {
handleScreenSetting(setting, n, o);
} else if (root === 'action') {
handleActionSetting(setting, n, o);
} else if (root === 'audio') {
handleAudioSetting(setting, n, o);
} else if (root === 'ui') {
handleUiSetting(setting, n, o);
}
});
const root = document.getElementById('root') as HTMLDivElement;
function handleScreenSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'fullscreen') {
// 全屏
triggerFullscreen(n as boolean);
} else if (key === 'heroDetail') {
// 勇士显伤
core.drawHero();
} else if (key === 'fontSize') {
// 字体大小
root.style.fontSize = `${n}px`;
const absoluteSize = (n as number) * devicePixelRatio;
storage.setValue('@@absoluteFontSize', absoluteSize);
storage.write();
} else if (key === 'fontSizeStatus') {
// fontSize.value = n as number;
}
}
function handleActionSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'autoSkill') {
// 自动切换技能
const HeroSkill = Mota.require('module', 'Mechanism').HeroSkill;
HeroSkill.setAutoSkill(n as boolean);
core.status.route.push(`set:autoSkill:${n}`)
}
}
function handleAudioSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'bgmEnabled') {
bgmController.setEnabled(n as boolean);
core.checkBgm();
} else if (key === 'bgmVolume') {
bgmController.setVolume((n as number) / 100);
} else if (key === 'soundEnabled') {
soundPlayer.setEnabled(n as boolean);
} else if (key === 'soundVolume') {
soundPlayer.setVolume((n as number) / 100)
}
}
function handleUiSetting<T extends number | boolean>(key: string, n: T, o: T) {
if (key === 'toolbarScale') {
const scale = (n as number) / (o as number);
CustomToolbar.list.forEach(v => {
v.setSize(v.width * scale, v.height * scale);
});
CustomToolbar.refreshAll(true);
} else if (key === 'danmaku') {
if (n) {
fixedUi.open('danmaku');
} else {
fixedUi.closeByName('danmaku');
}
} else if (key === 'tips') {
if (n && core.isPlaying()) {
fixedUi.open('tips');
} else {
fixedUi.closeByName('tips')
}
}
}
// ----- 游戏的所有设置项
// todo: 虚拟键盘缩放,小地图楼传缩放
mainSetting
.register(
'screen',
'显示设置',
new MotaSetting()
.register('fullscreen', '全屏游戏', false, COM.Boolean)
.register('halo', '光环显示', true, COM.Boolean)
.register('itemDetail', '宝石血瓶显伤', true, COM.Boolean)
.register('heroDetail', '勇士显伤', false, COM.Boolean)
.register('transition', '界面动画', false, COM.Boolean)
.register('fontSize', '字体大小', 16, COM.Number, [2, 48, 1])
.register('fontSizeStatus', '状态栏字体', 16, COM.Number, [10, 300, 10])
.register('smoothView', '平滑镜头', true, COM.Boolean)
.register('criticalGem', '临界显示方式', false, COM.Boolean)
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
.register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500])
.register('blur', '背景虚化', !isMobile, COM.Boolean)
)
.register(
'action',
'操作设置',
new MotaSetting()
.register('autoSkill', '自动切换技能', true, COM.Boolean)
.register('fixed', '定点查看', true, COM.Boolean)
.register('hotkey', '快捷键', false, COM.HotkeySetting)
.setDisplayFunc('hotkey', () => '')
.register('toolbar', '自定义工具栏', false, COM.ToolbarEditor)
.setDisplayFunc('toolbar', () => '')
)
.register(
'audio',
'音频设置',
new MotaSetting()
.register('bgmEnabled', '开启音乐', true, COM.Boolean)
.register('bgmVolume', '音乐音量', 80, COM.Number, [0, 100, 5])
.register('soundEnabled', '开启音效', true, COM.Boolean)
.register('soundVolume', '音效音量', 80, COM.Number, [0, 100, 5])
)
.register(
'utils',
'系统设置',
new MotaSetting()
.register('betterLoad', '优化加载', true, COM.Boolean)
.register('autoScale', '自动放缩', true, COM.Boolean)
)
.register(
'fx',
'特效设置',
new MotaSetting()
.register('paraLight', '野外阴影', true, COM.Boolean)
.register('frag', '打怪特效', true, COM.Boolean)
.register('portalParticle', '传送门特效', true, COM.Boolean)
)
.register(
'ui',
'ui设置',
new MotaSetting()
.register('mapScale', '小地图缩放', 100, COM.Number, [50, 1000, 50])
.setDisplayFunc('mapScale', value => `${value}%`)
.register('mapLazy', '小地图懒更新', false, COM.Boolean)
.register('toolbarScale', '工具栏缩放', 100, COM.Number, [10, 500, 10])
.setDisplayFunc('toolbarScale', value => `${value}%`)
.register('bookScale', '怪物手册缩放', 100, COM.Number, [10, 500, 10])
.setDisplayFunc('bookScale', value => `${value}%`)
.register('danmaku', '显示弹幕', true, COM.Boolean)
.register('danmakuSpeed', '弹幕速度', 60, COM.Number, [10, 1000, 5])
.register('tips', '小贴士', true, COM.Boolean)
);
const loading = Mota.require('var', 'loading');
loading.once('coreInit', () => {
mainSetting.reset({
'screen.fullscreen': !!document.fullscreenElement,
'screen.halo': !!storage.getValue('screen.showHalo', true),
'screen.itemDetail': !!storage.getValue('screen.itemDetail', true),
'screen.heroDetail': !!storage.getValue('screen.heroDetail', false),
'screen.transition': !!storage.getValue('screen.transition', false),
'screen.fontSize': storage.getValue('screen.fontSize', isMobile ? 9 : 16),
'screen.smoothView': !!storage.getValue('screen.smoothView', true),
'screen.criticalGem': !!storage.getValue('screen.criticalGem', false),
'screen.fontSizeStatus': storage.getValue('screen.fontSizeStatus', 100),
'action.fixed': !!storage.getValue('action.fixed', true),
'audio.bgmEnabled': !!storage.getValue('audio.bgmEnabled', true),
'audio.bgmVolume': storage.getValue('audio.bgmVolume', 80),
'audio.soundEnabled': !!storage.getValue('audio.soundEnabled', true),
'audio.soundVolume': storage.getValue('audio.soundVolume', 80),
'utils.betterLoad': !!storage.getValue('utils.betterLoad', true),
'utils.autoScale': !!storage.getValue('utils.autoScale', true),
'fx.paraLight': !!storage.getValue('fx.paraLight', true),
'fx.frag': !!storage.getValue('fx.frag', true),
'fx.portalParticle': !!storage.getValue('fx.portalParticle', true),
'ui.mapScale': storage.getValue(
'ui.mapScale',
isMobile ? 300 : Math.floor(window.innerWidth / 600) * 50
),
'ui.mapLazy': storage.getValue('ui.mapLazy', false),
'ui.toolbarScale': storage.getValue(
'ui.toolbarScale',
isMobile ? 50 : Math.floor((window.innerWidth / 1700) * 10) * 10
),
'ui.bookScale': storage.getValue('ui.bookScale', isMobile ? 100 : 80),
'ui.danmaku': storage.getValue('ui.danmaku', true),
'ui.danmakuSpeed': storage.getValue(
'ui.danmakuSpeed',
Math.floor(window.innerWidth / 30) * 5
),
'ui.tips': storage.getValue('ui.tips', true)
});
});
interface SettingTextData {
[x: string]: string[] | SettingTextData;
}
function getSettingText(obj: SettingTextData, key?: string) {
for (const [k, value] of Object.entries(obj)) {
const setKey = key ? key + '.' + k : k;
if (value instanceof Array) {
mainSetting.setDescription(setKey, value.join('\n'));
} else {
getSettingText(value, setKey);
}
}
}
getSettingText(settingsText);
mainSetting
.setDescription('audio.bgmEnabled', `是否开启背景音乐`)
.setDescription('audio.bgmVolume', `背景音乐的音量`)
.setDescription('audio.soundEnabled', `是否开启音效`)
.setDescription('audio.soundVolume', `音效的音量`)
.setDescription('ui.mapScale', `楼传小地图的缩放,百分比格式`)
.setDescription('ui.mapLazy', `是否启用小地图懒更新模式,此模式下剩余怪物数量不会实时更新而变成切换地图后更新,打开小地图时出现卡顿可以尝试开启此设置`)
.setDescription('ui.toolbarScale', `自定义工具栏的缩放比例`)
.setDescription('ui.bookScale', `怪物手册界面中每个怪物框体的高度缩放,最小值限定为 20% 屏幕高度`)
.setDescription('ui.danmaku', '是否显示弹幕')
.setDescription('ui.danmakuSpeed', '弹幕速度,刷新或开关弹幕显示后起效')
.setDescription('ui.tips', `是否在游戏画面右上角常亮显示小贴士`)
.setDescription('screen.fontSizeStatus', `修改状态栏的字体大小`)
.setDescription('screen.blur', '打开任意ui界面时是否有背景虚化效果移动端打开后可能会有掉帧或者发热现象。关闭ui后生效')
.setDescription('fx.portalParticle', '是否启用苍蓝之殿的传送门粒子特效,启用后可能对性能及设备发热有所影响')
function setFontSize() {
const absoluteSize = storage.getValue(
'@@absoluteFontSize',
16 * devicePixelRatio
);
const size = Math.round(absoluteSize / devicePixelRatio);
mainSetting.setValue('screen.fontSize', size);
}
setFontSize();
window.addEventListener('resize', () => {
setFontSize();
});

View File

@ -1,15 +0,0 @@
import * as axios from 'axios';
import * as chart from 'chart.js';
import jszip from 'jszip';
import * as lodash from 'lodash-es';
import * as lzstring from 'lz-string';
import * as animate from 'mutate-animate';
import * as vue from 'vue';
// Mota.Package.register('axios', axios);
// Mota.Package.register('chart.js', chart);
// Mota.Package.register('jszip', jszip);
// Mota.Package.register('lodash', lodash);
// Mota.Package.register('lz-string', lzstring);
// Mota.Package.register('mutate-animate', animate);
// Mota.Package.register('vue', vue);

View File

@ -1,7 +1,6 @@
import { logger } from '@motajs/common';
import { EventEmitter } from 'eventemitter3';
import { cloneDeep, isNil } from 'lodash-es';
import { ItemState } from './item';
import { cloneDeep } from 'lodash-es';
import { HeroSkill, NightSpecial } from '../mechanism/misc';
/**
@ -343,122 +342,3 @@ interface IHeroItem {
*/
hasItem(item: AllIdsOf<'items'>): boolean;
}
interface HeroEvent {
beforeMove: [dir: Dir2];
afterMove: [dir: Dir2];
beforeMoveDirectly: [x: number, y: number];
afterMoveDirectly: [x: number, y: number];
stateChange: [state: HeroState<any>];
}
export class Hero<T extends object = IHeroStatusDefault>
extends EventEmitter<HeroEvent>
implements IHeroItem
{
x: number;
y: number;
floorId: FloorIds;
dir: Dir2;
readonly items: Map<AllIdsOf<'items'>, number> = new Map();
readonly id: string;
state: HeroState<T>;
constructor(
id: string,
x: number,
y: number,
floorId: FloorIds,
state: HeroState<T>
) {
super();
this.id = id;
this.x = x;
this.y = y;
this.floorId = floorId;
this.state = state;
this.dir = 'down';
}
/**
* 使使使
* @param state
*/
setState(state: HeroState<T>): void {
this.state = state;
this.emit('stateChange', state);
}
/**
*
*/
getState(): HeroState<T> {
return this.state;
}
/**
* {@link HeroState.refreshStatus}
*/
refreshState(key?: keyof T) {
return this.state.refreshStatus(key);
}
setItem(item: AllIdsOf<'items'>, value: number): boolean {
this.items.set(item, value < 0 ? 0 : value);
return true;
}
addItem(item: AllIdsOf<'items'>, value: number): boolean {
return this.setItem(item, (this.items.get(item) ?? 0) + value);
}
useItem(item: AllIdsOf<'items'>): boolean {
const state = ItemState.item(item);
return !!state?.use(this);
}
/**
*
* @param item id
* @param num
*/
getItem(item: AllIdsOf<'items'>, num: number): boolean;
/**
*
* @param item id
* @param x x坐标
* @param y y坐标
* @param floorId
* @param num
*/
getItem(x: number, y: number, floorId?: FloorIds, num?: number): boolean;
getItem(
item: AllIdsOf<'items'> | number,
y: number,
floorId: FloorIds = this.floorId,
num: number = 1
): boolean {
if (!isNil(floorId) && typeof item === 'number') {
// 如果指定了坐标
const block = core.getBlock(item as number, y, floorId);
const id = block.event.id as AllIdsOf<'items'>;
const cls = core.material.items[id]?.cls;
if (cls === void 0) {
logger.warn(15, item.toString(), y.toString(), floorId);
return false;
}
return this.addItem(id, num!);
}
return this.addItem(item as AllIdsOf<'items'>, num!);
}
itemCount(item: AllIdsOf<'items'>): number {
return this.items.get(item) ?? 0;
}
hasItem(item: AllIdsOf<'items'>): boolean {
return this.itemCount(item) > 0;
}
}

View File

@ -1,13 +1,11 @@
import EventEmitter from 'eventemitter3';
import type { Hero } from './hero';
import { GameState, gameStates } from './state';
import { loading } from '../game';
type EffectFn = (state: GameState, hero: Hero<any>) => void;
type CanUseEffectFn = (state: GameState, hero: Hero<any>) => boolean;
type EffectFn = () => void;
type CanUseEffectFn = () => boolean;
interface ItemStateEvent {
use: [hero: Hero<any>];
use: [];
}
export class ItemState<
@ -56,7 +54,6 @@ export class ItemState<
this.canUseItemEffect = item.canUseItemEffect;
this.compileFunction();
this.compileEvent();
}
private compileFunction() {
@ -85,48 +82,6 @@ export class ItemState<
}
}
private compileEvent() {
// todo
}
/**
* 使
* @param hero 使
*/
use(hero: Hero<any>): boolean {
if (!this.canUse(hero)) return false;
if (!gameStates.now) return false;
const state = gameStates.now;
this.useItemEffectFn?.(state, hero);
if (this.useItemEvent) core.insertAction(this.useItemEvent);
if (!this.noRoute) {
core.status.route.push(`item:${this.id}`);
}
hero.addItem(this.id, -1);
this.emit('use', hero);
return true;
}
/**
* 使
* @param hero 使
*/
canUse(hero: Hero<any>, num: number = 1): boolean {
if (num <= 0) return false;
if (hero.itemCount(this.id) < num) return false;
if (!gameStates.now) return false;
return !!this.canUseItemEffectFn?.(gameStates.now, hero);
}
/**
* 使
*/
markNoRoute() {
this.noRoute = true;
}
/**
*
* @param id id

View File

@ -11,7 +11,7 @@ import type {
LayerMovingRenderable,
LayerFloorBinder
} from '@motajs/render';
import type { HeroKeyMover } from '@/core/main/action/move';
import type { HeroKeyMover } from '@/module/action/move';
import { BluePalace, MiscData } from '../mechanism/misc';
import { sleep } from 'mutate-animate';
@ -293,7 +293,7 @@ export class BlockMover extends ObjectMoverBase {
return true;
}
protected async onMoveStart(controller: IMoveController): Promise<void> {
protected async onMoveStart(_controller: IMoveController): Promise<void> {
const adapter = BlockMover.adapter;
if (adapter) {
const list = adapter.items;
@ -332,7 +332,7 @@ export class BlockMover extends ObjectMoverBase {
}
}
protected async onMoveEnd(controller: IMoveController): Promise<void> {
protected async onMoveEnd(_controller: IMoveController): Promise<void> {
if (this.renderable) {
this.layerItems.forEach(v => {
v.moving.delete(this.renderable!);
@ -349,7 +349,7 @@ export class BlockMover extends ObjectMoverBase {
protected async onStepStart(
step: MoveStepDir,
controller: IMoveController
_controller: IMoveController
): Promise<BlockMoveCode> {
await this.moveAnimate(step);
const { x: dx, y: dy } = core.utils.scan2[this.moveDir];
@ -360,17 +360,17 @@ export class BlockMover extends ObjectMoverBase {
}
protected async onStepEnd(
step: MoveStepDir,
code: BlockMoveCode,
controller: IMoveController
_step: MoveStepDir,
_code: BlockMoveCode,
_controller: IMoveController
): Promise<void> {}
protected onSetMoveSpeed(
speed: number,
controller: IMoveController
_speed: number,
_controller: IMoveController
): void {}
private moveAnimate(step: MoveStepDir) {
private moveAnimate(_step: MoveStepDir) {
const layer = this.layerItems[0];
if (!layer) return;
if (!this.renderable) return;
@ -521,7 +521,7 @@ export class HeroMover extends ObjectMoverBase {
}
protected async onStepStart(
step: MoveStepDir,
_step: MoveStepDir,
controller: IMoveController
): Promise<HeroMoveCode> {
const showDir = toDir(this.faceDir);
@ -594,7 +594,7 @@ export class HeroMover extends ObjectMoverBase {
}
protected async onStepEnd(
step: MoveStepDir,
_step: MoveStepDir,
code: HeroMoveCode,
controller: IMoveController
): Promise<void> {
@ -661,7 +661,10 @@ export class HeroMover extends ObjectMoverBase {
}
}
protected onSetMoveSpeed(speed: number, controller: IMoveController): void {
protected onSetMoveSpeed(
speed: number,
_controller: IMoveController
): void {
const adapter = HeroMover.adapter;
if (!adapter) return;
adapter.sync('setMoveSpeed', speed);
@ -785,7 +788,7 @@ export class HeroMover extends ObjectMoverBase {
const list = adapter.items;
const { x: tx, y: ty, dir: toDir } = data;
const { x, y, direction } = core.status.hero.loc;
const { x: dx, y: dy } = core.utils.scan[direction];
const { x: dx } = core.utils.scan[direction];
const { x: tdx } = core.utils.scan[toDir];
const promises = [...list].map(v => {

View File

@ -1,127 +0,0 @@
import { Undoable } from '@/core/interface';
import { EventEmitter } from 'eventemitter3';
import { logger } from '@motajs/common';
type ToJSONFunction<T> = (data: T) => string;
type FromJSONFunction<T> = (data: string) => T;
export class GameState {
state: Map<string, any> = new Map();
private static states: Set<string> = new Set();
private static toJSONFn: Map<string, ToJSONFunction<any>> = new Map();
private static fromJSONFn: Map<string, FromJSONFunction<any>> = new Map();
/**
*
*/
toJSON() {
const obj: Record<string, string> = {};
this.state.forEach((v, k) => {
const to = GameState.toJSONFn.get(k);
if (to) obj[k] = to(v);
else obj[k] = JSON.stringify(v);
});
return JSON.stringify(obj);
}
/**
*
* @param key
*/
get<T>(key: string): T | undefined {
return this.state.get(key);
}
/**
*
* @param key
* @param data
*/
set(key: string, data: any) {
this.state.set(key, data);
}
/**
*
* @param key
* @param toJSON
* 使JSON.stringify进行序列化
* @param fromJSON
* 使JSON.parse进行反序列化
*/
static register<T>(
key: string,
toJSON?: ToJSONFunction<T>,
fromJSON?: FromJSONFunction<T>
) {
if (this.states.has(key)) {
logger.warn(16, key);
}
if (toJSON) {
this.toJSONFn.set(key, toJSON);
} else {
this.toJSONFn.delete(key);
}
if (fromJSON) {
this.fromJSONFn.set(key, fromJSON);
} else {
this.fromJSONFn.delete(key);
}
}
/**
*
* @param json
*/
static fromJSON(json: string) {
const obj: Record<string, string> = JSON.parse(json);
const state = new GameState();
for (const [key, data] of Object.entries(obj)) {
const from = this.fromJSONFn.get(key);
if (from) state.set(key, from(data));
else state.set(key, JSON.parse(data));
}
return state;
}
}
interface StateStoreEvent {
undo: [state: GameState];
redo: [state: GameState];
change: [before: GameState | undefined, now: GameState];
}
class StateStore
extends EventEmitter<StateStoreEvent>
implements Undoable<GameState>
{
now?: GameState;
stack: GameState[] = [];
redoStack: GameState[] = [];
undo(): GameState | undefined {
const state = this.stack.pop();
if (!state) return void 0;
this.redoStack.push(state);
this.emit('undo', state);
return state;
}
redo(): GameState | undefined {
const state = this.redoStack.pop();
if (!state) return void 0;
this.stack.push(state);
this.emit('redo', state);
return state;
}
use(state: GameState) {
const before = this.now;
this.now = state;
this.emit('change', before, state);
}
}
export const gameStates = new StateStore();

View File

@ -23,7 +23,7 @@ import type { Danmaku } from '@/core/main/custom/danmaku';
import type * as misc from './mechanism/misc';
import type { Render } from '@motajs/client';
import type { ItemState } from './state/item';
import type { HeroKeyMover } from '@/core/main/action/move';
import type { HeroKeyMover } from '@/module/action/move';
import type { BlockMover, HeroMover, ObjectMoverBase } from './state/move';
import type * as Animation from 'mutate-animate';
import type { WeatherController } from '@/module/weather/weather';

View File

@ -1,8 +1,8 @@
import { KeyCode } from '@motajs/client-base';
import { Hotkey, HotkeyData } from '../custom/hotkey';
import { Hotkey, HotkeyData } from '@motajs/system-action';
import type { HeroMover, IMoveController } from '@/game/state/move';
import { Ticker } from 'mutate-animate';
import { mainScope } from '../init/hotkey';
import { mainScope } from '@motajs/legacy-ui';
type MoveKey = Record<Dir, HotkeyData>;
type MoveKeyConfig = Record<Dir, string>;

View File

@ -1,6 +1,6 @@
import { Patch, PatchClass } from '@motajs/legacy-common';
import { audioPlayer, bgmController, soundPlayer } from '../audio';
import { mainSetting } from '@/core/main/setting';
import { mainSetting } from '@motajs/legacy-ui';
import { sleep } from 'mutate-animate';
import { isNil } from 'lodash-es';

View File

@ -10,9 +10,9 @@ import {
SetupComponentOptions,
waitbox
} from '../components';
import { mainUi } from '@/core/main/init/ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { generateKeyboardEvent } from '@/core/main/custom/keyboard';
import { mainUi } from '@motajs/legacy-ui';
import { gameKey } from '@motajs/system-action';
import { generateKeyboardEvent } from '@motajs/system-action';
import { getVitualKeyOnce } from '@motajs/legacy-ui';
import { getAllSavesData, getSaveData } from '@/module/utils';

View File

@ -18,8 +18,8 @@ import {
getVitualKeyOnce,
openDanmakuPoster
} from '@motajs/legacy-ui';
import { gameKey } from '@/core/main/custom/hotkey';
import { generateKeyboardEvent } from '@/core/main/custom/keyboard';
import { gameKey } from '@motajs/system-action';
import { generateKeyboardEvent } from '@motajs/system-action';
import { transitioned } from '../use';
import { linear } from 'mutate-animate';
import { KeyCode } from '@motajs/client-base';

View File

@ -7,9 +7,11 @@
"@motajs/render": "workspace:*",
"@motajs/system": "workspace:*",
"@motajs/system-ui": "workspace:*",
"@motajs/system-action": "workspace:*",
"@motajs/legacy-common": "workspace:*",
"@motajs/legacy-client": "workspace:*",
"@motajs/legacy-data": "workspace:*",
"@motajs/legacy-ui": "workspace:*"
"@motajs/legacy-ui": "workspace:*",
"@motajs/legacy-system": "workspace:*"
}
}

View File

@ -1,6 +1,6 @@
import { logger } from '@motajs/common';
import { MotaOffscreenCanvas2D } from '@motajs/render';
import { mainSetting } from '@/core/main/setting';
import { mainSetting } from '@motajs/legacy-ui';
import {
LayerGroupFloorBinder,
ILayerGroupRenderExtends,

View File

@ -1,5 +1,5 @@
import { logger } from '@motajs/common';
import { mainSetting } from '@/core/main/setting';
import { mainSetting } from '@motajs/legacy-ui';
import {
Damage,
DamageRenderable,

View File

@ -1,6 +1,6 @@
import { logger } from '@motajs/common';
import { MotaOffscreenCanvas2D } from '@motajs/render';
import { mainSetting, MotaSettingItem } from '@/core/main/setting';
import { mainSetting, MotaSettingItem } from '@motajs/legacy-ui';
import {
LayerGroupFloorBinder,
ILayerGroupRenderExtends,