mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 04:02:59 +08:00 
			
		
		
		
	refactor: 部分 UI 换成新 UI
This commit is contained in:
		
							parent
							
								
									010383a914
								
							
						
					
					
						commit
						45c1d8c952
					
				| @ -6,6 +6,7 @@ import { TextAlign } from './textboxTyper'; | |||||||
| import { Page, PageExpose } from './page'; | import { Page, PageExpose } from './page'; | ||||||
| import { GameUI, IUIMountable, SetupComponentOptions } from '@motajs/system-ui'; | import { GameUI, IUIMountable, SetupComponentOptions } from '@motajs/system-ui'; | ||||||
| import { useKey } from '../use'; | import { useKey } from '../use'; | ||||||
|  | import { sleep } from 'mutate-animate'; | ||||||
| 
 | 
 | ||||||
| export interface ConfirmBoxProps extends DefaultProps, TextContentProps { | export interface ConfirmBoxProps extends DefaultProps, TextContentProps { | ||||||
|     text: string; |     text: string; | ||||||
| @ -192,7 +193,7 @@ export const ConfirmBox = defineComponent< | |||||||
|     ); |     ); | ||||||
| }, confirmBoxProps); | }, confirmBoxProps); | ||||||
| 
 | 
 | ||||||
| export type ChoiceKey = string | number | symbol; | export type ChoiceKey = string | number; | ||||||
| export type ChoiceItem = [key: ChoiceKey, text: string]; | export type ChoiceItem = [key: ChoiceKey, text: string]; | ||||||
| 
 | 
 | ||||||
| export interface ChoicesProps extends DefaultProps, TextContentProps { | export interface ChoicesProps extends DefaultProps, TextContentProps { | ||||||
| @ -211,6 +212,7 @@ export interface ChoicesProps extends DefaultProps, TextContentProps { | |||||||
|     titleFill?: CanvasStyle; |     titleFill?: CanvasStyle; | ||||||
|     pad?: number; |     pad?: number; | ||||||
|     interval?: number; |     interval?: number; | ||||||
|  |     selected?: number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type ChoicesEmits = { | export type ChoicesEmits = { | ||||||
| @ -233,7 +235,8 @@ const choicesProps = { | |||||||
|         'titleFont', |         'titleFont', | ||||||
|         'titleFill', |         'titleFill', | ||||||
|         'pad', |         'pad', | ||||||
|         'interval' |         'interval', | ||||||
|  |         'selected' | ||||||
|     ], |     ], | ||||||
|     emits: ['choose'] |     emits: ['choose'] | ||||||
| } satisfies SetupComponentOptions< | } satisfies SetupComponentOptions< | ||||||
| @ -283,7 +286,7 @@ export const Choices = defineComponent< | |||||||
| >((props, { emit, attrs }) => { | >((props, { emit, attrs }) => { | ||||||
|     const titleHeight = ref(0); |     const titleHeight = ref(0); | ||||||
|     const contentHeight = ref(0); |     const contentHeight = ref(0); | ||||||
|     const selected = ref(0); |     const selected = ref(props.selected ?? 0); | ||||||
|     const pageCom = ref<PageExpose>(); |     const pageCom = ref<PageExpose>(); | ||||||
|     const choiceSize = reactive<[number, number][]>([]); |     const choiceSize = reactive<[number, number][]>([]); | ||||||
| 
 | 
 | ||||||
| @ -634,6 +637,122 @@ export function getChoice<T extends ChoiceKey = ChoiceKey>( | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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<ConfirmBoxProps> | ||||||
|  | ) { | ||||||
|  |     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<ChoicesProps> | ||||||
|  | ) { | ||||||
|  |     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} */ | /** @see {@link ConfirmBox} */ | ||||||
| export const ConfirmBoxUI = new GameUI('confirm-box', ConfirmBox); | export const ConfirmBoxUI = new GameUI('confirm-box', ConfirmBox); | ||||||
| /** @see {@link Choices} */ | /** @see {@link Choices} */ | ||||||
|  | |||||||
| @ -1363,20 +1363,6 @@ export function calMapWalls(floor: FloorIds, nocache: boolean = false) { | |||||||
|     return res; |     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 { | export class LayerShadowExtends implements ILayerRenderExtends { | ||||||
|     static shadowList: Set<LayerShadowExtends> = new Set(); |     static shadowList: Set<LayerShadowExtends> = new Set(); | ||||||
|     id: string = 'shadow'; |     id: string = 'shadow'; | ||||||
|  | |||||||
| @ -30,6 +30,7 @@ | |||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { isMobile } from '../use'; | import { isMobile } from '../use'; | ||||||
| import { detailInfo, getSpecialHint } from '../tools/book'; | import { detailInfo, getSpecialHint } from '../tools/book'; | ||||||
|  | import Scroll from '../components/scroll.vue'; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
|     fromBook?: boolean; |     fromBook?: boolean; | ||||||
|  | |||||||
| @ -31,12 +31,7 @@ | |||||||
|                     class="detial-more" |                     class="detial-more" | ||||||
|                     v-if="panel === 'special'" |                     v-if="panel === 'special'" | ||||||
|                 > |                 > | ||||||
|                     <span |                     <span id="enemy-target" class="button-text more"> </span> | ||||||
|                         id="enemy-target" |  | ||||||
|                         class="button-text more" |  | ||||||
|                         @click="changePanel($event, 'target')" |  | ||||||
|                         ><LeftOutlined /> |  | ||||||
|                     </span> |  | ||||||
|                     <span |                     <span | ||||||
|                         id="critical-more" |                         id="critical-more" | ||||||
|                         class="button-text more" |                         class="button-text more" | ||||||
| @ -81,7 +76,6 @@ import { useDrag } from '../use'; | |||||||
| import EnemySpecial from '../panel/enemySpecial.vue'; | import EnemySpecial from '../panel/enemySpecial.vue'; | ||||||
| import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | 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 { detailInfo } from '../tools/book'; | import { detailInfo } from '../tools/book'; | ||||||
| import { gameKey } from '@motajs/system-action'; | import { gameKey } from '@motajs/system-action'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1272,7 +1272,6 @@ control.prototype.startReplay = function (list) { | |||||||
|     core.status.replay.totalList = core.status.route.concat(list); |     core.status.replay.totalList = core.status.route.concat(list); | ||||||
|     core.status.replay.steps = 0; |     core.status.replay.steps = 0; | ||||||
|     core.status.replay.save = []; |     core.status.replay.save = []; | ||||||
|     core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); |  | ||||||
|     core.setOpacity('replay', 0.6); |     core.setOpacity('replay', 0.6); | ||||||
|     this._replay_drawProgress(); |     this._replay_drawProgress(); | ||||||
|     core.updateStatusBar(false, true); |     core.updateStatusBar(false, true); | ||||||
| @ -1407,7 +1406,6 @@ control.prototype.rewindReplay = function () { | |||||||
|             steps: data.replay.steps, |             steps: data.replay.steps, | ||||||
|             save: save |             save: save | ||||||
|         }; |         }; | ||||||
|         core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); |  | ||||||
|         core.setOpacity('replay', 0.6); |         core.setOpacity('replay', 0.6); | ||||||
|         core.control._replay_drawProgress(); |         core.control._replay_drawProgress(); | ||||||
|         core.updateStatusBar(false, true); |         core.updateStatusBar(false, true); | ||||||
|  | |||||||
| @ -2520,70 +2520,6 @@ events.prototype._precompile_switch = function (data) { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| events.prototype._action_choices = function (data, x, y, prefix) { | events.prototype._action_choices = function (data, x, y, prefix) { | ||||||
|     data.choices = data.choices.filter(function (x) { |  | ||||||
|         if (x._disabled) return false; |  | ||||||
|         if (x.condition == null || x.condition == '') return true; |  | ||||||
|         try { |  | ||||||
|             return core.calValue(x.condition, prefix); |  | ||||||
|         } catch (e) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|     if (data.choices.length == 0) return this.doAction(); |  | ||||||
|     if (core.isReplaying()) { |  | ||||||
|         var action = core.status.replay.toReplay.shift(); |  | ||||||
|         if ( |  | ||||||
|             action.indexOf('choices:') == 0 && |  | ||||||
|             !(action == 'choices:none' && !data.timeout) |  | ||||||
|         ) { |  | ||||||
|             var index = action.substring(8); |  | ||||||
|             if (!this.__action_choices_replaying(data, index)) { |  | ||||||
|                 core.control._replay_error(action); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             // 容错录像
 |  | ||||||
|             if (main.replayChecking) { |  | ||||||
|                 // 录像验证系统中选最后一项
 |  | ||||||
|                 if (action != 'choices:none') |  | ||||||
|                     core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作
 |  | ||||||
|                 core.events.__action_choices_replaying(data, -1); |  | ||||||
|             } else { |  | ||||||
|                 // 正常游戏中弹窗选择
 |  | ||||||
|                 core.myprompt( |  | ||||||
|                     '录像回放出错!当前需要执行选择项但录像中未记录。\n如需修复请输入您要选的项(从0起),点击取消将不会修复。', |  | ||||||
|                     0, |  | ||||||
|                     function (value) { |  | ||||||
|                         if (value == null) { |  | ||||||
|                             core.control._replay_error(action); |  | ||||||
|                             return; |  | ||||||
|                         } |  | ||||||
|                         if (action != 'choices:none') |  | ||||||
|                             core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作
 |  | ||||||
|                         core.events.__action_choices_replaying( |  | ||||||
|                             data, |  | ||||||
|                             ((parseInt(value) || 0) + data.choices.length) % |  | ||||||
|                                 data.choices.length |  | ||||||
|                         ); |  | ||||||
|                     } |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         if (data.timeout) { |  | ||||||
|             core.status.event.interval = setTimeout(function () { |  | ||||||
|                 core.status.route.push('choices:none'); |  | ||||||
|                 core.setFlag('timeout', 0); |  | ||||||
|                 core.doAction(); |  | ||||||
|             }, data.timeout); |  | ||||||
|         } |  | ||||||
|         core.status.event.timeout = new Date().getTime() + (data.timeout || 0); |  | ||||||
|     } |  | ||||||
|     for (var i = 0; i < data.choices.length; i++) { |  | ||||||
|         if (typeof data.choices[i] === 'string') |  | ||||||
|             data.choices[i] = { text: data.choices[i] }; |  | ||||||
|         data.choices[i].text = core.replaceText(data.choices[i].text, prefix); |  | ||||||
|     } |  | ||||||
|     core.ui.drawChoices( |     core.ui.drawChoices( | ||||||
|         core.replaceText(data.text, prefix), |         core.replaceText(data.text, prefix), | ||||||
|         data.choices, |         data.choices, | ||||||
| @ -2646,41 +2582,17 @@ events.prototype._precompile_choices = function (data) { | |||||||
| 
 | 
 | ||||||
| events.prototype._action_confirm = function (data, x, y, prefix) { | events.prototype._action_confirm = function (data, x, y, prefix) { | ||||||
|     data.text = core.replaceText(data.text, prefix); |     data.text = core.replaceText(data.text, prefix); | ||||||
|     core.status.event.ui = { text: data.text, yes: data.yes, no: data.no }; |     core.ui.drawConfirmBox( | ||||||
|     if (core.isReplaying()) { |         data.text, | ||||||
|         var action = core.status.replay.toReplay.shift(); |         () => { | ||||||
|         if ( |             core.insertAction(data.yes ?? []); | ||||||
|             action.indexOf('choices:') == 0 && |             core.doAction(); | ||||||
|             !(action == 'choices:none' && !data.timeout) |         }, | ||||||
|         ) { |         () => { | ||||||
|             var index = action.substring(8); |             core.insertAction(data.no ?? []); | ||||||
|             if ( |  | ||||||
|                 index == 'none' || |  | ||||||
|                 ((index = parseInt(index)) >= 0 && index % 100 < 2) |  | ||||||
|             ) { |  | ||||||
|                 this.__action_confirm_replaying(data, index); |  | ||||||
|             } else { |  | ||||||
|                 core.control._replay_error(action); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             // 录像中未记录选了哪个,则选默认值,而不是直接报错
 |  | ||||||
|             if (action != 'choices:none') |  | ||||||
|                 core.status.replay.toReplay.unshift(action); |  | ||||||
|             this.__action_confirm_replaying(data, data['default'] ? 0 : 1); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         core.status.event.selection = data['default'] ? 0 : 1; |  | ||||||
|         if (data.timeout) { |  | ||||||
|             core.status.event.interval = setTimeout(function () { |  | ||||||
|                 core.status.route.push('choices:none'); |  | ||||||
|                 core.setFlag('timeout', 0); |  | ||||||
|             core.doAction(); |             core.doAction(); | ||||||
|             }, data.timeout); |  | ||||||
|         } |         } | ||||||
|         core.status.event.timeout = new Date().getTime() + (data.timeout || 0); |     ); | ||||||
|     } |  | ||||||
|     core.ui.drawConfirmBox(data.text); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| events.prototype.__action_confirm_replaying = function (data, index) { | events.prototype.__action_confirm_replaying = function (data, index) { | ||||||
|  | |||||||
							
								
								
									
										1732
									
								
								public/libs/ui.js
									
									
									
									
									
								
							
							
						
						
									
										1732
									
								
								public/libs/ui.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								src/types/declaration/control.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/types/declaration/control.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1179,6 +1179,7 @@ interface Control { | |||||||
|     ): boolean; |     ): boolean; | ||||||
|     _setAutomaticRoute_drawRoute(step: any): void; |     _setAutomaticRoute_drawRoute(step: any): void; | ||||||
|     _setAutomaticRoute_setAutoSteps(step: any): void; |     _setAutomaticRoute_setAutoSteps(step: any): void; | ||||||
|  |     __replay_getTimeout(): number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| declare const control: new () => Control; | declare const control: new () => Control; | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								src/types/declaration/ui.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/types/declaration/ui.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -764,7 +764,7 @@ interface Ui { | |||||||
|      */ |      */ | ||||||
|     drawChoices( |     drawChoices( | ||||||
|         content: string, |         content: string, | ||||||
|         choices: string[], |         choices: object[], | ||||||
|         width?: number, |         width?: number, | ||||||
|         ctx?: CtxRefer |         ctx?: CtxRefer | ||||||
|     ): void; |     ): void; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user