mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-18 17:48:52 +08:00
refactor: src/core/main 全部移入 monorepo
This commit is contained in:
parent
9ec13e5314
commit
960dbd429a
1
.gitignore
vendored
1
.gitignore
vendored
@ -49,3 +49,4 @@ script/people.ts
|
|||||||
docs/
|
docs/
|
||||||
user.ts
|
user.ts
|
||||||
.antlr
|
.antlr
|
||||||
|
graph.svg
|
12
.madgerc
Normal file
12
.madgerc
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"fileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"tsx"
|
||||||
|
],
|
||||||
|
"tsConfig": "./tsconfig.json",
|
||||||
|
"detectiveOptions": {
|
||||||
|
"ts": {
|
||||||
|
"skipTypeImports": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -63,6 +63,7 @@
|
|||||||
"glob": "^11.0.1",
|
"glob": "^11.0.1",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.14.0",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
|
"madge": "^8.0.0",
|
||||||
"postcss-preset-env": "^9.6.0",
|
"postcss-preset-env": "^9.6.0",
|
||||||
"rollup": "^3.29.4",
|
"rollup": "^3.29.4",
|
||||||
"terser": "^5.31.6",
|
"terser": "^5.31.6",
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './keyCodes';
|
export * from './keyCodes';
|
||||||
|
export * from './types';
|
||||||
|
4
packages/client-base/src/types.ts
Normal file
4
packages/client-base/src/types.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface ResponseBase {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
}
|
6
packages/legacy-system/package.json
Normal file
6
packages/legacy-system/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "@motajs/legacy-system",
|
||||||
|
"dependencies": {
|
||||||
|
"@motajs/system-action": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
2
packages/legacy-system/src/index.ts
Normal file
2
packages/legacy-system/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './misc';
|
||||||
|
export * from './storage';
|
@ -1,5 +1,5 @@
|
|||||||
import { Keyboard } from '../custom/keyboard';
|
import { Keyboard } from '@motajs/system-action';
|
||||||
import KeyboardUI from '@/panel/keyboard.vue';
|
import KeyboardUI from '../panel/keyboard.vue';
|
||||||
|
|
||||||
interface VirtualKeyProps {
|
interface VirtualKeyProps {
|
||||||
keyboard: Keyboard;
|
keyboard: Keyboard;
|
@ -4,6 +4,8 @@
|
|||||||
"@motajs/render": "workspace:*",
|
"@motajs/render": "workspace:*",
|
||||||
"@motajs/common": "workspace:*",
|
"@motajs/common": "workspace:*",
|
||||||
"@motajs/client": "workspace:*",
|
"@motajs/client": "workspace:*",
|
||||||
"@motajs/client-base": "workspace:*"
|
"@motajs/client-base": "workspace:*",
|
||||||
|
"@motajs/legacy-system": "workspace:*",
|
||||||
|
"@motajs/system-action": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
6
packages/legacy-ui/src/components/index.ts
Normal file
6
packages/legacy-ui/src/components/index.ts
Normal 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';
|
@ -8,7 +8,7 @@ import { requireUniqueSymbol } from '../utils';
|
|||||||
import { MinimapDrawer, getArea } from '../tools/fly';
|
import { MinimapDrawer, getArea } from '../tools/fly';
|
||||||
import { useDrag, useWheel } from '../use';
|
import { useDrag, useWheel } from '../use';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
action?: boolean;
|
action?: boolean;
|
||||||
@ -168,6 +168,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
const hook = Mota.require('var', 'hook');
|
||||||
hook.off('afterChangeFloor', onChange);
|
hook.off('afterChangeFloor', onChange);
|
||||||
hook.off('afterBattle', afterBattle);
|
hook.off('afterBattle', afterBattle);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Component, shallowReactive } from 'vue';
|
import { Component, shallowReactive } from 'vue';
|
||||||
import { EventEmitter } from '@motajs/legacy-common';
|
import { EventEmitter } from '@motajs/legacy-common';
|
||||||
import { Hotkey } from './hotkey';
|
|
||||||
|
|
||||||
interface FocusEvent<T> {
|
interface FocusEvent<T> {
|
||||||
focus: (before: T | null, after: T) => void;
|
focus: (before: T | null, after: T) => void;
|
||||||
@ -125,6 +124,7 @@ type UiVBind = Record<string, any>;
|
|||||||
interface MountedVBind {
|
interface MountedVBind {
|
||||||
num: number;
|
num: number;
|
||||||
ui: GameUi;
|
ui: GameUi;
|
||||||
|
controller: UiController;
|
||||||
[x: string]: any;
|
[x: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,14 +132,12 @@ export class GameUi extends EventEmitter<GameUiEvent> {
|
|||||||
static uiList: GameUi[] = [];
|
static uiList: GameUi[] = [];
|
||||||
|
|
||||||
component: Component;
|
component: Component;
|
||||||
hotkey?: Hotkey;
|
|
||||||
id: string;
|
id: string;
|
||||||
symbol: symbol = Symbol();
|
symbol: symbol = Symbol();
|
||||||
|
|
||||||
constructor(id: string, component: Component, hotkey?: Hotkey) {
|
constructor(id: string, component: Component) {
|
||||||
super();
|
super();
|
||||||
this.component = component;
|
this.component = component;
|
||||||
this.hotkey = hotkey;
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
GameUi.uiList.push(this);
|
GameUi.uiList.push(this);
|
||||||
}
|
}
|
||||||
@ -273,6 +271,7 @@ export class UiController extends Focus<IndexedGameUi> {
|
|||||||
const bind = {
|
const bind = {
|
||||||
num,
|
num,
|
||||||
ui,
|
ui,
|
||||||
|
controller: this,
|
||||||
...(vBind ?? {})
|
...(vBind ?? {})
|
||||||
};
|
};
|
||||||
const sui = ui.with(bind, vOn);
|
const sui = ui.with(bind, vOn);
|
@ -1,18 +1,10 @@
|
|||||||
import BoxAnimate from '@/components/boxAnimate.vue';
|
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { ResponseBase } from '@/core/interface';
|
import { deleteWith, ensureArray, parseCss, tip } from '@motajs/legacy-ui';
|
||||||
import {
|
import { ResponseBase } from '@motajs/client-base';
|
||||||
deleteWith,
|
|
||||||
ensureArray,
|
|
||||||
getIconHeight,
|
|
||||||
parseCss,
|
|
||||||
tip
|
|
||||||
} from '@motajs/legacy-ui';
|
|
||||||
import axios, { AxiosResponse, toFormData } from 'axios';
|
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';
|
// /* @__PURE__ */ import { id, password } from '../../../../user';
|
||||||
import { mainSetting } from '../setting';
|
|
||||||
|
|
||||||
type CSSObj = Partial<Record<CanParseCss, string>>;
|
type CSSObj = Partial<Record<CanParseCss, string>>;
|
||||||
|
|
||||||
@ -213,8 +205,8 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
|
|||||||
* @param stroke 描边颜色
|
* @param stroke 描边颜色
|
||||||
*/
|
*/
|
||||||
color(fill?: string, stroke?: string) {
|
color(fill?: string, stroke?: string) {
|
||||||
fill && (this.textColor = fill);
|
if (fill) this.textColor = fill;
|
||||||
stroke && (this.strokeColor = stroke);
|
if (stroke) this.strokeColor = stroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -492,41 +484,3 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
|
|||||||
this.specList[type] = fn;
|
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,7 +1,12 @@
|
|||||||
export * as UI from './ui';
|
export * as UI from './ui';
|
||||||
|
export * as Components from './components';
|
||||||
|
export * from './preset';
|
||||||
export * from './tools';
|
export * from './tools';
|
||||||
|
|
||||||
export * from './animateController';
|
export * from './animateController';
|
||||||
|
export * from './controller';
|
||||||
|
export * from './danmaku';
|
||||||
export * from './mark';
|
export * from './mark';
|
||||||
|
export * from './setting';
|
||||||
export * from './use';
|
export * from './use';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { fixedUi } from '@/core/main/init/ui';
|
import { fixedUi } from './preset/ui';
|
||||||
import type { DamageEnemy } from '@/game/enemy/damage';
|
import type { DamageEnemy } from '@/game/enemy/damage';
|
||||||
import { tip } from './utils';
|
import { tip } from './utils';
|
||||||
import { ref, Ref } from 'vue';
|
import { ref, Ref } from 'vue';
|
||||||
|
43
packages/legacy-ui/src/preset/danmaku.ts
Normal file
43
packages/legacy-ui/src/preset/danmaku.ts
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -18,7 +18,7 @@ const showFixed = debounce((block: Block) => {
|
|||||||
if (!enemy) return;
|
if (!enemy) return;
|
||||||
fixedUi.open(
|
fixedUi.open(
|
||||||
'fixed',
|
'fixed',
|
||||||
{ enemy, close, loc: [cx, cy] },
|
{ enemy, close, loc: [cx, cy], hovered },
|
||||||
{ close: closeFixed }
|
{ close: closeFixed }
|
||||||
);
|
);
|
||||||
}, 200);
|
}, 200);
|
@ -1,5 +1,5 @@
|
|||||||
import { KeyCode } from '@motajs/client-base';
|
import { KeyCode } from '@motajs/client-base';
|
||||||
import { gameKey, HotkeyJSON } from '../custom/hotkey';
|
import { gameKey, HotkeyJSON } from '@motajs/system-action';
|
||||||
import {
|
import {
|
||||||
openDanmakuPoster,
|
openDanmakuPoster,
|
||||||
tip,
|
tip,
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
} from '@motajs/legacy-ui';
|
} from '@motajs/legacy-ui';
|
||||||
import { hovered } from './fixed';
|
import { hovered } from './fixed';
|
||||||
import { mainUi } from './ui';
|
import { mainUi } from './ui';
|
||||||
import { GameStorage } from '../storage';
|
import { GameStorage } from '@motajs/legacy-system';
|
||||||
|
|
||||||
export const mainScope = Symbol.for('@key_main');
|
export const mainScope = Symbol.for('@key_main');
|
||||||
|
|
6
packages/legacy-ui/src/preset/index.ts
Normal file
6
packages/legacy-ui/src/preset/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './ui';
|
||||||
|
export * from './settings';
|
||||||
|
export * from './danmaku';
|
||||||
|
export * from './fixed';
|
||||||
|
export * from './hotkey';
|
||||||
|
export * from './keyboard';
|
@ -1,5 +1,5 @@
|
|||||||
import { KeyCode } from '@motajs/client-base';
|
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 qweKey = new Keyboard('qwe'); // 字母键盘,A-Z
|
||||||
const numKey = new Keyboard('num'); // 数字键盘,1-0
|
const numKey = new Keyboard('num'); // 数字键盘,1-0
|
||||||
@ -239,7 +239,7 @@ numKey
|
|||||||
y: 0,
|
y: 0,
|
||||||
width: 45,
|
width: 45,
|
||||||
height: 45,
|
height: 45,
|
||||||
text: `<span style='font-size: 80%'>\$<br />4</span>`
|
text: `<span style='font-size: 80%'>$<br />4</span>`
|
||||||
})
|
})
|
||||||
.add({
|
.add({
|
||||||
key: KeyCode.Digit5,
|
key: KeyCode.Digit5,
|
@ -1,7 +1,7 @@
|
|||||||
import type { SettingComponent, SettingComponentProps } from '../setting';
|
import type { SettingComponent, SettingComponentProps } from '../setting';
|
||||||
import { Button, InputNumber, Radio } from 'ant-design-vue';
|
import { Button, InputNumber, Radio } from 'ant-design-vue';
|
||||||
import { mainUi } from './ui';
|
import { mainUi } from './ui';
|
||||||
import { gameKey } from '../custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
|
|
||||||
interface Components {
|
interface Components {
|
||||||
Default: SettingComponent;
|
Default: SettingComponent;
|
||||||
@ -28,7 +28,7 @@ export function createSettingComponents() {
|
|||||||
return com;
|
return com;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DefaultSetting(props: SettingComponentProps) {
|
function DefaultSetting(_props: SettingComponentProps) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<span> 未知的设置类型 </span>
|
<span> 未知的设置类型 </span>
|
||||||
@ -149,7 +149,7 @@ function showSpecialSetting(id: string, vBind?: any) {
|
|||||||
mainUi.open(id, vBind);
|
mainUi.open(id, vBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HotkeySetting(props: SettingComponentProps) {
|
function HotkeySetting(_props: SettingComponentProps) {
|
||||||
return (
|
return (
|
||||||
<div style="display: flex; justify-content: center">
|
<div style="display: flex; justify-content: center">
|
||||||
<Button
|
<Button
|
||||||
@ -168,7 +168,7 @@ function HotkeySetting(props: SettingComponentProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolbarEditor(props: SettingComponentProps) {
|
function ToolbarEditor(_props: SettingComponentProps) {
|
||||||
return (
|
return (
|
||||||
<div style="display: flex; justify-content: center">
|
<div style="display: flex; justify-content: center">
|
||||||
<Button
|
<Button
|
||||||
@ -183,7 +183,7 @@ function ToolbarEditor(props: SettingComponentProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function PerformanceSetting(props: SettingComponentProps) {
|
function PerformanceSetting(_props: SettingComponentProps) {
|
||||||
return (
|
return (
|
||||||
<div style="display: flex; justify-content: center">
|
<div style="display: flex; justify-content: center">
|
||||||
<Button
|
<Button
|
364
packages/legacy-ui/src/preset/ui.ts
Normal file
364
packages/legacy-ui/src/preset/ui.ts
Normal 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();
|
||||||
|
});
|
308
packages/legacy-ui/src/setting.ts
Normal file
308
packages/legacy-ui/src/setting.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '../preset/ui';
|
||||||
import { downloadCanvasImage, has, tip } from '../utils';
|
import { downloadCanvasImage, has, tip } from '../utils';
|
||||||
|
|
||||||
type BFSFromString = `${FloorIds},${number},${number},${Dir}`;
|
type BFSFromString = `${FloorIds},${number},${number},${Dir}`;
|
||||||
|
@ -46,10 +46,10 @@ import BookDetail from './bookDetail.vue';
|
|||||||
import { LeftOutlined } from '@ant-design/icons-vue';
|
import { LeftOutlined } from '@ant-design/icons-vue';
|
||||||
import { ToShowEnemy, detailInfo } from '../tools/book';
|
import { ToShowEnemy, detailInfo } from '../tools/book';
|
||||||
import { getDetailedEnemy } from '../tools/fixed';
|
import { getDetailedEnemy } from '../tools/fixed';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '../preset/ui';
|
||||||
import { isMobile } from '../use';
|
import { isMobile } from '../use';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -83,7 +83,7 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
|
|||||||
import EnemyCritical from '../panel/enemyCritical.vue';
|
import EnemyCritical from '../panel/enemyCritical.vue';
|
||||||
import EnemyTarget from '../panel/enemyTarget.vue';
|
import EnemyTarget from '../panel/enemyTarget.vue';
|
||||||
import { detailInfo } from '../tools/book';
|
import { detailInfo } from '../tools/book';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fromBook?: boolean;
|
fromBook?: boolean;
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
import { Animation, hyper, sleep } from 'mutate-animate';
|
import { Animation, hyper, sleep } from 'mutate-animate';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { has } from '../utils';
|
import { has } from '../utils';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { fixedUi } from '@/core/main/init/ui';
|
import { fixedUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -23,8 +23,8 @@ import { computed, onMounted, ref } from 'vue';
|
|||||||
import Box from '../components/box.vue';
|
import Box from '../components/box.vue';
|
||||||
import list from '../data/achievement.json';
|
import list from '../data/achievement.json';
|
||||||
import { AchievementType, getNowPoint, totalPoint } from '../tools/achievement';
|
import { AchievementType, getNowPoint, totalPoint } from '../tools/achievement';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { fixedUi } from '@/core/main/init/ui';
|
import { fixedUi } from '../preset/ui';
|
||||||
|
|
||||||
const height = window.innerHeight;
|
const height = window.innerHeight;
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onUnmounted, reactive, watch } from 'vue';
|
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 { LikeFilled } from '@ant-design/icons-vue';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '../preset/ui';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
interface ElementMap {
|
interface ElementMap {
|
||||||
|
@ -158,17 +158,17 @@ import {
|
|||||||
SendOutlined,
|
SendOutlined,
|
||||||
UpOutlined
|
UpOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { Danmaku } from '@/core/main/custom/danmaku';
|
import { Danmaku } from '../danmaku';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
import { fixedUi } from '@/core/main/init/ui';
|
import { fixedUi } from '../preset/ui';
|
||||||
import { calStringSize, tip } from '../utils';
|
import { calStringSize, tip } from '../utils';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
import { stringifyCSS, parseCss, getIconHeight } from '../utils';
|
import { stringifyCSS, parseCss, getIconHeight } from '../utils';
|
||||||
import { logger, LogLevel } from '@motajs/common';
|
import { logger, LogLevel } from '@motajs/common';
|
||||||
import Scroll from '@/components/scroll.vue';
|
import Scroll from '../components/scroll.vue';
|
||||||
import BoxAnimate from '@/components/boxAnimate.vue';
|
import BoxAnimate from '../components/boxAnimate.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -22,9 +22,9 @@ import { computed, onUnmounted, ref } from 'vue';
|
|||||||
import desc from '../data/desc.json';
|
import desc from '../data/desc.json';
|
||||||
import { splitText } from '../utils';
|
import { splitText } from '../utils';
|
||||||
import Colomn from '../components/colomn.vue';
|
import Colomn from '../components/colomn.vue';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -186,10 +186,10 @@ import BoxAnimate from '../components/boxAnimate.vue';
|
|||||||
import { has, tip, type } from '../utils';
|
import { has, tip, type } from '../utils';
|
||||||
import { cancelGlobalDrag, isMobile, useDrag } from '../use';
|
import { cancelGlobalDrag, isMobile, useDrag } from '../use';
|
||||||
import { hyper } from 'mutate-animate';
|
import { hyper } from 'mutate-animate';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { getStatusLabel } from '../utils';
|
import { getStatusLabel } from '../utils';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUpdated, Ref, ref, watch } from 'vue';
|
import { onMounted, onUpdated, Ref, ref, watch } from 'vue';
|
||||||
import Box from '../components/box.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 type { DamageEnemy, EnemyInfo } from '@/game/enemy/damage';
|
||||||
import { nextFrame } from '../utils';
|
import { nextFrame } from '../utils';
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
import { getDetailedEnemy } from '../tools/fixed';
|
import { getDetailedEnemy } from '../tools/fixed';
|
||||||
import BookDetail from './bookDetail.vue';
|
import BookDetail from './bookDetail.vue';
|
||||||
import { detailInfo } from '../tools/book';
|
import { detailInfo } from '../tools/book';
|
||||||
import { hovered } from '@/core/main/init/fixed';
|
import { hovered } from '../preset/fixed';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -99,12 +99,12 @@ import {
|
|||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import { tip } from '../utils';
|
import { tip } from '../utils';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { createChangable } from '../tools/common';
|
import { createChangable } from '../tools/common';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '../preset/ui';
|
||||||
import { GameStorage } from '@/core/main/storage';
|
import { GameStorage } from '@motajs/legacy-system';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -36,15 +36,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Hotkey } from '@/core/main/custom/hotkey';
|
import { Hotkey } from '@motajs/system-action';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import Column from '../components/colomn.vue';
|
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 { computed, onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
import { KeyCode, KeyCodeUtils } from '@motajs/client-base';
|
import { KeyCode, KeyCodeUtils } from '@motajs/client-base';
|
||||||
import { generateBinary, keycode } from '../utils';
|
import { generateBinary, keycode } from '../utils';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
|
|
||||||
interface HotkeyKeys {
|
interface HotkeyKeys {
|
||||||
index: number;
|
index: number;
|
||||||
|
@ -31,10 +31,10 @@ import {
|
|||||||
loadDefaultResource,
|
loadDefaultResource,
|
||||||
LoadTask
|
LoadTask
|
||||||
} from '@motajs/legacy-common';
|
} from '@motajs/legacy-common';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { formatSize } from '../utils';
|
import { formatSize } from '../utils';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { fixedUi } from '@/core/main/init/ui';
|
import { fixedUi } from '../preset/ui';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -56,8 +56,8 @@ import { MarkInfo, unmarkEnemy } from '../mark';
|
|||||||
import Box from '../components/box.vue';
|
import Box from '../components/box.vue';
|
||||||
import Scroll from '../components/scroll.vue';
|
import Scroll from '../components/scroll.vue';
|
||||||
import BoxAnimate from '../components/boxAnimate.vue';
|
import BoxAnimate from '../components/boxAnimate.vue';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { fixedUi } from '@/core/main/init/ui';
|
import { fixedUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -73,20 +73,20 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onUnmounted, ref, shallowRef } from 'vue';
|
import { computed, onUnmounted, ref, shallowRef } from 'vue';
|
||||||
|
import { mainSetting } from '../preset/ui';
|
||||||
import {
|
import {
|
||||||
mainSetting,
|
|
||||||
MotaSetting,
|
MotaSetting,
|
||||||
MotaSettingItem,
|
MotaSettingItem,
|
||||||
SettingDisplayer,
|
SettingDisplayer,
|
||||||
SettingDisplayInfo
|
SettingDisplayInfo
|
||||||
} from '@/core/main/setting';
|
} from '../setting';
|
||||||
import { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';
|
import { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';
|
||||||
import { splitText } from '../utils';
|
import { splitText } from '../utils';
|
||||||
import Scroll from '../components/scroll.vue';
|
import Scroll from '../components/scroll.vue';
|
||||||
import { isMobile } from '../use';
|
import { isMobile } from '../use';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
info?: MotaSetting;
|
info?: MotaSetting;
|
||||||
|
@ -172,9 +172,9 @@ import {
|
|||||||
import { splitText, tip } from '../utils';
|
import { splitText, tip } from '../utils';
|
||||||
import Scroll from '../components/scroll.vue';
|
import Scroll from '../components/scroll.vue';
|
||||||
import BoxAnimate from '../components/boxAnimate.vue';
|
import BoxAnimate from '../components/boxAnimate.vue';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -21,7 +21,7 @@ import { computed, ref } from 'vue';
|
|||||||
import skills from '../data/skill.json';
|
import skills from '../data/skill.json';
|
||||||
import { has } from '../utils';
|
import { has } from '../utils';
|
||||||
import Column from '../components/colomn.vue';
|
import Column from '../components/colomn.vue';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -84,9 +84,9 @@ import Scroll from '../components/scroll.vue';
|
|||||||
import { has, splitText, tip } from '../utils';
|
import { has, splitText, tip } from '../utils';
|
||||||
import { isMobile } from '../use';
|
import { isMobile } from '../use';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
import type { Chapter } from '@/plugin/game/skillTree';
|
import type { Chapter } from '@/plugin/game/skillTree';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -67,11 +67,10 @@ import { sleep } from 'mutate-animate';
|
|||||||
import { doByInterval } from '../utils';
|
import { doByInterval } from '../utils';
|
||||||
import { triggerFullscreen } from '../utils';
|
import { triggerFullscreen } from '../utils';
|
||||||
import { isMobile } from '../use';
|
import { isMobile } from '../use';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
import { CustomToolbar } from '@/core/main/custom/toolbar';
|
import { mainSetting } from '../preset/ui';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
|
||||||
import { mat4 } from 'gl-matrix';
|
import { mat4 } from 'gl-matrix';
|
||||||
// todo: 改了
|
// todo: 改了
|
||||||
import { bgmController } from '@/module';
|
import { bgmController } from '@/module';
|
||||||
@ -323,7 +322,6 @@ onMounted(async () => {
|
|||||||
main = document.getElementById('start-main') as HTMLDivElement;
|
main = document.getElementById('start-main') as HTMLDivElement;
|
||||||
start = document.getElementById('start') as HTMLDivElement;
|
start = document.getElementById('start') as HTMLDivElement;
|
||||||
background = document.getElementById('background') as HTMLImageElement;
|
background = document.getElementById('background') as HTMLImageElement;
|
||||||
CustomToolbar.closeAll();
|
|
||||||
|
|
||||||
window.addEventListener('resize', resize);
|
window.addEventListener('resize', resize);
|
||||||
resize();
|
resize();
|
||||||
|
@ -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>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
@ -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>
|
|
@ -116,9 +116,9 @@ import { isMobile } from '../use';
|
|||||||
import { type, has } from '../utils';
|
import { type, has } from '../utils';
|
||||||
import { hyper } from 'mutate-animate';
|
import { hyper } from 'mutate-animate';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { GameUi } from '@/core/main/custom/ui';
|
import { GameUi } from '../controller';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '../preset/ui';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
num: number;
|
num: number;
|
||||||
|
@ -6,9 +6,8 @@ import { Ref, ref } from 'vue';
|
|||||||
import { EVENT_KEY_CODE_MAP, KeyCode } from '@motajs/client-base';
|
import { EVENT_KEY_CODE_MAP, KeyCode } from '@motajs/client-base';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { decompressFromBase64 } from 'lz-string';
|
import { decompressFromBase64 } from 'lz-string';
|
||||||
import { Keyboard, KeyboardEmits } from '@/core/main/custom/keyboard';
|
import { Keyboard, KeyboardEmits, isAssist } from '@motajs/system-action';
|
||||||
import { fixedUi, mainUi } from '@/core/main/init/ui';
|
import { fixedUi, mainUi } from './preset/ui';
|
||||||
import { isAssist } from '@/core/main/custom/hotkey';
|
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
|
|
||||||
type CanParseCss = keyof {
|
type CanParseCss = keyof {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"@motajs/common": "workspace:*",
|
"@motajs/common": "workspace:*",
|
||||||
"@motajs/render-core": "workspace:*",
|
"@motajs/render-core": "workspace:*",
|
||||||
"@motajs/render-elements": "workspace:*",
|
"@motajs/render-elements": "workspace:*",
|
||||||
"@motajs/render-style": "workspace:*"
|
"@motajs/render-style": "workspace:*",
|
||||||
|
"@motajs/system-action": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import { Animation, Ticker, Transition } from 'mutate-animate';
|
import { Animation, Ticker, Transition } from 'mutate-animate';
|
||||||
import { ERenderItemEvent, RenderItem } from '@motajs/render-core';
|
import { ERenderItemEvent, RenderItem } from '@motajs/render-core';
|
||||||
// todo: 改为 monorepo
|
import { gameKey, Hotkey } from '@motajs/system-action';
|
||||||
import { gameKey, Hotkey } from '@/core/main/custom/hotkey';
|
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
|
|
||||||
|
6
packages/system-action/package.json
Normal file
6
packages/system-action/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "@motajs/system-action",
|
||||||
|
"dependencies": {
|
||||||
|
"@motajs/common": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
2
packages/system-action/src/index.ts
Normal file
2
packages/system-action/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './hotkey';
|
||||||
|
export * from './keyboard';
|
@ -105,10 +105,10 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
this.scopeStack.push(symbol);
|
this.scopeStack.push(symbol);
|
||||||
const ev: Listener<VirtualKeyEmitFn>[] = [];
|
const ev: Listener<VirtualKeyEmitFn>[] = [];
|
||||||
this.onEmitKey[symbol] = ev;
|
this.onEmitKey[symbol] = ev;
|
||||||
// @ts-ignore
|
// @ts-expect-error 无法推导
|
||||||
this.events = ev;
|
this.events = ev;
|
||||||
this.emit('scopeCreate', symbol);
|
this.emit('scopeCreate', symbol);
|
||||||
if (!!last) {
|
if (last) {
|
||||||
this.scopeAssist[symbol] = this.assist;
|
this.scopeAssist[symbol] = this.assist;
|
||||||
}
|
}
|
||||||
this.assist = 0;
|
this.assist = 0;
|
||||||
@ -133,7 +133,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
if (!symbol) return;
|
if (!symbol) return;
|
||||||
this.scope = symbol;
|
this.scope = symbol;
|
||||||
this.assist = this.scopeAssist[symbol];
|
this.assist = this.scopeAssist[symbol];
|
||||||
// @ts-ignore
|
// @ts-expect-error 无法推导
|
||||||
this.events = this.onEmitKey[symbol];
|
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 { ctrl, alt, shift } = unwarpBinary(assist);
|
||||||
const ev = new KeyboardEvent('keyup', {
|
const ev = new KeyboardEvent('keyup', {
|
||||||
ctrlKey: ctrl,
|
ctrlKey: ctrl,
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@motajs/system",
|
"name": "@motajs/system",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@motajs/system-ui": "workspace:*"
|
"@motajs/system-ui": "workspace:*",
|
||||||
|
"@motajs/system-action": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1 +1,2 @@
|
|||||||
|
export * as Action from '@motajs/system-action';
|
||||||
export * as UI from '@motajs/system-ui';
|
export * as UI from '@motajs/system-ui';
|
||||||
|
1187
pnpm-lock.yaml
1187
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { mainUi, fixedUi } from './core/main/init/ui';
|
import { mainUi, fixedUi } from '@motajs/legacy-ui';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { hook } = Mota.requireAll('var');
|
const { hook } = Mota.requireAll('var');
|
||||||
|
@ -1,35 +1,12 @@
|
|||||||
// todo: 这个引入不加会报错,应该是循环引用导致的
|
// todo: 这个引入不加会报错,应该是循环引用导致的
|
||||||
import '@/plugin/utils';
|
import '@/plugin/utils';
|
||||||
import { Focus, GameUi, UiController } from './main/custom/ui';
|
import * as LegacyUI from '@motajs/legacy-ui';
|
||||||
import { GameStorage } from './main/storage';
|
import * as LegacySystem from '@motajs/legacy-system';
|
||||||
import './main/init/';
|
import './main/init/';
|
||||||
import './main/custom/toolbar';
|
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 { KeyCode } from '@motajs/client-base';
|
||||||
import '@/plugin';
|
import '@/plugin';
|
||||||
import './package';
|
import * as System from '@motajs/system';
|
||||||
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 { UI } from '@motajs/legacy-ui';
|
import { UI } from '@motajs/legacy-ui';
|
||||||
import Box from '@/components/box.vue';
|
import Box from '@/components/box.vue';
|
||||||
import BoxAnimate from '@/components/boxAnimate.vue';
|
import BoxAnimate from '@/components/boxAnimate.vue';
|
||||||
@ -41,47 +18,20 @@ import EnemySpecial from '@/panel/enemySpecial.vue';
|
|||||||
import EnemyTarget from '@/panel/enemyTarget.vue';
|
import EnemyTarget from '@/panel/enemyTarget.vue';
|
||||||
import KeyboardPanel from '@/panel/keyboard.vue';
|
import KeyboardPanel from '@/panel/keyboard.vue';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { Danmaku } from './main/custom/danmaku';
|
|
||||||
import * as Shadow from './fx/shadow';
|
import * as Shadow from './fx/shadow';
|
||||||
import { Render } from '@motajs/client';
|
import { Render } from '@motajs/client';
|
||||||
import { HeroKeyMover } from './main/action/move';
|
import { HeroKeyMover } from '../module/action/move';
|
||||||
import * as Animation from 'mutate-animate';
|
import * as Animation from 'mutate-animate';
|
||||||
import '@/module';
|
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', 'KeyCode', KeyCode);
|
||||||
Mota.register('var', 'settingStorage', settingStorage);
|
|
||||||
Mota.register('var', 'status', status);
|
Mota.register('var', 'status', status);
|
||||||
Mota.register('var', 'logger', logger);
|
Mota.register('var', 'logger', logger);
|
||||||
// ----- 模块注册
|
// ----- 模块注册
|
||||||
Mota.register('module', 'CustomComponents', {
|
Mota.register('module', 'LegacyUI', LegacyUI);
|
||||||
createSettingComponents,
|
Mota.register('module', 'System', System);
|
||||||
createToolbarComponents,
|
Mota.register('module', 'LegacySystem', LegacySystem);
|
||||||
createToolbarEditorComponents
|
|
||||||
});
|
|
||||||
Mota.register('module', 'MiscComponents', {
|
|
||||||
VirtualKey
|
|
||||||
});
|
|
||||||
Mota.register('module', 'RenderUtils', utils);
|
Mota.register('module', 'RenderUtils', utils);
|
||||||
Mota.register('module', 'UI', UI);
|
Mota.register('module', 'UI', UI);
|
||||||
Mota.register('module', 'UIComponents', {
|
Mota.register('module', 'UIComponents', {
|
||||||
|
@ -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>>;
|
|
@ -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 指定包含这个杂项工具的工具栏刷新,例如填drag,则只会刷新包含drag杂项工具的工具栏
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
});
|
|
@ -1,3 +0,0 @@
|
|||||||
import './fixed';
|
|
||||||
import './keyboard';
|
|
||||||
import './hotkey';
|
|
File diff suppressed because it is too large
Load Diff
@ -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';
|
|
||||||
});
|
|
||||||
});
|
|
@ -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();
|
|
||||||
});
|
|
@ -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);
|
|
@ -1,7 +1,6 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import { cloneDeep, isNil } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { ItemState } from './item';
|
|
||||||
import { HeroSkill, NightSpecial } from '../mechanism/misc';
|
import { HeroSkill, NightSpecial } from '../mechanism/misc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -343,122 +342,3 @@ interface IHeroItem {
|
|||||||
*/
|
*/
|
||||||
hasItem(item: AllIdsOf<'items'>): boolean;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import type { Hero } from './hero';
|
|
||||||
import { GameState, gameStates } from './state';
|
|
||||||
import { loading } from '../game';
|
import { loading } from '../game';
|
||||||
|
|
||||||
type EffectFn = (state: GameState, hero: Hero<any>) => void;
|
type EffectFn = () => void;
|
||||||
type CanUseEffectFn = (state: GameState, hero: Hero<any>) => boolean;
|
type CanUseEffectFn = () => boolean;
|
||||||
|
|
||||||
interface ItemStateEvent {
|
interface ItemStateEvent {
|
||||||
use: [hero: Hero<any>];
|
use: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ItemState<
|
export class ItemState<
|
||||||
@ -56,7 +54,6 @@ export class ItemState<
|
|||||||
this.canUseItemEffect = item.canUseItemEffect;
|
this.canUseItemEffect = item.canUseItemEffect;
|
||||||
|
|
||||||
this.compileFunction();
|
this.compileFunction();
|
||||||
this.compileEvent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private compileFunction() {
|
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
|
* @param id 要获取的道具id
|
||||||
|
@ -11,7 +11,7 @@ import type {
|
|||||||
LayerMovingRenderable,
|
LayerMovingRenderable,
|
||||||
LayerFloorBinder
|
LayerFloorBinder
|
||||||
} from '@motajs/render';
|
} 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 { BluePalace, MiscData } from '../mechanism/misc';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ export class BlockMover extends ObjectMoverBase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onMoveStart(controller: IMoveController): Promise<void> {
|
protected async onMoveStart(_controller: IMoveController): Promise<void> {
|
||||||
const adapter = BlockMover.adapter;
|
const adapter = BlockMover.adapter;
|
||||||
if (adapter) {
|
if (adapter) {
|
||||||
const list = adapter.items;
|
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) {
|
if (this.renderable) {
|
||||||
this.layerItems.forEach(v => {
|
this.layerItems.forEach(v => {
|
||||||
v.moving.delete(this.renderable!);
|
v.moving.delete(this.renderable!);
|
||||||
@ -349,7 +349,7 @@ export class BlockMover extends ObjectMoverBase {
|
|||||||
|
|
||||||
protected async onStepStart(
|
protected async onStepStart(
|
||||||
step: MoveStepDir,
|
step: MoveStepDir,
|
||||||
controller: IMoveController
|
_controller: IMoveController
|
||||||
): Promise<BlockMoveCode> {
|
): Promise<BlockMoveCode> {
|
||||||
await this.moveAnimate(step);
|
await this.moveAnimate(step);
|
||||||
const { x: dx, y: dy } = core.utils.scan2[this.moveDir];
|
const { x: dx, y: dy } = core.utils.scan2[this.moveDir];
|
||||||
@ -360,17 +360,17 @@ export class BlockMover extends ObjectMoverBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async onStepEnd(
|
protected async onStepEnd(
|
||||||
step: MoveStepDir,
|
_step: MoveStepDir,
|
||||||
code: BlockMoveCode,
|
_code: BlockMoveCode,
|
||||||
controller: IMoveController
|
_controller: IMoveController
|
||||||
): Promise<void> {}
|
): Promise<void> {}
|
||||||
|
|
||||||
protected onSetMoveSpeed(
|
protected onSetMoveSpeed(
|
||||||
speed: number,
|
_speed: number,
|
||||||
controller: IMoveController
|
_controller: IMoveController
|
||||||
): void {}
|
): void {}
|
||||||
|
|
||||||
private moveAnimate(step: MoveStepDir) {
|
private moveAnimate(_step: MoveStepDir) {
|
||||||
const layer = this.layerItems[0];
|
const layer = this.layerItems[0];
|
||||||
if (!layer) return;
|
if (!layer) return;
|
||||||
if (!this.renderable) return;
|
if (!this.renderable) return;
|
||||||
@ -521,7 +521,7 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async onStepStart(
|
protected async onStepStart(
|
||||||
step: MoveStepDir,
|
_step: MoveStepDir,
|
||||||
controller: IMoveController
|
controller: IMoveController
|
||||||
): Promise<HeroMoveCode> {
|
): Promise<HeroMoveCode> {
|
||||||
const showDir = toDir(this.faceDir);
|
const showDir = toDir(this.faceDir);
|
||||||
@ -594,7 +594,7 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async onStepEnd(
|
protected async onStepEnd(
|
||||||
step: MoveStepDir,
|
_step: MoveStepDir,
|
||||||
code: HeroMoveCode,
|
code: HeroMoveCode,
|
||||||
controller: IMoveController
|
controller: IMoveController
|
||||||
): Promise<void> {
|
): 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;
|
const adapter = HeroMover.adapter;
|
||||||
if (!adapter) return;
|
if (!adapter) return;
|
||||||
adapter.sync('setMoveSpeed', speed);
|
adapter.sync('setMoveSpeed', speed);
|
||||||
@ -785,7 +788,7 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
const list = adapter.items;
|
const list = adapter.items;
|
||||||
const { x: tx, y: ty, dir: toDir } = data;
|
const { x: tx, y: ty, dir: toDir } = data;
|
||||||
const { x, y, direction } = core.status.hero.loc;
|
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 { x: tdx } = core.utils.scan[toDir];
|
||||||
|
|
||||||
const promises = [...list].map(v => {
|
const promises = [...list].map(v => {
|
||||||
|
@ -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();
|
|
@ -23,7 +23,7 @@ import type { Danmaku } from '@/core/main/custom/danmaku';
|
|||||||
import type * as misc from './mechanism/misc';
|
import type * as misc from './mechanism/misc';
|
||||||
import type { Render } from '@motajs/client';
|
import type { Render } from '@motajs/client';
|
||||||
import type { ItemState } from './state/item';
|
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 { BlockMover, HeroMover, ObjectMoverBase } from './state/move';
|
||||||
import type * as Animation from 'mutate-animate';
|
import type * as Animation from 'mutate-animate';
|
||||||
import type { WeatherController } from '@/module/weather/weather';
|
import type { WeatherController } from '@/module/weather/weather';
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { KeyCode } from '@motajs/client-base';
|
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 type { HeroMover, IMoveController } from '@/game/state/move';
|
||||||
import { Ticker } from 'mutate-animate';
|
import { Ticker } from 'mutate-animate';
|
||||||
import { mainScope } from '../init/hotkey';
|
import { mainScope } from '@motajs/legacy-ui';
|
||||||
|
|
||||||
type MoveKey = Record<Dir, HotkeyData>;
|
type MoveKey = Record<Dir, HotkeyData>;
|
||||||
type MoveKeyConfig = Record<Dir, string>;
|
type MoveKeyConfig = Record<Dir, string>;
|
@ -1,6 +1,6 @@
|
|||||||
import { Patch, PatchClass } from '@motajs/legacy-common';
|
import { Patch, PatchClass } from '@motajs/legacy-common';
|
||||||
import { audioPlayer, bgmController, soundPlayer } from '../audio';
|
import { audioPlayer, bgmController, soundPlayer } from '../audio';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '@motajs/legacy-ui';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ import {
|
|||||||
SetupComponentOptions,
|
SetupComponentOptions,
|
||||||
waitbox
|
waitbox
|
||||||
} from '../components';
|
} from '../components';
|
||||||
import { mainUi } from '@/core/main/init/ui';
|
import { mainUi } from '@motajs/legacy-ui';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { generateKeyboardEvent } from '@/core/main/custom/keyboard';
|
import { generateKeyboardEvent } from '@motajs/system-action';
|
||||||
import { getVitualKeyOnce } from '@motajs/legacy-ui';
|
import { getVitualKeyOnce } from '@motajs/legacy-ui';
|
||||||
import { getAllSavesData, getSaveData } from '@/module/utils';
|
import { getAllSavesData, getSaveData } from '@/module/utils';
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ import {
|
|||||||
getVitualKeyOnce,
|
getVitualKeyOnce,
|
||||||
openDanmakuPoster
|
openDanmakuPoster
|
||||||
} from '@motajs/legacy-ui';
|
} from '@motajs/legacy-ui';
|
||||||
import { gameKey } from '@/core/main/custom/hotkey';
|
import { gameKey } from '@motajs/system-action';
|
||||||
import { generateKeyboardEvent } from '@/core/main/custom/keyboard';
|
import { generateKeyboardEvent } from '@motajs/system-action';
|
||||||
import { transitioned } from '../use';
|
import { transitioned } from '../use';
|
||||||
import { linear } from 'mutate-animate';
|
import { linear } from 'mutate-animate';
|
||||||
import { KeyCode } from '@motajs/client-base';
|
import { KeyCode } from '@motajs/client-base';
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
"@motajs/render": "workspace:*",
|
"@motajs/render": "workspace:*",
|
||||||
"@motajs/system": "workspace:*",
|
"@motajs/system": "workspace:*",
|
||||||
"@motajs/system-ui": "workspace:*",
|
"@motajs/system-ui": "workspace:*",
|
||||||
|
"@motajs/system-action": "workspace:*",
|
||||||
"@motajs/legacy-common": "workspace:*",
|
"@motajs/legacy-common": "workspace:*",
|
||||||
"@motajs/legacy-client": "workspace:*",
|
"@motajs/legacy-client": "workspace:*",
|
||||||
"@motajs/legacy-data": "workspace:*",
|
"@motajs/legacy-data": "workspace:*",
|
||||||
"@motajs/legacy-ui": "workspace:*"
|
"@motajs/legacy-ui": "workspace:*",
|
||||||
|
"@motajs/legacy-system": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { MotaOffscreenCanvas2D } from '@motajs/render';
|
import { MotaOffscreenCanvas2D } from '@motajs/render';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '@motajs/legacy-ui';
|
||||||
import {
|
import {
|
||||||
LayerGroupFloorBinder,
|
LayerGroupFloorBinder,
|
||||||
ILayerGroupRenderExtends,
|
ILayerGroupRenderExtends,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { mainSetting } from '@/core/main/setting';
|
import { mainSetting } from '@motajs/legacy-ui';
|
||||||
import {
|
import {
|
||||||
Damage,
|
Damage,
|
||||||
DamageRenderable,
|
DamageRenderable,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { MotaOffscreenCanvas2D } from '@motajs/render';
|
import { MotaOffscreenCanvas2D } from '@motajs/render';
|
||||||
import { mainSetting, MotaSettingItem } from '@/core/main/setting';
|
import { mainSetting, MotaSettingItem } from '@motajs/legacy-ui';
|
||||||
import {
|
import {
|
||||||
LayerGroupFloorBinder,
|
LayerGroupFloorBinder,
|
||||||
ILayerGroupRenderExtends,
|
ILayerGroupRenderExtends,
|
||||||
|
Loading…
Reference in New Issue
Block a user