重构设置模块

This commit is contained in:
unanmed 2023-08-05 12:12:02 +08:00
parent b7f0d6f4d8
commit 4d4aeed3cd
14 changed files with 603 additions and 227 deletions

2
components.d.ts vendored
View File

@ -7,7 +7,9 @@ export {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
AButton: typeof import('ant-design-vue/es')['Button']
ADivider: typeof import('ant-design-vue/es')['Divider'] ADivider: typeof import('ant-design-vue/es')['Divider']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AProgress: typeof import('ant-design-vue/es')['Progress'] AProgress: typeof import('ant-design-vue/es')['Progress']
ASelect: typeof import('ant-design-vue/es')['Select'] ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption']

View File

@ -415,10 +415,7 @@ main.prototype.loadAsync = async function (mode, callback) {
// 自动放缩最大化 // 自动放缩最大化
let auto = core.getLocalStorage('autoScale'); let auto = core.getLocalStorage('autoScale');
if (auto == null) {
core.setLocalStorage('autoScale', true);
auto = true;
}
if (auto && !core.domStyle.isVertical) { if (auto && !core.domStyle.isVertical) {
try { try {
core.plugin.utils.maxGameScale(); core.plugin.utils.maxGameScale();

View File

@ -51,11 +51,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
else core.showStatusBar(); else core.showStatusBar();
if (main.mode === 'play' && !main.replayChecking) { if (main.mode === 'play' && !main.replayChecking) {
ancTe.plugin.fly.splitArea(); ancTe.plugin.fly.splitArea();
ancTe.plugin.setting.resetFlagSettings(); ancTe.game.hook.emit('reset');
} else { } else {
flags.autoSkill ??= true; flags.autoSkill ??= true;
flags.itemDetail ??= true;
flags.autoLocate ??= true;
} }
}, },
win: function (reason, norank, noexit) { win: function (reason, norank, noexit) {

View File

@ -18,7 +18,6 @@ import { uiStack } from './plugin/uiController';
display: flex; display: flex;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
font-size: 16px;
} }
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {

View File

@ -1,7 +1,9 @@
import { BgmController } from './audio/bgm'; import { BgmController } from './audio/bgm';
import { SoundController } from './audio/sound'; import { SoundController } from './audio/sound';
import { EventEmitter } from './common/eventEmitter';
import { loading, readyAllResource } from './loader/load'; import { loading, readyAllResource } from './loader/load';
import { ResourceStore, ResourceType } from './loader/resource'; import { ResourceStore, ResourceType } from './loader/resource';
import { GameEvent } from './main/game';
import { resolvePlugin } from './plugin'; import { resolvePlugin } from './plugin';
interface AncTePlugin { interface AncTePlugin {
@ -38,6 +40,9 @@ export interface AncTe {
zipResource: ResourceStore<'zip'>; zipResource: ResourceStore<'zip'>;
bgm: BgmController; bgm: BgmController;
plugin: AncTePlugin; plugin: AncTePlugin;
game: {
hook: EventEmitter<GameEvent>;
};
} }
function ready() { function ready() {

9
src/core/main/game.ts Normal file
View File

@ -0,0 +1,9 @@
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
export interface GameEvent extends EmitableEvent {
reset: () => void;
}
export const hook = new EventEmitter<GameEvent>();
ancTe.game.hook = hook;

View File

@ -1,12 +1,17 @@
import { reactive } from 'vue';
import { EmitableEvent, EventEmitter } from '../common/eventEmitter'; import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
import { transition } from '../../plugin/uiController';
import { loading } from '../loader/load';
import { hook } from './game';
type MotaSettingType = boolean | number | MotaSetting; type MotaSettingType = boolean | number | MotaSetting;
interface MotaSettingItem<T extends MotaSettingType = MotaSettingType> { export interface MotaSettingItem<T extends MotaSettingType = MotaSettingType> {
name: string; name: string;
key: string;
value: T; value: T;
defaults?: boolean | number; defaults?: boolean | number;
step?: number; step?: [number, number, number];
display?: (value: T) => string; display?: (value: T) => string;
special?: string; special?: string;
} }
@ -19,8 +24,28 @@ interface SettingEvent extends EmitableEvent {
) => void; ) => void;
} }
class MotaSetting extends EventEmitter<SettingEvent> { export type SettingText = {
private list: Record<string, MotaSettingItem> = {}; [key: string]: string[] | SettingText;
};
export interface SettingDisplayInfo {
item: MotaSettingItem | null;
list: Record<string, MotaSettingItem>;
text: string[];
}
export class MotaSetting extends EventEmitter<SettingEvent> {
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);
}
}
/** /**
* *
@ -36,7 +61,12 @@ class MotaSetting extends EventEmitter<SettingEvent> {
* @param key * @param key
* @param value * @param value
*/ */
register(key: string, name: string, value: number, step?: number): this; register(
key: string,
name: string,
value: number,
step?: [number, number, number]
): this;
/** /**
* *
* @param key * @param key
@ -47,11 +77,12 @@ class MotaSetting extends EventEmitter<SettingEvent> {
key: string, key: string,
name: string, name: string,
value: MotaSettingType, value: MotaSettingType,
step: number = 1 step: [number, number, number] = [0, 100, 1]
) { ) {
const setting: MotaSettingItem = { const setting: MotaSettingItem = {
name, name,
value value,
key
}; };
if (!(value instanceof MotaSetting)) setting.defaults = value; if (!(value instanceof MotaSetting)) setting.defaults = value;
if (typeof value === 'number') setting.step = step; if (typeof value === 'number') setting.step = step;
@ -83,7 +114,7 @@ class MotaSetting extends EventEmitter<SettingEvent> {
} }
const old = setting.value as boolean | number; const old = setting.value as boolean | number;
setting.value = value; setting.value = value;
this.emit('valueChange', key, old, value); this.emit('valueChange', key, value, old);
} }
/** /**
@ -112,13 +143,14 @@ class MotaSetting extends EventEmitter<SettingEvent> {
setDisplayFunc(key: string, func: (value: MotaSettingType) => string) { setDisplayFunc(key: string, func: (value: MotaSettingType) => string) {
const setting = this.getSettingBy(key.split('.')); const setting = this.getSettingBy(key.split('.'));
setting.display = func; setting.display = func;
return this;
} }
private getSettingBy(list: string[]) { private getSettingBy(list: string[]) {
let now: MotaSetting = this; let now: MotaSetting = this;
for (let i = 0; i < list.length - 1; i++) { for (let i = 0; i < list.length - 1; i++) {
const item = now.list[list[i]]; const item = now.list[list[i]].value;
if (!(item instanceof MotaSetting)) { if (!(item instanceof MotaSetting)) {
throw new Error( throw new Error(
`Cannot get setting. The parent isn't a MotaSetting instance.` + `Cannot get setting. The parent isn't a MotaSetting instance.` +
@ -132,8 +164,203 @@ class MotaSetting extends EventEmitter<SettingEvent> {
} }
} }
interface SettingDisplayerEvent extends EmitableEvent {
update: (stack: string[], display: SettingDisplayInfo[]) => void;
}
export class SettingDisplayer extends EventEmitter<SettingDisplayerEvent> {
setting: MotaSetting;
textInfo: SettingText;
/** 选项选中栈 */
selectStack: string[] = [];
displayInfo: SettingDisplayInfo[] = reactive([]);
constructor(setting: MotaSetting, textInfo: SettingText) {
super();
this.setting = setting;
this.textInfo = textInfo;
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;
let nowText: string[] | SettingText = this.textInfo;
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;
if (nowText && !(nowText instanceof Array))
nowText = nowText[list[i]];
}
if (nowText && !(nowText instanceof Array))
nowText = nowText[list.at(-1)!];
const last = now.list[list.at(-1)!];
if (last) {
this.displayInfo.push({
item: last,
text: nowText instanceof Array ? nowText : ['请选择设置'],
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(); export const mainSetting = new MotaSetting();
// ----- 监听设置修改
mainSetting.on('valueChange', (key, n, o) => {
const [root, setting] = key.split('.');
if (root === 'screen') {
handleScreenSetting(setting, n, o);
} else if (root === 'action') {
handleActionSetting(setting, n, o);
} else if (root === 'utils') {
handleUtilsSetting(setting, n, o);
}
});
export async function triggerFullscreen(full: boolean) {
const { maxGameScale } = core.plugin.utils;
if (!!document.fullscreenElement && !full) {
await document.exitFullscreen();
requestAnimationFrame(() => {
maxGameScale(1);
});
}
if (full && !document.fullscreenElement) {
await document.body.requestFullscreen();
requestAnimationFrame(() => {
maxGameScale();
});
}
}
const root = document.getElementById('root') as HTMLDivElement;
const root2 = document.getElementById('root2') as HTMLDivElement;
function handleScreenSetting<T extends number | boolean>(
key: string,
n: T,
o: T
) {
if (n === o) return;
if (key === 'fullscreen') {
// 全屏
triggerFullscreen(n as boolean);
} else if (key === 'halo') {
// 光环
core.setLocalStorage('showHalo', n);
} else if (key === 'frag') {
// 打怪特效
core.setLocalStorage('frag', n);
} else if (key === 'itemDetail') {
// 宝石血瓶显伤
core.setLocalStorage('itemDetail', n);
} else if (key === 'transition') {
// 界面动画
core.setLocalStorage('transition', n);
transition.value = n as boolean;
} else if (key === 'antiAlias') {
// 抗锯齿
core.setLocalStorage('antiAlias', n);
for (const canvas of core.dom.gameCanvas) {
if (core.domStyle.hdCanvas.includes(canvas.id)) continue;
if (n) {
canvas.classList.remove('no-anti-aliasing');
} else {
canvas.classList.add('no-anti-aliasing');
}
}
} else if (key === 'autoScale') {
// 自动放缩
core.setLocalStorage('autoScale', n);
} else if (key === 'fontSize') {
// 字体大小
core.setLocalStorage('fontSize', n);
root.style.fontSize = root2.style.fontSize = `${n}px`;
}
}
function handleActionSetting<T extends number | boolean>(
key: string,
n: T,
o: T
) {
if (n === o) return;
if (key === 'autoSkill') {
// 自动切换技能
flags.autoSkill = n;
}
if (key === 'fixed') {
// 定点查看
core.setLocalStorage('fixed', n);
}
}
function handleUtilsSetting<T extends number | boolean>(
key: string,
n: T,
o: T
) {
if (n === o) return;
if (key === 'betterLoad') {
// 加载优化
core.setLocalStorage('betterLoad', n);
}
}
// ----- 游戏的所有设置项 // ----- 游戏的所有设置项
mainSetting mainSetting
.register( .register(
@ -147,7 +374,7 @@ mainSetting
.register('transition', '界面动画', false) .register('transition', '界面动画', false)
.register('antiAlias', '抗锯齿', false) .register('antiAlias', '抗锯齿', false)
.register('autoScale', '自动放缩', true) .register('autoScale', '自动放缩', true)
.register('fontSize', '字体大小', 16, 1) .register('fontSize', '字体大小', 16, [8, 28, 1])
) )
.register( .register(
'action', 'action',
@ -157,9 +384,31 @@ mainSetting
.register('fixed', '定点查看', true) .register('fixed', '定点查看', true)
.register('hotkey', '快捷键', false) .register('hotkey', '快捷键', false)
.markSpecial('hotkey', 'hotkey') .markSpecial('hotkey', 'hotkey')
.setDisplayFunc('hotkey', () => '')
) )
.register( .register(
'utils', 'utils',
'功能设置', '功能设置',
new MotaSetting().register('betterLoad', '优化加载', true) new MotaSetting().register('betterLoad', '优化加载', true)
); );
loading.once('coreInit', () => {
mainSetting.reset({
'screen.fullscreen': false,
'screen.halo': !!core.getLocalStorage('showHalo', true),
'screen.frag': !!core.getLocalStorage('frag', true),
'screen.itemDetail': !!core.getLocalStorage('itemDetail', true),
'screen.transition': !!core.getLocalStorage('transition', false),
'screen.antiAlias': !!core.getLocalStorage('antiAlias', false),
'screen.autoScale': !!core.getLocalStorage('autoScale', true),
'screen.fontSize': core.getLocalStorage('fontSize', 16),
'action.fixed': !!core.getLocalStorage('fixed', true),
'utils.betterLoad': !!core.getLocalStorage('betterLoad', true)
});
});
hook.once('reset', () => {
mainSetting.reset({
'action.autoSkill': flags.autoSkill ?? true
});
});

View File

@ -1,32 +1,20 @@
{ {
"fullscreen": { "screen": {
"text": "全屏游戏", "fullscreen": [
"desc": [
"是否全屏进行游戏全屏后按ESC退出全屏不能开启系统设置菜单请按下方的按钮打开。", "是否全屏进行游戏全屏后按ESC退出全屏不能开启系统设置菜单请按下方的按钮打开。",
"进入或退出全屏后请存读档一下,以解决一部分绘制问题。" "进入或退出全屏后请存读档一下,以解决一部分绘制问题。"
] ],
}, "halo": ["开启后,会在地图上显示范围光环。"],
"transition": { "frag": ["开启后,在打败怪物后会触发怪物碎裂特效。"],
"text": "界面动画", "itemDetail": ["是否在地图上显示宝石血瓶装备等增加的属性值"],
"desc": [ "transition": [
"是否展示当一个ui界面如怪物手册等的打开与关闭时的动画。当此项开启时", "是否展示当一个ui界面如怪物手册等的打开与关闭时的动画。当此项开启时",
"所有界面被打开或关闭时都会展示动画,否则会直接展示出来" "所有界面被打开或关闭时都会展示动画,否则会直接展示出来"
] ],
}, "antiAlias": [
"itemDetail": { "是否开启抗锯齿。开启后,画面会变得不那么锐利,观感更加舒适;关闭后,可以更好地展现出像素感,同时部分像素错误也不会出现。"
"text": "宝石血瓶显伤", ],
"desc": ["是否在地图上显示宝石血瓶装备等增加的属性值"] "autoScale": [
},
"autoSkill": {
"text": "自动切换技能",
"desc": [
"开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,",
"临界也会考虑技能在内"
]
},
"autoScale": {
"text": "自动放缩",
"desc": [
"开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。", "开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。",
"<br>", "<br>",
"<br>", "<br>",
@ -35,46 +23,26 @@
"1. 首先尝试缩放至最大缩放比例", "1. 首先尝试缩放至最大缩放比例",
"<br>", "<br>",
"2. 如果缩放后游戏画面高度高于页面高度的95%,那么缩小一个缩放比例,否则保持最大比例" "2. 如果缩放后游戏画面高度高于页面高度的95%,那么缩小一个缩放比例,否则保持最大比例"
] ],
"fontSize": ["在各种 ui 界面中显示的文字大小,范围为 8 - 28"]
}, },
"showHalo": { "action": {
"text": "展示范围光环", "autoSkill": [
"desc": ["开启后,会在地图上显示范围光环。"] "开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,",
}, "临界也会考虑技能在内"
"useFixed": { ],
"text": "移动鼠标显示怪物信息", "fixed": [
"desc": [
"开启后,当鼠标移动到怪物上时,会以盒子的形式展示该点的怪物信息。手机端此功能无效。", "开启后,当鼠标移动到怪物上时,会以盒子的形式展示该点的怪物信息。手机端此功能无效。",
"<br>", "<br>",
"<br>", "<br>",
"注当鼠标移动到怪物上时经过200毫秒才会显示信息防止误操作。" "注当鼠标移动到怪物上时经过200毫秒才会显示信息防止误操作。"
] ],
"hotkey": ["设置游戏中会用到的一些快捷键"]
}, },
"autoLocate": { "utils": {
"text": "自动勇士定位", "betterLoad": [
"desc": [ "<span style=\"color: yellow; font-weight: 700\">试验性功能</span>",
"此项会在进入第二章后会起作用。开启后,当勇士处于不同位置打同一个怪物伤害不同时,在地图上使用绿色箭头标出伤害最低的位置,",
"其余方向,伤害越高,箭头颜色越红,同时在自动寻路中选择可以到达的伤害最低的位置。",
"<br>", "<br>",
"<br>",
"注:如果出现明显卡顿现象可以考虑关闭本设置或自动切换技能设置。"
]
},
"antiAliasing": {
"text": "抗锯齿",
"desc": [
"是否开启抗锯齿。开启后,画面会变得不那么锐利,观感更加舒适;关闭后,可以更好地展现出像素感,同时部分像素错误也不会出现。"
]
},
"showStudied": {
"text": "展示已学习技能",
"desc": [
"开启后,会在画面内以类似状态栏的盒子的形式显示当前已学习的怪物技能。"
]
},
"betterLoad": {
"text": "优化加载",
"desc": [
"开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。", "开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。",
"该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致楼层转换时间明显变长。", "该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致楼层转换时间明显变长。",
"<br>", "<br>",

View File

@ -169,7 +169,7 @@ core.events.afterBattle = function (
else core.clearContinueAutomaticRoute(); else core.clearContinueAutomaticRoute();
// 打怪特效 // 打怪特效
if (has(x) && has(y)) { if (core.getLocalStorage('frag') && has(x) && has(y)) {
const frame = core.status.globalAnimateStatus % 2; const frame = core.status.globalAnimateStatus % 2;
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.width = 32; canvas.width = 32;

View File

@ -31,7 +31,7 @@ core.control.updateDamage = function (floorId = core.status.floorId, ctx) {
// 获取宝石信息 并绘制 // 获取宝石信息 并绘制
function getItemDetail(floorId: FloorIds, onMap: boolean) { function getItemDetail(floorId: FloorIds, onMap: boolean) {
if (!core.getFlag('itemDetail')) return; if (!core.getLocalStorage('itemDetail')) return;
floorId ??= core.status.thisMap.floorId; floorId ??= core.status.thisMap.floorId;
let diff: Record<string | symbol, number | undefined> = {}; let diff: Record<string | symbol, number | undefined> = {};
const before = core.status.hero; const before = core.status.hero;

View File

@ -21,8 +21,16 @@ export let isMobile = matchMedia('(max-width: 600px)').matches;
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
isMobile = matchMedia('(max-width: 600px)').matches; isMobile = matchMedia('(max-width: 600px)').matches;
checkMobile();
}); });
}); });
checkMobile();
function checkMobile() {
if (isMobile) {
alert('手机端建议使用自带的浏览器进行游玩,并在进入游戏后开启全屏游玩');
}
}
/** /**
* *

View File

@ -12,6 +12,11 @@
transition: all 0.6s linear; transition: all 0.6s linear;
opacity: 0; opacity: 0;
background-color: #000d; background-color: #000d;
font-size: 16px;
}
#root2 {
font-size: 16px;
} }
.antdv-message { .antdv-message {

View File

@ -1,171 +1,307 @@
<template> <template>
<Column :width="60" :height="60" @close="exit" <div class="setting-main">
><template #left <div id="tools">
><div id="setting-list"> <span class="button-text" @click="exit"
<span ><left-outlined /> 返回游戏</span
class="selectable" >
:selected="selected === 'fullscreen'" </div>
@click="click('fullscreen')" <div class="setting-container">
>全屏游戏:&nbsp;&nbsp;&nbsp;{{ <div class="setting-select">
fullscreen ? 'ON' : 'OFF' <TransitionGroup name="list">
}}</span <div
> v-for="(info, i) of display"
<span :key="i"
class="selectable" class="setting-display"
:selected="selected === 'transition'" >
@click="click('transition')" <Scroll class="setting-scroll">
>界面动画:&nbsp;&nbsp;&nbsp;{{ <div class="setting-list">
transition ? 'ON' : 'OFF' <div
}}</span v-for="item of info.list"
> class="setting-item selectable"
<span :selected="item === info.item"
class="selectable" @click="click(item.key, i, item)"
:selected="selected === 'itemDetail'" >
@click="click('itemDetail')" <span>{{ item.name }}</span>
>宝石血瓶显伤:&nbsp;&nbsp;&nbsp;{{ <span
itemDetail ? 'ON' : 'OFF' :selected="item === info.item"
}}</span class="setting-cascade"
> v-if="isCascade(item)"
<span >
class="selectable" <RightOutlined />
:selected="selected === 'autoSkill'" </span>
@click="click('autoSkill')" <span v-else class="setting-value">
>自动切换技能:&nbsp;&nbsp;&nbsp;{{ {{ getItemValue(item) }}
autoSkill ? 'ON' : 'OFF' </span>
}}</span </div>
> </div>
<span </Scroll>
class="selectable" <a-divider
:selected="selected === 'autoScale'" class="display-divider"
@click="click('autoScale')" type="vertical"
>自动放缩:&nbsp;&nbsp;&nbsp;{{ dashed
autoScale ? 'ON' : 'OFF' ></a-divider>
}}</span </div>
> </TransitionGroup>
<span </div>
class="selectable" <div class="setting-info">
:selected="selected === 'showHalo'" <div
@click="click('showHalo')" class="info-text"
>展示范围光环:&nbsp;&nbsp;&nbsp;{{ v-html="splitText(display.at(-1)?.text ?? ['请选择设置'])"
showHalo ? 'ON' : 'OFF' ></div>
}}</span <a-divider class="info-divider" dashed></a-divider>
> <div class="info-editor" v-if="!!selectedItem">
<span <div v-if="!!selectedItem.special"></div>
class="selectable" <div
:selected="selected === 'useFixed'" class="editor-number"
@click="click('useFixed')" v-else-if="typeof selectedItem.value === 'number'"
>移动鼠标显示怪物信息:&nbsp;&nbsp;&nbsp;{{ >
useFixed ? 'ON' : 'OFF' <span>修改设置</span>
}}</span <a-input-number
> class="number-input"
<!-- <span size="large"
class="selectable" :min="selectedItem.step?.[0] ?? 0"
:selected="selected === 'autoLocate'" :max="selectedItem.step?.[1] ?? 100"
@click="click('autoLocate')" :step="selectedItem.step?.[2] ?? 1"
>勇士自动定位:&nbsp;&nbsp;&nbsp;{{ :keyboard="true"
autoLocate ? 'ON' : 'OFF' :value="selectedItem.value"
}}</span @change="changeValue"
> --> ></a-input-number>
<span </div>
class="selectable" <div
:selected="selected === 'antiAliasing'" class="editor-boolean"
@click="click('antiAliasing')" v-else-if="typeof selectedItem.value === 'boolean'"
>抗锯齿:&nbsp;&nbsp;&nbsp;{{ >
antiAliasing ? 'ON' : 'OFF' <span
}}</span >当前{{
> selectedItem.value ? '开启' : '关闭'
<!-- <span }}</span
class="selectable" >
:selected="selected === 'showStudied'" <a-button
v-if="core.plugin.skillTree.getSkillLevel(11) > 0" class="boolean-button"
@click="click('showStudied')" type="primary"
>展示已学习技能:&nbsp;&nbsp;&nbsp;{{ size="large"
showStudied ? 'ON' : 'OFF' @click="changeValue(!selectedItem.value)"
}}</span >
> --> {{ selectedItem.value ? '关闭' : '开启' }}设置
</div></template </a-button>
> </div>
<template #right><span v-html="descText"></span></template </div>
></Column> </div>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue'; import { computed, shallowRef } from 'vue';
import { import {
transition, mainSetting,
itemDetail, MotaSetting,
autoSkill, MotaSettingItem,
autoScale, SettingDisplayer,
showStudied, SettingDisplayInfo,
showHalo, SettingText
useFixed, } from '../core/main/setting';
autoLocate, import settingText from '../data/settings.json';
antiAliasing, import { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';
fullscreen, import { splitText } from '../plugin/utils';
triggerFullscreen import Scroll from '../components/scroll.vue';
} from '../plugin/settings'; import { settingsOpened } from '../plugin/uiController';
import settingInfo from '../data/settings.json';
import { has, splitText } from '../plugin/utils';
import Column from '../components/colomn.vue';
type Settings = typeof settingInfo; const props = defineProps<{
info?: MotaSetting;
text?: SettingText;
}>();
const core = window.core; const setting = props.info ?? mainSetting;
const text = props.text ?? (settingText as SettingText);
const display = shallowRef<SettingDisplayInfo[]>([]);
const selectedItem = computed(() => display.value.at(-1)?.item);
const selected = ref<keyof Settings>('fullscreen'); const displayer = new SettingDisplayer(setting, text);
displayer.on('update', (stack, dis) => {
fullscreen.value = !!document.fullscreenElement; display.value = dis;
const descText = computed(() => {
return splitText(settingInfo[selected.value].desc);
}); });
display.value = displayer.displayInfo;
const settings: Record<keyof Settings, Ref<boolean>> = { function getItemValue(item: MotaSettingItem) {
transition, if (item.value instanceof MotaSetting) {
itemDetail, return '';
autoSkill, } else {
autoScale, if (item.display) {
showHalo, return item.display(item.value);
showStudied, } else {
useFixed, if (typeof item.value === 'number') {
autoLocate, return item.value.toString();
antiAliasing, } else {
fullscreen, return item.value ? 'ON' : 'OFF';
betterLoad: ref(false) }
}; }
}
const ignore: (keyof Settings)[] = ['fullscreen'];
function exit() {
ancTe.plugin.ui.settingsOpened.value = false;
} }
function click(id: keyof Settings) { function isCascade(item: MotaSettingItem) {
if (selected.value !== id) { return item.value instanceof MotaSetting;
selected.value = id; }
return;
} function click(key: string, index: number, item: MotaSettingItem) {
if (!ignore.includes(id)) { if (item.value instanceof MotaSetting) {
settings[id].value = !settings[id].value; if (index === display.value.length - 1) {
if (id === 'autoSkill') { displayer.add(key);
core.status.route.push(`set:autoSkill:${settings.autoSkill.value}`); } else {
if (displayer.selectStack.includes(key)) {
displayer.cut(index);
} else {
displayer.cut(index, true);
displayer.add(key);
}
} }
} else { } else {
if (id === 'fullscreen') { displayer.cut(index, true);
triggerFullscreen(); displayer.add(key);
}
} }
} }
function changeValue(value: number | boolean) {
setting.setValue(displayer.selectStack.join('.'), value);
displayer.update();
}
function exit() {
settingsOpened.value = false;
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
#setting-list { #tools {
display: flex; width: 100%;
flex-direction: column; font-family: 'normal';
font-size: 3.2vh;
height: 5vh;
position: fixed;
left: 10vw;
top: 10vh;
} }
.setting-item { .setting-main {
width: 100%; user-select: none;
padding: 1% 3% 1% 3%; display: flex;
flex-direction: column;
justify-content: center;
font-family: normal;
font-size: 150%;
.setting-container {
height: 60%;
display: flex;
flex-direction: row;
}
}
.setting-select {
display: flex;
flex-direction: row;
transition: all 0.5s ease;
}
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(-50px);
}
.list-leave-active {
position: absolute;
pointer-events: none;
}
.setting-display {
display: flex;
flex-direction: row;
height: 100%;
.setting-list {
display: flex;
flex-direction: column;
}
.display-divider {
border-color: #fff4;
height: auto;
}
.setting-item {
padding: 2%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.setting-cascade {
font-size: 75%;
margin-right: 40px;
transition: all 0.5s ease;
}
.setting-cascade[selected='true'] {
font-size: 90%;
margin-right: 10px;
color: cyan;
}
.setting-value {
margin-right: 10px;
color: rgb(242, 255, 101);
}
}
.setting-scroll {
width: 300px;
height: 100%;
}
.setting-info {
width: 400px;
.info-divider {
border-color: #fff4;
margin: 2% 0;
}
.editor-boolean {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
padding: 0 10% 0 5%;
.boolean-button {
font-size: 75%;
}
}
.info-text {
font-size: 85%;
min-height: 30%;
}
.editor-number {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
padding: 0 10% 0 5%;
.number-input {
font-size: 80%;
width: 40%;
}
}
} }
</style> </style>

View File

@ -68,7 +68,7 @@ import { Matrix4 } from '../plugin/webgl/matrix';
import { doByInterval, keycode } from '../plugin/utils'; import { doByInterval, keycode } from '../plugin/utils';
import { KeyCode } from '../plugin/keyCodes'; import { KeyCode } from '../plugin/keyCodes';
import { achievementOpened } from '../plugin/uiController'; import { achievementOpened } from '../plugin/uiController';
import { triggerFullscreen } from '../plugin/settings'; import { triggerFullscreen } from '../core/main/setting';
import { loading } from '../core/loader/load'; import { loading } from '../core/loader/load';
let startdiv: HTMLDivElement; let startdiv: HTMLDivElement;
@ -203,7 +203,7 @@ function bgm() {
async function setFullscreen() { async function setFullscreen() {
const index = toshow.length - toshow.indexOf(selected.value) - 1; const index = toshow.length - toshow.indexOf(selected.value) - 1;
await triggerFullscreen(); await triggerFullscreen(!fullscreen.value);
requestAnimationFrame(() => { requestAnimationFrame(() => {
fullscreen.value = !!document.fullscreenElement; fullscreen.value = !!document.fullscreenElement;
setCursor(buttons[index], index); setCursor(buttons[index], index);