mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 12:12:58 +08:00 
			
		
		
		
	feat: 部分设置菜单
This commit is contained in:
		
							parent
							
								
									0cc76bd475
								
							
						
					
					
						commit
						8cc0c76846
					
				| @ -23,7 +23,7 @@ export const UIContainer = defineComponent<UIContainerProps>(props => { | |||||||
|                     controller={data} |                     controller={data} | ||||||
|                     instance={b} |                     instance={b} | ||||||
|                     key={b.key} |                     key={b.key} | ||||||
|                     hidden={b.hidden} |                     hidden={b.hidden && !b.alwaysShow} | ||||||
|                 ></b.ui.component> |                 ></b.ui.component> | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| @ -34,7 +34,7 @@ export const UIContainer = defineComponent<UIContainerProps>(props => { | |||||||
|                     key={v.key} |                     key={v.key} | ||||||
|                     controller={data} |                     controller={data} | ||||||
|                     instance={v} |                     instance={v} | ||||||
|                     hidden={v.hidden} |                     hidden={v.hidden && !v.alwaysShow} | ||||||
|                 ></v.ui.component> |                 ></v.ui.component> | ||||||
|             )) |             )) | ||||||
|         ); |         ); | ||||||
|  | |||||||
| @ -94,6 +94,7 @@ | |||||||
|         "60": "Repeated Tip id: '$1'.", |         "60": "Repeated Tip id: '$1'.", | ||||||
|         "61": "Unexpected recursive call of $1.update in render function. Please ensure you have to do this, if you do, ignore this warn.", |         "61": "Unexpected recursive call of $1.update in render function. Please ensure you have to do this, if you do, ignore this warn.", | ||||||
|         "62": "Recursive fallback fonts in '$1'.", |         "62": "Recursive fallback fonts in '$1'.", | ||||||
|  |         "63": "Uncaught promise error in waiting box component. Error reason: $1", | ||||||
|         "1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.", |         "1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.", | ||||||
|         "1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance." |         "1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance." | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ const confirmBoxProps = { | |||||||
| >; | >; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 确认框组件,与 2.x 的 drawConfirm 类似,可以键盘操作, |  * 确认框组件,与 2.x 的 drawConfirm 类似,可以键盘操作,单次调用参考 {@link getConfirm}。 | ||||||
|  * 参数参考 {@link ConfirmBoxProps},事件参考 {@link ConfirmBoxEmits},用例如下: |  * 参数参考 {@link ConfirmBoxProps},事件参考 {@link ConfirmBoxEmits},用例如下: | ||||||
|  * ```tsx
 |  * ```tsx
 | ||||||
|  * const onYes = () => console.log('yes'); |  * const onYes = () => console.log('yes'); | ||||||
| @ -243,7 +243,7 @@ const choicesProps = { | |||||||
| >; | >; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 选项框组件,用于在多个选项中选择一个,例如样板的系统设置就由它实现。 |  * 选项框组件,用于在多个选项中选择一个,例如样板的系统设置就由它实现。单次调用参考 {@link getChoice}。 | ||||||
|  * 参数参考 {@link ChoicesProps},事件参考 {@link ChoicesEmits}。用例如下: |  * 参数参考 {@link ChoicesProps},事件参考 {@link ChoicesEmits}。用例如下: | ||||||
|  * ```tsx
 |  * ```tsx
 | ||||||
|  * <Choices |  * <Choices | ||||||
| @ -530,12 +530,28 @@ export const Choices = defineComponent< | |||||||
| }, choicesProps); | }, choicesProps); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 弹出一个确认框,然后将确认结果返回 |  * 弹出一个确认框,然后将确认结果返回,例如给玩家弹出一个确认框,并获取玩家是否确认: | ||||||
|  |  * ```ts
 | ||||||
|  |  * const confirm = await getConfirm( | ||||||
|  |  *   // 在哪个 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 controller UI 控制器 | ||||||
|  * @param text 确认文本内容 |  * @param text 确认文本内容 | ||||||
|  * @param loc 确认框的位置 |  * @param loc 确认框的位置 | ||||||
|  * @param width 确认框的宽度 |  * @param width 确认框的宽度 | ||||||
|  * @param props 额外的 props |  * @param props 额外的 props,参考 {@link ConfirmBoxProps} | ||||||
|  */ |  */ | ||||||
| export function getConfirm( | export function getConfirm( | ||||||
|     controller: IUIMountable, |     controller: IUIMountable, | ||||||
| @ -567,12 +583,28 @@ export function getConfirm( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 弹出一个选择框,然后将选择结果返回 |  * 弹出一个选择框,然后将选择结果返回,例如给玩家弹出一个选择框,并获取玩家选择了哪个: | ||||||
|  |  * ```ts
 | ||||||
|  |  * const choice = await getChoice( | ||||||
|  |  *   // 在哪个 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 controller UI 控制器 | ||||||
|  * @param choices 选择框的选项 |  * @param choices 选择框的选项 | ||||||
|  * @param loc 选择框的位置 |  * @param loc 选择框的位置 | ||||||
|  * @param width 选择框的宽度 |  * @param width 选择框的宽度 | ||||||
|  * @param props 额外的 props |  * @param props 额外的 props,参考 {@link ChoicesProps} | ||||||
|  */ |  */ | ||||||
| export function getChoice<T extends ChoiceKey = ChoiceKey>( | export function getChoice<T extends ChoiceKey = ChoiceKey>( | ||||||
|     controller: IUIMountable, |     controller: IUIMountable, | ||||||
|  | |||||||
| @ -5,13 +5,15 @@ import { | |||||||
|     PathProps, |     PathProps, | ||||||
|     Sprite |     Sprite | ||||||
| } from '@/core/render'; | } from '@/core/render'; | ||||||
| import { computed, defineComponent, ref, watch } from 'vue'; | import { computed, defineComponent, ref, SetupContext, watch } from 'vue'; | ||||||
| import { SetupComponentOptions } from './types'; | import { SetupComponentOptions } from './types'; | ||||||
| import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; | import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; | ||||||
| import { TextboxProps, TextContent } from './textbox'; | import { TextboxProps, TextContent, TextContentProps } from './textbox'; | ||||||
| import { Scroll, ScrollExpose, ScrollProps } from './scroll'; | import { Scroll, ScrollExpose, ScrollProps } from './scroll'; | ||||||
| import { transitioned } from '../use'; | import { transitioned } from '../use'; | ||||||
| import { hyper } from 'mutate-animate'; | import { hyper } from 'mutate-animate'; | ||||||
|  | import { logger } from '@/core/common/logger'; | ||||||
|  | import { GameUI, IUIMountable } from '@/core/system'; | ||||||
| 
 | 
 | ||||||
| interface ProgressProps extends DefaultProps { | interface ProgressProps extends DefaultProps { | ||||||
|     /** 进度条的位置 */ |     /** 进度条的位置 */ | ||||||
| @ -401,3 +403,181 @@ export const Background = defineComponent<BackgroundProps>(props => { | |||||||
|             /> |             /> | ||||||
|         ); |         ); | ||||||
| }, backgroundProps); | }, backgroundProps); | ||||||
|  | 
 | ||||||
|  | export interface WaitBoxProps<T> | ||||||
|  |     extends Partial<BackgroundProps>, | ||||||
|  |         Partial<TextContentProps> { | ||||||
|  |     loc: ElementLocator; | ||||||
|  |     width: number; | ||||||
|  |     promise?: Promise<T>; | ||||||
|  |     text?: string; | ||||||
|  |     pad?: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export type WaitBoxEmits<T> = { | ||||||
|  |     resolve: (data: T) => void; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export interface WaitBoxExpose<T> { | ||||||
|  |     /** | ||||||
|  |      * 手动将此组件兑现,注意调用时如果传入的 Promise 还没有兑现, | ||||||
|  |      * 当 Promise 兑现后将不会再次触发 resolve 事件,即 resolve 事件只会被触发一次 | ||||||
|  |      * @param data 兑现值 | ||||||
|  |      */ | ||||||
|  |     resolve(data: T): void; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const waitBoxProps = { | ||||||
|  |     props: ['promise', 'loc', 'winskin', 'color', 'border'], | ||||||
|  |     emits: ['resolve'] | ||||||
|  | } satisfies SetupComponentOptions< | ||||||
|  |     WaitBoxProps<unknown>, | ||||||
|  |     WaitBoxEmits<unknown>, | ||||||
|  |     keyof WaitBoxEmits<unknown> | ||||||
|  | >; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 等待框,可以等待某个异步操作 (Promise),操作完毕后触发兑现事件,单次调用参考 {@link waitbox}。 | ||||||
|  |  * 参数参考 {@link WaitBoxProps},事件参考 {@link WaitBoxEmits},函数接口参考 {@link WaitBoxExpose}。用例如下: | ||||||
|  |  * ```tsx
 | ||||||
|  |  * // 创建一个等待 1000ms 的 Promise,兑现值是等待完毕时的当前时间刻
 | ||||||
|  |  * const promise = new Promise(res => window.setTimeout(() => res(Date.now()), 1000)); | ||||||
|  |  * | ||||||
|  |  * <WaitBox | ||||||
|  |  *   // 传入要等待的 Promise
 | ||||||
|  |  *   promise={promise} | ||||||
|  |  *   // 等待框的位置,宽度由 width 参数指定,高度由内部计算得来,不需要手动指定,即使手动指定也无效
 | ||||||
|  |  *   loc={[240, 240, void 0, void 0, 0.5, 0.5]} | ||||||
|  |  *   // 等待框的宽度
 | ||||||
|  |  *   width={240} | ||||||
|  |  *   // 完全继承 Background 的参数,因此可以直接指定背景样式
 | ||||||
|  |  *   winskin="winskin2.png" | ||||||
|  |  *   // 完全继承 TextContent 的参数,因此可以直接指定字体
 | ||||||
|  |  *   font={new Font('Verdana', 28)} | ||||||
|  |  * /> | ||||||
|  |  * ``` | ||||||
|  |  */ | ||||||
|  | export const WaitBox = defineComponent< | ||||||
|  |     WaitBoxProps<unknown>, | ||||||
|  |     WaitBoxEmits<unknown>, | ||||||
|  |     keyof WaitBoxEmits<unknown> | ||||||
|  | >( | ||||||
|  |     <T,>( | ||||||
|  |         props: WaitBoxProps<T>, | ||||||
|  |         { emit, expose, attrs }: SetupContext<WaitBoxEmits<T>> | ||||||
|  |     ) => { | ||||||
|  |         const contentHeight = ref(200); | ||||||
|  | 
 | ||||||
|  |         const text = computed(() => props.text ?? '请等待 ...'); | ||||||
|  |         const pad = computed(() => props.pad ?? 24); | ||||||
|  |         const containerLoc = computed<ElementLocator>(() => { | ||||||
|  |             const [x = 0, y = 0, , , ax = 0, ay = 0] = props.loc; | ||||||
|  |             return [x, y, props.width, contentHeight.value, ax, ay]; | ||||||
|  |         }); | ||||||
|  |         const backLoc = computed<ElementLocator>(() => { | ||||||
|  |             return [1, 1, props.width - 2, contentHeight.value - 2]; | ||||||
|  |         }); | ||||||
|  |         const contentLoc = computed<ElementLocator>(() => { | ||||||
|  |             return [ | ||||||
|  |                 pad.value, | ||||||
|  |                 pad.value, | ||||||
|  |                 props.width - pad.value * 2, | ||||||
|  |                 contentHeight.value - pad.value * 2 | ||||||
|  |             ]; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         let resolved: boolean = false; | ||||||
|  | 
 | ||||||
|  |         props.promise?.then( | ||||||
|  |             value => { | ||||||
|  |                 resolve(value); | ||||||
|  |             }, | ||||||
|  |             reason => { | ||||||
|  |                 logger.warn(63, reason); | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         const resolve = (data: T) => { | ||||||
|  |             if (resolved) return; | ||||||
|  |             resolved = true; | ||||||
|  |             emit('resolve', data); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         const onContentHeight = (height: number) => { | ||||||
|  |             contentHeight.value = height + pad.value * 2; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         expose<WaitBoxExpose<T>>({ resolve }); | ||||||
|  | 
 | ||||||
|  |         return () => ( | ||||||
|  |             <container loc={containerLoc.value}> | ||||||
|  |                 <Background | ||||||
|  |                     loc={backLoc.value} | ||||||
|  |                     zIndex={0} | ||||||
|  |                     winskin={props.winskin} | ||||||
|  |                     color={props.color} | ||||||
|  |                     border={props.border} | ||||||
|  |                 /> | ||||||
|  |                 <TextContent | ||||||
|  |                     {...attrs} | ||||||
|  |                     autoHeight | ||||||
|  |                     text={text.value} | ||||||
|  |                     loc={contentLoc.value} | ||||||
|  |                     width={props.width - pad.value * 2} | ||||||
|  |                     zIndex={5} | ||||||
|  |                     onUpdateHeight={onContentHeight} | ||||||
|  |                 /> | ||||||
|  |             </container> | ||||||
|  |         ); | ||||||
|  |     }, | ||||||
|  |     waitBoxProps | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 打开一个等待框,等待传入的 Promise 兑现后,关闭等待框,并将兑现值返回。 | ||||||
|  |  * 示例,等待 1000ms: | ||||||
|  |  * ```ts
 | ||||||
|  |  * // 创建一个等待 1000ms 的 Promise,兑现值是等待完毕时的当前时间刻
 | ||||||
|  |  * const promise = new Promise(res => window.setTimeout(() => res(Date.now()), 1000)); | ||||||
|  |  * const value = await waitbox( | ||||||
|  |  *   // 在哪个 UI 控制器上打开,对于一般 UI 组件来说,直接填写 props.controller 即可
 | ||||||
|  |  *   props.controller, | ||||||
|  |  *   // 确认框的位置,宽度由下一个参数指定,高度参数由组件内部计算得出,指定无效
 | ||||||
|  |  *   [240, 240, void 0, void 0, 0.5, 0.5], | ||||||
|  |  *   // 确认框的宽度
 | ||||||
|  |  *   240, | ||||||
|  |  *   // 要等待的 Promise
 | ||||||
|  |  *   promise, | ||||||
|  |  *   // 额外的 props,例如填写等待文本,此项可选,参考 WaitBoxProps
 | ||||||
|  |  *   { text: '请等待 1000ms' } | ||||||
|  |  * ); | ||||||
|  |  * console.log(value); // 输出时间刻
 | ||||||
|  |  * ``` | ||||||
|  |  * @param controller UI 控制器 | ||||||
|  |  * @param loc 等待框的位置 | ||||||
|  |  * @param width 等待框的宽度 | ||||||
|  |  * @param promise 要等待的 Promise | ||||||
|  |  * @param props 额外的 props,参考 {@link WaitBoxProps} | ||||||
|  |  */ | ||||||
|  | export function waitbox<T>( | ||||||
|  |     controller: IUIMountable, | ||||||
|  |     loc: ElementLocator, | ||||||
|  |     width: number, | ||||||
|  |     promise: Promise<T>, | ||||||
|  |     props?: Partial<WaitBoxProps<T>> | ||||||
|  | ): Promise<T> { | ||||||
|  |     return new Promise<T>(res => { | ||||||
|  |         const instance = controller.open(WaitBoxUI, { | ||||||
|  |             ...(props ?? {}), | ||||||
|  |             loc, | ||||||
|  |             width, | ||||||
|  |             promise, | ||||||
|  |             onResolve: data => { | ||||||
|  |                 controller.close(instance); | ||||||
|  |                 res(data as T); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const WaitBoxUI = new GameUI('wait-box', WaitBox); | ||||||
|  | |||||||
							
								
								
									
										579
									
								
								src/module/render/ui/settings.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										579
									
								
								src/module/render/ui/settings.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,579 @@ | |||||||
|  | import { ElementLocator } from '@/core/render'; | ||||||
|  | import { GameUI, UIComponentProps } from '@/core/system'; | ||||||
|  | import { defineComponent } from 'vue'; | ||||||
|  | import { | ||||||
|  |     ChoiceItem, | ||||||
|  |     ChoiceKey, | ||||||
|  |     Choices, | ||||||
|  |     ChoicesProps, | ||||||
|  |     getConfirm, | ||||||
|  |     SetupComponentOptions, | ||||||
|  |     waitbox | ||||||
|  | } from '../components'; | ||||||
|  | import { mainUi } from '@/core/main/init/ui'; | ||||||
|  | import { gameKey } from '@/core/main/custom/hotkey'; | ||||||
|  | import { generateKeyboardEvent } from '@/core/main/custom/keyboard'; | ||||||
|  | import { getVitualKeyOnce } from '@/plugin/utils'; | ||||||
|  | import { getAllSavesData, getSaveData } from '@/module/utils'; | ||||||
|  | 
 | ||||||
|  | export interface SettingsProps extends Partial<ChoicesProps>, UIComponentProps { | ||||||
|  |     loc: ElementLocator; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const settingsProps = { | ||||||
|  |     props: ['loc', 'controller', 'instance'] | ||||||
|  | } satisfies SetupComponentOptions<SettingsProps>; | ||||||
|  | 
 | ||||||
|  | const enum MainChoice { | ||||||
|  |     SystemSetting, | ||||||
|  |     VirtualKey, | ||||||
|  |     ViewMap, | ||||||
|  |     /** @see {@link ReplaySettings} */ | ||||||
|  |     Replay, | ||||||
|  |     /** @see {@link SyncSave} */ | ||||||
|  |     SyncSave, | ||||||
|  |     /** @see {@link GameInfo} */ | ||||||
|  |     GameInfo, | ||||||
|  |     Restart, | ||||||
|  |     Back | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const MainSettings = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choices: ChoiceItem[] = [ | ||||||
|  |         [MainChoice.SystemSetting, '系统设置'], | ||||||
|  |         [MainChoice.VirtualKey, '虚拟键盘'], | ||||||
|  |         [MainChoice.ViewMap, '浏览地图'], | ||||||
|  |         [MainChoice.Replay, '录像回放'], | ||||||
|  |         [MainChoice.SyncSave, '同步存档'], | ||||||
|  |         [MainChoice.GameInfo, '游戏信息'], | ||||||
|  |         [MainChoice.Restart, '返回标题'], | ||||||
|  |         [MainChoice.Back, '返回游戏'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case MainChoice.SystemSetting: { | ||||||
|  |                 mainUi.open('settings'); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.VirtualKey: { | ||||||
|  |                 getVitualKeyOnce().then(value => { | ||||||
|  |                     gameKey.emitKey( | ||||||
|  |                         value.key, | ||||||
|  |                         value.assist, | ||||||
|  |                         'up', | ||||||
|  |                         generateKeyboardEvent(value.key, value.assist) | ||||||
|  |                     ); | ||||||
|  |                 }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.ViewMap: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.Replay: { | ||||||
|  |                 props.controller.open(ReplaySettingsUI, { loc: props.loc }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.SyncSave: { | ||||||
|  |                 props.controller.open(SyncSaveUI, { loc: props.loc }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.GameInfo: { | ||||||
|  |                 props.controller.open(GameInfoUI, { loc: props.loc }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.Restart: { | ||||||
|  |                 props.controller.closeAll(); | ||||||
|  |                 core.restart(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MainChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             choices={choices} | ||||||
|  |             width={240} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }, settingsProps); | ||||||
|  | 
 | ||||||
|  | const enum ReplayChoice { | ||||||
|  |     Start, | ||||||
|  |     StartFromSave, | ||||||
|  |     ResumeReplay, | ||||||
|  |     ReplayRest, | ||||||
|  |     ChooseReplay, | ||||||
|  |     Download, | ||||||
|  |     Back | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const ReplaySettings = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choice: ChoiceItem[] = [ | ||||||
|  |         [ReplayChoice.Start, '从头回放录像'], | ||||||
|  |         [ReplayChoice.StartFromSave, '从存档开始回放'], | ||||||
|  |         [ReplayChoice.ResumeReplay, '接续播放剩余录像'], | ||||||
|  |         [ReplayChoice.ReplayRest, '播放存档剩余录像'], | ||||||
|  |         [ReplayChoice.ChooseReplay, '选择录像文件'], | ||||||
|  |         [ReplayChoice.Download, '下载当前录像'], | ||||||
|  |         [ReplayChoice.Back, '返回游戏'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case ReplayChoice.Start: { | ||||||
|  |                 props.controller.closeAll(); | ||||||
|  |                 core.ui.closePanel(); | ||||||
|  |                 const route = core.status.route.slice(); | ||||||
|  |                 const seed = core.getFlag<number>('__seed__'); | ||||||
|  |                 core.startGame(core.status.hard, seed, route); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case ReplayChoice.StartFromSave: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case ReplayChoice.ResumeReplay: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case ReplayChoice.ReplayRest: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case ReplayChoice.ChooseReplay: { | ||||||
|  |                 props.controller.closeAll(); | ||||||
|  |                 core.chooseReplayFile(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case ReplayChoice.Download: { | ||||||
|  |                 core.download( | ||||||
|  |                     core.firstData.name + '_' + core.formatDate2() + '.h5route', | ||||||
|  |                     // @ts-expect-error 暂时无法推导
 | ||||||
|  |                     LZString.compressToBase64( | ||||||
|  |                         JSON.stringify({ | ||||||
|  |                             name: core.firstData.name, | ||||||
|  |                             hard: core.status.hard, | ||||||
|  |                             seed: core.getFlag('__seed__'), | ||||||
|  |                             route: core.encodeRoute(core.status.route) | ||||||
|  |                         }) | ||||||
|  |                     ) | ||||||
|  |                 ); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case ReplayChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             choices={choice} | ||||||
|  |             width={240} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }, settingsProps); | ||||||
|  | 
 | ||||||
|  | const enum GameInfoChoice { | ||||||
|  |     Statistics, | ||||||
|  |     Project, | ||||||
|  |     Tower, | ||||||
|  |     Help, | ||||||
|  |     Download, | ||||||
|  |     Back | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const GameInfo = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choices: ChoiceItem[] = [ | ||||||
|  |         [GameInfoChoice.Statistics, '数据统计'], | ||||||
|  |         [GameInfoChoice.Project, '查看工程'], | ||||||
|  |         [GameInfoChoice.Tower, '游戏主页'], | ||||||
|  |         [GameInfoChoice.Help, '操作帮助'], | ||||||
|  |         [GameInfoChoice.Download, '下载离线版本'], | ||||||
|  |         [GameInfoChoice.Back, '返回主菜单'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = async (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case GameInfoChoice.Statistics: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case GameInfoChoice.Project: { | ||||||
|  |                 if (core.platform.isPC) window.open('editor.html', '_blank'); | ||||||
|  |                 else { | ||||||
|  |                     const confirm = await getConfirm( | ||||||
|  |                         props.controller, | ||||||
|  |                         '即将离开本游戏,跳转至工程页面,确认跳转?', | ||||||
|  |                         props.loc, | ||||||
|  |                         240 | ||||||
|  |                     ); | ||||||
|  |                     if (confirm) { | ||||||
|  |                         window.location.href = 'editor-mobile.html'; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case GameInfoChoice.Tower: { | ||||||
|  |                 const name = core.firstData.name; | ||||||
|  |                 const href = `/tower/?name=${name}`; | ||||||
|  |                 if (core.platform.isPC) { | ||||||
|  |                     window.open(href, '_blank'); | ||||||
|  |                 } else { | ||||||
|  |                     const confirm = await getConfirm( | ||||||
|  |                         props.controller, | ||||||
|  |                         '即将离开本游戏,跳转至评论页面,确认跳转?', | ||||||
|  |                         props.loc, | ||||||
|  |                         240 | ||||||
|  |                     ); | ||||||
|  |                     if (confirm) { | ||||||
|  |                         window.location.href = href; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case GameInfoChoice.Download: { | ||||||
|  |                 const name = core.firstData.name; | ||||||
|  |                 const href = `/games/${name}/${name}.zip`; | ||||||
|  |                 if (core.platform.isPC) window.open(href); | ||||||
|  |                 else window.location.href = href; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case GameInfoChoice.Help: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case GameInfoChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             choices={choices} | ||||||
|  |             width={240} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const enum SyncSaveChoice { | ||||||
|  |     // ----- 主菜单
 | ||||||
|  |     ToServer, | ||||||
|  |     FromServer, | ||||||
|  |     ToLocal, | ||||||
|  |     FromLocal, | ||||||
|  |     ClearLocal, | ||||||
|  |     Back, | ||||||
|  |     // ----- 子菜单
 | ||||||
|  |     AllSaves, | ||||||
|  |     NowSave | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const SyncSave = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choices: ChoiceItem[] = [ | ||||||
|  |         [SyncSaveChoice.ToServer, '同步存档至服务器'], | ||||||
|  |         [SyncSaveChoice.FromServer, '从服务器加载存档'], | ||||||
|  |         [SyncSaveChoice.ToLocal, '存档至本地文件'], | ||||||
|  |         [SyncSaveChoice.FromLocal, '存本地文件读档'], | ||||||
|  |         [SyncSaveChoice.ClearLocal, '清空本地存档'], | ||||||
|  |         [SyncSaveChoice.Back, '返回上一级'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case SyncSaveChoice.ToServer: { | ||||||
|  |                 props.controller.open(SyncSaveSelectUI, { loc: props.loc }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.FromServer: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.ToLocal: { | ||||||
|  |                 props.controller.open(DownloadSaveSelectUI, { loc: props.loc }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.FromLocal: { | ||||||
|  |                 // todo
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.ClearLocal: { | ||||||
|  |                 props.controller.open(ClearSaveSelectUI, { loc: props.loc }); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             width={240} | ||||||
|  |             choices={choices} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const SyncSaveSelect = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choices: ChoiceItem[] = [ | ||||||
|  |         [SyncSaveChoice.AllSaves, '同步全部存档'], | ||||||
|  |         [SyncSaveChoice.NowSave, '同步当前存档'], | ||||||
|  |         [SyncSaveChoice.Back, '返回上一级'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = async (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case SyncSaveChoice.AllSaves: { | ||||||
|  |                 core.playSound('confirm.opus'); | ||||||
|  |                 const confirm = await getConfirm( | ||||||
|  |                     props.controller, | ||||||
|  |                     '你确定要同步全部存档么?这可能在存档较多的时候比较慢。', | ||||||
|  |                     props.loc, | ||||||
|  |                     240 | ||||||
|  |                 ); | ||||||
|  |                 if (confirm) { | ||||||
|  |                     core.syncSave('all'); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.NowSave: { | ||||||
|  |                 core.playSound('confirm.opus'); | ||||||
|  |                 const confirm = await getConfirm( | ||||||
|  |                     props.controller, | ||||||
|  |                     '确定要同步当前存档吗?', | ||||||
|  |                     props.loc, | ||||||
|  |                     240 | ||||||
|  |                 ); | ||||||
|  |                 if (confirm) { | ||||||
|  |                     core.syncSave(); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             width={240} | ||||||
|  |             choices={choices} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const DownloadSaveSelect = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choices: ChoiceItem[] = [ | ||||||
|  |         [SyncSaveChoice.AllSaves, '下载全部存档'], | ||||||
|  |         [SyncSaveChoice.NowSave, '下载当前存档'], | ||||||
|  |         [SyncSaveChoice.Back, '返回上一级'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = async (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case SyncSaveChoice.AllSaves: { | ||||||
|  |                 const confirm = await getConfirm( | ||||||
|  |                     props.controller, | ||||||
|  |                     '确认要下载所有存档吗?', | ||||||
|  |                     props.loc, | ||||||
|  |                     240 | ||||||
|  |                 ); | ||||||
|  |                 if (confirm) { | ||||||
|  |                     const data = await waitbox( | ||||||
|  |                         props.controller, | ||||||
|  |                         props.loc, | ||||||
|  |                         240, | ||||||
|  |                         getAllSavesData(), | ||||||
|  |                         { text: '请等待处理完毕' } | ||||||
|  |                     ); | ||||||
|  |                     core.download( | ||||||
|  |                         `${core.firstData.name}_${core.formatDate2(new Date())}.h5save`, | ||||||
|  |                         data | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.NowSave: { | ||||||
|  |                 const confirm = await getConfirm( | ||||||
|  |                     props.controller, | ||||||
|  |                     '确认要下载当前存档吗?', | ||||||
|  |                     props.loc, | ||||||
|  |                     240 | ||||||
|  |                 ); | ||||||
|  |                 if (confirm) { | ||||||
|  |                     const data = await getSaveData(core.saves.saveIndex); | ||||||
|  |                     core.download( | ||||||
|  |                         `${core.firstData.name}_${core.formatDate2(new Date())}.h5save`, | ||||||
|  |                         data | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             width={240} | ||||||
|  |             choices={choices} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const ClearSaveSelect = defineComponent<SettingsProps>(props => { | ||||||
|  |     const choices: ChoiceItem[] = [ | ||||||
|  |         [SyncSaveChoice.AllSaves, '清空全部塔存档'], | ||||||
|  |         [SyncSaveChoice.NowSave, '清空当前塔存档'], | ||||||
|  |         [SyncSaveChoice.Back, '返回上一级'] | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const choose = async (key: ChoiceKey) => { | ||||||
|  |         switch (key) { | ||||||
|  |             case SyncSaveChoice.AllSaves: { | ||||||
|  |                 const confirm = await getConfirm( | ||||||
|  |                     props.controller, | ||||||
|  |                     '你确定要清除【全部游戏】的所有本地存档?此行为不可逆!!!', | ||||||
|  |                     props.loc, | ||||||
|  |                     240 | ||||||
|  |                 ); | ||||||
|  |                 if (confirm) { | ||||||
|  |                     await waitbox( | ||||||
|  |                         props.controller, | ||||||
|  |                         props.loc, | ||||||
|  |                         240, | ||||||
|  |                         new Promise<void>(res => { | ||||||
|  |                             core.clearLocalForage(() => { | ||||||
|  |                                 core.saves.ids = {}; | ||||||
|  |                                 core.saves.autosave.data = null; | ||||||
|  |                                 core.saves.autosave.updated = false; | ||||||
|  |                                 core.saves.autosave.now = 0; | ||||||
|  |                                 // @ts-expect-error 沙比样板
 | ||||||
|  |                                 core.saves.cache = {}; | ||||||
|  |                                 core.saves.saveIndex = 1; | ||||||
|  |                                 core.saves.favorite = []; | ||||||
|  |                                 core.saves.favoriteName = {}; | ||||||
|  |                                 // @ts-expect-error 沙比样板
 | ||||||
|  |                                 core.control._updateFavoriteSaves(); | ||||||
|  |                                 core.removeLocalStorage('saveIndex'); | ||||||
|  |                                 res(); | ||||||
|  |                             }); | ||||||
|  |                         }), | ||||||
|  |                         { text: '正在情况,请稍后...' } | ||||||
|  |                     ); | ||||||
|  |                     await getConfirm( | ||||||
|  |                         props.controller, | ||||||
|  |                         '所有塔的存档已经全部清空', | ||||||
|  |                         props.loc, | ||||||
|  |                         240 | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.NowSave: { | ||||||
|  |                 const confirm = await getConfirm( | ||||||
|  |                     props.controller, | ||||||
|  |                     '你确定要清除【当前游戏】的所有本地存档?此行为不可逆!!!', | ||||||
|  |                     props.loc, | ||||||
|  |                     240 | ||||||
|  |                 ); | ||||||
|  |                 if (confirm) { | ||||||
|  |                     await waitbox( | ||||||
|  |                         props.controller, | ||||||
|  |                         props.loc, | ||||||
|  |                         240, | ||||||
|  |                         new Promise<void>(res => { | ||||||
|  |                             Object.keys(core.saves.ids).forEach(function (v) { | ||||||
|  |                                 core.removeLocalForage('save' + v); | ||||||
|  |                             }); | ||||||
|  |                             core.removeLocalForage('autoSave', () => { | ||||||
|  |                                 core.saves.ids = {}; | ||||||
|  |                                 core.saves.autosave.data = null; | ||||||
|  |                                 core.saves.autosave.updated = false; | ||||||
|  |                                 core.saves.autosave.now = 0; | ||||||
|  |                                 core.ui.closePanel(); | ||||||
|  |                                 core.saves.saveIndex = 1; | ||||||
|  |                                 core.saves.favorite = []; | ||||||
|  |                                 core.saves.favoriteName = {}; | ||||||
|  |                                 // @ts-expect-error 沙比样板
 | ||||||
|  |                                 core.control._updateFavoriteSaves(); | ||||||
|  |                                 core.removeLocalStorage('saveIndex'); | ||||||
|  |                                 res(); | ||||||
|  |                             }); | ||||||
|  |                         }), | ||||||
|  |                         { text: '正在情况,请稍后...' } | ||||||
|  |                     ); | ||||||
|  |                     await getConfirm( | ||||||
|  |                         props.controller, | ||||||
|  |                         '当前塔的存档已被清空', | ||||||
|  |                         props.loc, | ||||||
|  |                         240 | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case SyncSaveChoice.Back: { | ||||||
|  |                 props.controller.close(props.instance); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <Choices | ||||||
|  |             loc={props.loc} | ||||||
|  |             width={240} | ||||||
|  |             choices={choices} | ||||||
|  |             onChoose={choose} | ||||||
|  |         /> | ||||||
|  |     ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** @see {@link MainSettings} */ | ||||||
|  | export const MainSettingsUI = new GameUI('main-settings', MainSettings); | ||||||
|  | /** @see {@link ReplaySettings} */ | ||||||
|  | export const ReplaySettingsUI = new GameUI('replay-settings', ReplaySettings); | ||||||
|  | /** @see {@link GameInfo} */ | ||||||
|  | export const GameInfoUI = new GameUI('game-info', GameInfo); | ||||||
|  | /** @see {@link SyncSave} */ | ||||||
|  | export const SyncSaveUI = new GameUI('sync-save', SyncSave); | ||||||
|  | /** @see {@link SyncSaveSelect} */ | ||||||
|  | export const SyncSaveSelectUI = new GameUI('sync-save-select', SyncSaveSelect); | ||||||
|  | /** @see {@link DownloadSaveSelect} */ | ||||||
|  | export const DownloadSaveSelectUI = new GameUI( | ||||||
|  |     'download-save-select', | ||||||
|  |     DownloadSaveSelect | ||||||
|  | ); | ||||||
|  | /** @see {@link ClearSaveSelect} */ | ||||||
|  | export const ClearSaveSelectUI = new GameUI( | ||||||
|  |     'clear-save-select', | ||||||
|  |     ClearSaveSelect | ||||||
|  | ); | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { GameUI } from '@/core/system'; | import { GameUI, UIComponentProps } from '@/core/system'; | ||||||
| import { computed, defineComponent, ref, watch } from 'vue'; | import { computed, defineComponent, ref, watch } from 'vue'; | ||||||
| import { SetupComponentOptions, TextContent } from '../components'; | import { SetupComponentOptions, TextContent } from '../components'; | ||||||
| import { DefaultProps, ElementLocator, Sprite, Font } from '@/core/render'; | import { DefaultProps, ElementLocator, Sprite, Font } from '@/core/render'; | ||||||
| @ -34,7 +34,7 @@ export interface ILeftHeroStatus { | |||||||
|     magicDef: number; |     magicDef: number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface StatusBarProps<T> extends DefaultProps { | interface StatusBarProps<T> extends DefaultProps, UIComponentProps { | ||||||
|     loc: ElementLocator; |     loc: ElementLocator; | ||||||
|     status: T; |     status: T; | ||||||
|     hidden: boolean; |     hidden: boolean; | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								src/module/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/module/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | export * from './saves'; | ||||||
							
								
								
									
										35
									
								
								src/module/utils/saves.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/module/utils/saves.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | export function getAllSavesData() { | ||||||
|  |     return new Promise<string>(res => { | ||||||
|  |         core.getAllSaves(saves => { | ||||||
|  |             if (!saves) { | ||||||
|  |                 res(''); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             const content = { | ||||||
|  |                 name: core.firstData.name, | ||||||
|  |                 version: core.firstData.version, | ||||||
|  |                 data: saves | ||||||
|  |             }; | ||||||
|  |             // @ts-expect-error 暂时无法推导
 | ||||||
|  |             res(LZString.compressToBase64(JSON.stringify(content))); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getSaveData(index: number) { | ||||||
|  |     return new Promise<string>(res => { | ||||||
|  |         core.getSave(index, data => { | ||||||
|  |             if (!data) { | ||||||
|  |                 res(''); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             const content = { | ||||||
|  |                 name: core.firstData.name, | ||||||
|  |                 version: core.firstData.version, | ||||||
|  |                 data: data | ||||||
|  |             }; | ||||||
|  |             // @ts-expect-error 暂时无法推导
 | ||||||
|  |             res(LZString.compressToBase64(JSON.stringify(content))); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								src/types/core.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								src/types/core.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -657,7 +657,7 @@ interface CoreSave { | |||||||
|     /** |     /** | ||||||
|      * 自动存档信息 |      * 自动存档信息 | ||||||
|      */ |      */ | ||||||
|     autosave: Readonly<Autosave>; |     autosave: Autosave; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 收藏的存档 |      * 收藏的存档 | ||||||
| @ -679,7 +679,7 @@ interface Autosave { | |||||||
|     /** |     /** | ||||||
|      * 当前存档信息 |      * 当前存档信息 | ||||||
|      */ |      */ | ||||||
|     data?: Save[]; |     data?: Save[] | null; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 自动存档位的最大值 |      * 自动存档位的最大值 | ||||||
| @ -982,7 +982,7 @@ interface Core extends Pick<Main, CoreDataFromMain> { | |||||||
|     /** |     /** | ||||||
|      * 存档信息 |      * 存档信息 | ||||||
|      */ |      */ | ||||||
|     readonly saves: Readonly<CoreSave>; |     readonly saves: CoreSave; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 全局数值信息 |      * 全局数值信息 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								src/types/event.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/types/event.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -46,7 +46,7 @@ interface Events extends EventData { | |||||||
|     startGame( |     startGame( | ||||||
|         hard: string, |         hard: string, | ||||||
|         seed?: number, |         seed?: number, | ||||||
|         route?: string, |         route?: string[], | ||||||
|         callback?: () => void |         callback?: () => void | ||||||
|     ): void; |     ): void; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user