From 4d4aeed3cde22123475008ff5db2e7b955d00cca Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sat, 5 Aug 2023 12:12:02 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=AE=BE=E7=BD=AE=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 2 + public/main.js | 5 +- public/project/functions.js | 4 +- src/App.vue | 1 - src/core/index.ts | 5 + src/core/main/game.ts | 9 + src/core/main/setting.ts | 269 ++++++++++++++++++- src/data/settings.json | 82 ++---- src/plugin/game/enemy/battle.ts | 2 +- src/plugin/game/fx/itemDetail.ts | 2 +- src/plugin/use.ts | 8 + src/styles.less | 5 + src/ui/settings.vue | 432 ++++++++++++++++++++----------- src/ui/start.vue | 4 +- 14 files changed, 603 insertions(+), 227 deletions(-) create mode 100644 src/core/main/game.ts diff --git a/components.d.ts b/components.d.ts index 46765fe..b7530c5 100644 --- a/components.d.ts +++ b/components.d.ts @@ -7,7 +7,9 @@ export {} declare module '@vue/runtime-core' { export interface GlobalComponents { + AButton: typeof import('ant-design-vue/es')['Button'] ADivider: typeof import('ant-design-vue/es')['Divider'] + AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] AProgress: typeof import('ant-design-vue/es')['Progress'] ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] diff --git a/public/main.js b/public/main.js index a2c9547..c3db4f0 100644 --- a/public/main.js +++ b/public/main.js @@ -415,10 +415,7 @@ main.prototype.loadAsync = async function (mode, callback) { // 自动放缩最大化 let auto = core.getLocalStorage('autoScale'); - if (auto == null) { - core.setLocalStorage('autoScale', true); - auto = true; - } + if (auto && !core.domStyle.isVertical) { try { core.plugin.utils.maxGameScale(); diff --git a/public/project/functions.js b/public/project/functions.js index a528ce6..9194834 100644 --- a/public/project/functions.js +++ b/public/project/functions.js @@ -51,11 +51,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { else core.showStatusBar(); if (main.mode === 'play' && !main.replayChecking) { ancTe.plugin.fly.splitArea(); - ancTe.plugin.setting.resetFlagSettings(); + ancTe.game.hook.emit('reset'); } else { flags.autoSkill ??= true; - flags.itemDetail ??= true; - flags.autoLocate ??= true; } }, win: function (reason, norank, noexit) { diff --git a/src/App.vue b/src/App.vue index 4955796..7644a4c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -18,7 +18,6 @@ import { uiStack } from './plugin/uiController'; display: flex; justify-content: center; overflow: hidden; - font-size: 16px; } @media screen and (max-width: 600px) { diff --git a/src/core/index.ts b/src/core/index.ts index 5e1ea8b..6f042b7 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,7 +1,9 @@ import { BgmController } from './audio/bgm'; import { SoundController } from './audio/sound'; +import { EventEmitter } from './common/eventEmitter'; import { loading, readyAllResource } from './loader/load'; import { ResourceStore, ResourceType } from './loader/resource'; +import { GameEvent } from './main/game'; import { resolvePlugin } from './plugin'; interface AncTePlugin { @@ -38,6 +40,9 @@ export interface AncTe { zipResource: ResourceStore<'zip'>; bgm: BgmController; plugin: AncTePlugin; + game: { + hook: EventEmitter; + }; } function ready() { diff --git a/src/core/main/game.ts b/src/core/main/game.ts new file mode 100644 index 0000000..fc00910 --- /dev/null +++ b/src/core/main/game.ts @@ -0,0 +1,9 @@ +import { EmitableEvent, EventEmitter } from '../common/eventEmitter'; + +export interface GameEvent extends EmitableEvent { + reset: () => void; +} + +export const hook = new EventEmitter(); + +ancTe.game.hook = hook; diff --git a/src/core/main/setting.ts b/src/core/main/setting.ts index 6705d98..70951a0 100644 --- a/src/core/main/setting.ts +++ b/src/core/main/setting.ts @@ -1,12 +1,17 @@ +import { reactive } from 'vue'; 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; -interface MotaSettingItem { +export interface MotaSettingItem { name: string; + key: string; value: T; defaults?: boolean | number; - step?: number; + step?: [number, number, number]; display?: (value: T) => string; special?: string; } @@ -19,8 +24,28 @@ interface SettingEvent extends EmitableEvent { ) => void; } -class MotaSetting extends EventEmitter { - private list: Record = {}; +export type SettingText = { + [key: string]: string[] | SettingText; +}; + +export interface SettingDisplayInfo { + item: MotaSettingItem | null; + list: Record; + text: string[]; +} + +export class MotaSetting extends EventEmitter { + readonly list: Record = {}; + + /** + * 重设设置 + * @param setting 设置信息 + */ + reset(setting: Record) { + for (const [key, value] of Object.entries(setting)) { + this.setValue(key, value); + } + } /** * 标记为特殊的设置项 @@ -36,7 +61,12 @@ class MotaSetting extends EventEmitter { * @param key 设置的键名 * @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 设置的键名 @@ -47,11 +77,12 @@ class MotaSetting extends EventEmitter { key: string, name: string, value: MotaSettingType, - step: number = 1 + step: [number, number, number] = [0, 100, 1] ) { const setting: MotaSettingItem = { name, - value + value, + key }; if (!(value instanceof MotaSetting)) setting.defaults = value; if (typeof value === 'number') setting.step = step; @@ -83,7 +114,7 @@ class MotaSetting extends EventEmitter { } const old = setting.value as boolean | number; setting.value = value; - this.emit('valueChange', key, old, value); + this.emit('valueChange', key, value, old); } /** @@ -112,13 +143,14 @@ class MotaSetting extends EventEmitter { setDisplayFunc(key: string, func: (value: MotaSettingType) => string) { const setting = this.getSettingBy(key.split('.')); setting.display = func; + 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]]; + const item = now.list[list[i]].value; if (!(item instanceof MotaSetting)) { throw new Error( `Cannot get setting. The parent isn't a MotaSetting instance.` + @@ -132,8 +164,203 @@ class MotaSetting extends EventEmitter { } } +interface SettingDisplayerEvent extends EmitableEvent { + update: (stack: string[], display: SettingDisplayInfo[]) => void; +} + +export class SettingDisplayer extends EventEmitter { + 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(); +// ----- 监听设置修改 +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( + 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( + 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( + key: string, + n: T, + o: T +) { + if (n === o) return; + + if (key === 'betterLoad') { + // 加载优化 + core.setLocalStorage('betterLoad', n); + } +} + // ----- 游戏的所有设置项 mainSetting .register( @@ -147,7 +374,7 @@ mainSetting .register('transition', '界面动画', false) .register('antiAlias', '抗锯齿', false) .register('autoScale', '自动放缩', true) - .register('fontSize', '字体大小', 16, 1) + .register('fontSize', '字体大小', 16, [8, 28, 1]) ) .register( 'action', @@ -157,9 +384,31 @@ mainSetting .register('fixed', '定点查看', true) .register('hotkey', '快捷键', false) .markSpecial('hotkey', 'hotkey') + .setDisplayFunc('hotkey', () => '') ) .register( 'utils', '功能设置', 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 + }); +}); diff --git a/src/data/settings.json b/src/data/settings.json index f8749fe..98c84ca 100644 --- a/src/data/settings.json +++ b/src/data/settings.json @@ -1,32 +1,20 @@ { - "fullscreen": { - "text": "全屏游戏", - "desc": [ + "screen": { + "fullscreen": [ "是否全屏进行游戏,全屏后按ESC退出全屏,不能开启系统设置菜单,请按下方的按钮打开。", "进入或退出全屏后请存读档一下,以解决一部分绘制问题。" - ] - }, - "transition": { - "text": "界面动画", - "desc": [ + ], + "halo": ["开启后,会在地图上显示范围光环。"], + "frag": ["开启后,在打败怪物后会触发怪物碎裂特效。"], + "itemDetail": ["是否在地图上显示宝石血瓶装备等增加的属性值"], + "transition": [ "是否展示当一个ui界面,如怪物手册等的打开与关闭时的动画。当此项开启时,", "所有界面被打开或关闭时都会展示动画,否则会直接展示出来" - ] - }, - "itemDetail": { - "text": "宝石血瓶显伤", - "desc": ["是否在地图上显示宝石血瓶装备等增加的属性值"] - }, - "autoSkill": { - "text": "自动切换技能", - "desc": [ - "开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,", - "临界也会考虑技能在内" - ] - }, - "autoScale": { - "text": "自动放缩", - "desc": [ + ], + "antiAlias": [ + "是否开启抗锯齿。开启后,画面会变得不那么锐利,观感更加舒适;关闭后,可以更好地展现出像素感,同时部分像素错误也不会出现。" + ], + "autoScale": [ "开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。", "
", "
", @@ -35,46 +23,26 @@ "1. 首先尝试缩放至最大缩放比例", "
", "2. 如果缩放后游戏画面高度高于页面高度的95%,那么缩小一个缩放比例,否则保持最大比例" - ] + ], + "fontSize": ["在各种 ui 界面中显示的文字大小,范围为 8 - 28"] }, - "showHalo": { - "text": "展示范围光环", - "desc": ["开启后,会在地图上显示范围光环。"] - }, - "useFixed": { - "text": "移动鼠标显示怪物信息", - "desc": [ + "action": { + "autoSkill": [ + "开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,", + "临界也会考虑技能在内" + ], + "fixed": [ "开启后,当鼠标移动到怪物上时,会以盒子的形式展示该点的怪物信息。手机端此功能无效。", "
", "
", "注:当鼠标移动到怪物上时,经过200毫秒才会显示信息,防止误操作。" - ] + ], + "hotkey": ["设置游戏中会用到的一些快捷键"] }, - "autoLocate": { - "text": "自动勇士定位", - "desc": [ - "此项会在进入第二章后会起作用。开启后,当勇士处于不同位置打同一个怪物伤害不同时,在地图上使用绿色箭头标出伤害最低的位置,", - "其余方向,伤害越高,箭头颜色越红,同时在自动寻路中选择可以到达的伤害最低的位置。", + "utils": { + "betterLoad": [ + "试验性功能", "
", - "
", - "注:如果出现明显卡顿现象可以考虑关闭本设置或自动切换技能设置。" - ] - }, - "antiAliasing": { - "text": "抗锯齿", - "desc": [ - "是否开启抗锯齿。开启后,画面会变得不那么锐利,观感更加舒适;关闭后,可以更好地展现出像素感,同时部分像素错误也不会出现。" - ] - }, - "showStudied": { - "text": "展示已学习技能", - "desc": [ - "开启后,会在画面内以类似状态栏的盒子的形式显示当前已学习的怪物技能。" - ] - }, - "betterLoad": { - "text": "优化加载", - "desc": [ "开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。", "该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致楼层转换时间明显变长。", "
", diff --git a/src/plugin/game/enemy/battle.ts b/src/plugin/game/enemy/battle.ts index 35134b6..8ce9a59 100644 --- a/src/plugin/game/enemy/battle.ts +++ b/src/plugin/game/enemy/battle.ts @@ -169,7 +169,7 @@ core.events.afterBattle = function ( else core.clearContinueAutomaticRoute(); // 打怪特效 - if (has(x) && has(y)) { + if (core.getLocalStorage('frag') && has(x) && has(y)) { const frame = core.status.globalAnimateStatus % 2; const canvas = document.createElement('canvas'); canvas.width = 32; diff --git a/src/plugin/game/fx/itemDetail.ts b/src/plugin/game/fx/itemDetail.ts index a98c6f4..febf160 100644 --- a/src/plugin/game/fx/itemDetail.ts +++ b/src/plugin/game/fx/itemDetail.ts @@ -31,7 +31,7 @@ core.control.updateDamage = function (floorId = core.status.floorId, ctx) { // 获取宝石信息 并绘制 function getItemDetail(floorId: FloorIds, onMap: boolean) { - if (!core.getFlag('itemDetail')) return; + if (!core.getLocalStorage('itemDetail')) return; floorId ??= core.status.thisMap.floorId; let diff: Record = {}; const before = core.status.hero; diff --git a/src/plugin/use.ts b/src/plugin/use.ts index f5f8863..63b1925 100644 --- a/src/plugin/use.ts +++ b/src/plugin/use.ts @@ -21,8 +21,16 @@ export let isMobile = matchMedia('(max-width: 600px)').matches; window.addEventListener('resize', () => { requestAnimationFrame(() => { isMobile = matchMedia('(max-width: 600px)').matches; + checkMobile(); }); }); +checkMobile(); + +function checkMobile() { + if (isMobile) { + alert('手机端建议使用自带的浏览器进行游玩,并在进入游戏后开启全屏游玩'); + } +} /** * 向一个元素添加拖拽事件 diff --git a/src/styles.less b/src/styles.less index 384e6f6..a66430d 100644 --- a/src/styles.less +++ b/src/styles.less @@ -12,6 +12,11 @@ transition: all 0.6s linear; opacity: 0; background-color: #000d; + font-size: 16px; +} + +#root2 { + font-size: 16px; } .antdv-message { diff --git a/src/ui/settings.vue b/src/ui/settings.vue index 66acf2c..9256eec 100644 --- a/src/ui/settings.vue +++ b/src/ui/settings.vue @@ -1,171 +1,307 @@ diff --git a/src/ui/start.vue b/src/ui/start.vue index 45eb3d4..3707b45 100644 --- a/src/ui/start.vue +++ b/src/ui/start.vue @@ -68,7 +68,7 @@ import { Matrix4 } from '../plugin/webgl/matrix'; import { doByInterval, keycode } from '../plugin/utils'; import { KeyCode } from '../plugin/keyCodes'; import { achievementOpened } from '../plugin/uiController'; -import { triggerFullscreen } from '../plugin/settings'; +import { triggerFullscreen } from '../core/main/setting'; import { loading } from '../core/loader/load'; let startdiv: HTMLDivElement; @@ -203,7 +203,7 @@ function bgm() { async function setFullscreen() { const index = toshow.length - toshow.indexOf(selected.value) - 1; - await triggerFullscreen(); + await triggerFullscreen(!fullscreen.value); requestAnimationFrame(() => { fullscreen.value = !!document.fullscreenElement; setCursor(buttons[index], index);