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 { 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<PageExpose>(); | ||||
|     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} */ | ||||
| export const ConfirmBoxUI = new GameUI('confirm-box', ConfirmBox); | ||||
| /** @see {@link Choices} */ | ||||
|  | ||||
| @ -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<LayerShadowExtends> = new Set(); | ||||
|     id: string = 'shadow'; | ||||
|  | ||||
| @ -30,6 +30,7 @@ | ||||
| <script lang="ts" setup> | ||||
| import { isMobile } from '../use'; | ||||
| import { detailInfo, getSpecialHint } from '../tools/book'; | ||||
| import Scroll from '../components/scroll.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     fromBook?: boolean; | ||||
|  | ||||
| @ -31,12 +31,7 @@ | ||||
|                     class="detial-more" | ||||
|                     v-if="panel === 'special'" | ||||
|                 > | ||||
|                     <span | ||||
|                         id="enemy-target" | ||||
|                         class="button-text more" | ||||
|                         @click="changePanel($event, 'target')" | ||||
|                         ><LeftOutlined /> | ||||
|                     </span> | ||||
|                     <span id="enemy-target" class="button-text more"> </span> | ||||
|                     <span | ||||
|                         id="critical-more" | ||||
|                         class="button-text more" | ||||
| @ -81,7 +76,6 @@ import { useDrag } from '../use'; | ||||
| import EnemySpecial from '../panel/enemySpecial.vue'; | ||||
| import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | ||||
| import EnemyCritical from '../panel/enemyCritical.vue'; | ||||
| // import EnemyTarget from '../panel/enemyTarget.vue'; | ||||
| import { detailInfo } from '../tools/book'; | ||||
| 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.steps = 0; | ||||
|     core.status.replay.save = []; | ||||
|     core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); | ||||
|     core.setOpacity('replay', 0.6); | ||||
|     this._replay_drawProgress(); | ||||
|     core.updateStatusBar(false, true); | ||||
| @ -1407,7 +1406,6 @@ control.prototype.rewindReplay = function () { | ||||
|             steps: data.replay.steps, | ||||
|             save: save | ||||
|         }; | ||||
|         core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); | ||||
|         core.setOpacity('replay', 0.6); | ||||
|         core.control._replay_drawProgress(); | ||||
|         core.updateStatusBar(false, true); | ||||
|  | ||||
| @ -2520,70 +2520,6 @@ events.prototype._precompile_switch = function (data) { | ||||
| }; | ||||
| 
 | ||||
| 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.replaceText(data.text, prefix), | ||||
|         data.choices, | ||||
| @ -2646,41 +2582,17 @@ events.prototype._precompile_choices = function (data) { | ||||
| 
 | ||||
| events.prototype._action_confirm = function (data, x, y, prefix) { | ||||
|     data.text = core.replaceText(data.text, prefix); | ||||
|     core.status.event.ui = { text: data.text, yes: data.yes, no: data.no }; | ||||
|     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 ( | ||||
|                 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); | ||||
|     core.ui.drawConfirmBox( | ||||
|         data.text, | ||||
|         () => { | ||||
|             core.insertAction(data.yes ?? []); | ||||
|             core.doAction(); | ||||
|         }, | ||||
|         () => { | ||||
|             core.insertAction(data.no ?? []); | ||||
|             core.doAction(); | ||||
|         } | ||||
|     } 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(); | ||||
|             }, 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) { | ||||
|  | ||||
							
								
								
									
										1738
									
								
								public/libs/ui.js
									
									
									
									
									
								
							
							
						
						
									
										1738
									
								
								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; | ||||
|     _setAutomaticRoute_drawRoute(step: any): void; | ||||
|     _setAutomaticRoute_setAutoSteps(step: any): void; | ||||
|     __replay_getTimeout(): number; | ||||
| } | ||||
| 
 | ||||
| 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( | ||||
|         content: string, | ||||
|         choices: string[], | ||||
|         choices: object[], | ||||
|         width?: number, | ||||
|         ctx?: CtxRefer | ||||
|     ): void; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user