From 45c1d8c952b843f1e60d3069eecc0a978e3251d6 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 25 Jun 2025 23:43:19 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=83=A8=E5=88=86=20UI=20=E6=8D=A2?= =?UTF-8?q?=E6=88=90=E6=96=B0=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/render/components/choices.tsx | 125 +- .../src/render/legacy/shadow.ts | 14 - packages/legacy-ui/src/panel/enemySpecial.vue | 1 + packages/legacy-ui/src/ui/bookDetail.vue | 8 +- public/libs/control.js | 2 - public/libs/events.js | 108 +- public/libs/ui.js | 1738 ++--------------- src/types/declaration/control.d.ts | 1 + src/types/declaration/ui.d.ts | 2 +- 9 files changed, 264 insertions(+), 1735 deletions(-) diff --git a/packages-user/client-modules/src/render/components/choices.tsx b/packages-user/client-modules/src/render/components/choices.tsx index ad6b81d..7ef24bb 100644 --- a/packages-user/client-modules/src/render/components/choices.tsx +++ b/packages-user/client-modules/src/render/components/choices.tsx @@ -6,6 +6,7 @@ import { TextAlign } from './textboxTyper'; import { Page, PageExpose } from './page'; import { GameUI, IUIMountable, SetupComponentOptions } from '@motajs/system-ui'; import { useKey } from '../use'; +import { sleep } from 'mutate-animate'; export interface ConfirmBoxProps extends DefaultProps, TextContentProps { text: string; @@ -192,7 +193,7 @@ export const ConfirmBox = defineComponent< ); }, confirmBoxProps); -export type ChoiceKey = string | number | symbol; +export type ChoiceKey = string | number; export type ChoiceItem = [key: ChoiceKey, text: string]; export interface ChoicesProps extends DefaultProps, TextContentProps { @@ -211,6 +212,7 @@ export interface ChoicesProps extends DefaultProps, TextContentProps { titleFill?: CanvasStyle; pad?: number; interval?: number; + selected?: number; } export type ChoicesEmits = { @@ -233,7 +235,8 @@ const choicesProps = { 'titleFont', 'titleFill', 'pad', - 'interval' + 'interval', + 'selected' ], emits: ['choose'] } satisfies SetupComponentOptions< @@ -283,7 +286,7 @@ export const Choices = defineComponent< >((props, { emit, attrs }) => { const titleHeight = ref(0); const contentHeight = ref(0); - const selected = ref(0); + const selected = ref(props.selected ?? 0); const pageCom = ref(); const choiceSize = reactive<[number, number][]>([]); @@ -634,6 +637,122 @@ export function getChoice( }); } +function getChoiceRoute() { + const route = core.status.replay.toReplay[0]; + if (!route.startsWith('choices:')) { + return 0; + } else { + return Number(route.slice(8)); + } +} + +/** + * 弹出一个确认框,然后将确认结果返回,与 getConfirm 不同的是内置录像支持,如果这个选择框需要进录像, + * 需要使用此方法。例如给玩家弹出一个确认框,并获取玩家是否确认: + * ```ts + * const confirm = await routedConfirm( + * // 在哪个 UI 控制器上打开,对于一般 UI 组件来说,直接填写 props.controller 即可 + * props.controller, + * // 确认内容 + * '确认要 xxx 吗?', + * // 确认框的位置,宽度由下一个参数指定,高度参数由组件内部计算得出,指定无效 + * [240, 240, void 0, void 0, 0.5, 0.5], + * // 宽度设为 240 + * 240, + * // 可以给选择框传入其他的 props,例如指定字体,此项可选 + * { font: new Font('Verdana', 20) } + * ); + * // 之后,就可以直接判断 confirm 来执行不同的操作了 + * if (confirm) { ... } + * ``` + * @param controller UI 控制器 + * @param text 确认文本内容 + * @param loc 确认框的位置 + * @param width 确认框的宽度 + * @param props 额外的 props,参考 {@link ConfirmBoxProps} + */ +export async function routedConfirm( + controller: IUIMountable, + text: string, + loc: ElementLocator, + width: number, + props?: Partial +) { + if (core.isReplaying()) { + const confirm = getChoiceRoute() === 1; + const timeout = core.control.__replay_getTimeout(); + if (timeout === 0) return confirm; + const instance = controller.open(ConfirmBoxUI, { + ...(props ?? {}), + text, + loc, + width, + defaultYes: confirm + }); + await sleep(core.control.__replay_getTimeout()); + controller.close(instance); + return confirm; + } else { + const confirm = await getConfirm(controller, text, loc, width, props); + core.status.route.push(`choices:${confirm ? 1 : 0}`); + return confirm; + } +} + +/** + * 弹出一个选择框,然后将选择结果返回,与 getChoice 不同的是内置录像支持,如果这个选择框需要进录像, + * 需要使用此方法。例如给玩家弹出一个选择框,并获取玩家选择了哪个: + * ```ts + * const choice = await routedChoice( + * // 在哪个 UI 控制器上打开,对于一般 UI 组件来说,直接填写 props.controller 即可 + * props.controller, + * // 选项内容,参考 Choices 的注释 + * [[0, '选项1'], [1, '选项2'], [2, '选项3']], + * // 选择框的位置,宽度由下一个参数指定,高度参数由组件内部计算得出,指定无效 + * [240, 240, void 0, void 0, 0.5, 0.5], + * // 宽度设为 240 + * 240, + * // 可以给选择框传入其他的 props,例如指定标题,此项可选 + * { title: '选项标题' } + * ); + * // 之后,就可以直接判断 choice 来执行不同的操作了 + * if (choice === 0) { ... } + * ``` + * @param controller UI 控制器 + * @param choices 选择框的选项 + * @param loc 选择框的位置 + * @param width 选择框的宽度 + * @param props 额外的 props,参考 {@link ChoicesProps} + */ +export async function routedChoices( + controller: IUIMountable, + choices: ChoiceItem[], + loc: ElementLocator, + width: number, + props?: Partial +) { + if (core.isReplaying()) { + const selected = getChoiceRoute(); + const timeout = core.control.__replay_getTimeout(); + if (timeout === 0) return selected; + const instance = controller.open(ChoicesUI, { + ...(props ?? {}), + choices, + loc, + width, + selected + }); + await sleep(core.control.__replay_getTimeout()); + controller.close(instance); + return selected; + } else { + const choice = await getChoice(controller, choices, loc, width, props); + const index = choices.findIndex(v => v[1] === choice); + core.status.route.push(`choices:${index}`); + return choice; + } +} + /** @see {@link ConfirmBox} */ export const ConfirmBoxUI = new GameUI('confirm-box', ConfirmBox); /** @see {@link Choices} */ diff --git a/packages-user/client-modules/src/render/legacy/shadow.ts b/packages-user/client-modules/src/render/legacy/shadow.ts index 67a986b..512f7f5 100644 --- a/packages-user/client-modules/src/render/legacy/shadow.ts +++ b/packages-user/client-modules/src/render/legacy/shadow.ts @@ -1363,20 +1363,6 @@ export function calMapWalls(floor: FloorIds, nocache: boolean = false) { return res; } -/* @__PURE__ */ export function drawPolygons(floor: FloorIds) { - const polygons = calMapPolygons(floor); - const ctx = core.createCanvas('polygons', 0, 0, 480, 480, 130); - - ctx.lineWidth = 1; - ctx.lineJoin = 'round'; - ctx.strokeStyle = 'white'; - for (const p of polygons) { - for (const [x, y, w, h] of p) { - ctx.strokeRect(x, y, w, h); - } - } -} - export class LayerShadowExtends implements ILayerRenderExtends { static shadowList: Set = new Set(); id: string = 'shadow'; diff --git a/packages/legacy-ui/src/panel/enemySpecial.vue b/packages/legacy-ui/src/panel/enemySpecial.vue index 1012f12..410ab09 100644 --- a/packages/legacy-ui/src/panel/enemySpecial.vue +++ b/packages/legacy-ui/src/panel/enemySpecial.vue @@ -30,6 +30,7 @@