mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-07-18 11:01:48 +08:00
670 lines
22 KiB
TypeScript
670 lines
22 KiB
TypeScript
import { ElementLocator } from '@motajs/render';
|
|
import {
|
|
GameUI,
|
|
IUIMountable,
|
|
SetupComponentOptions,
|
|
UIComponentProps
|
|
} from '@motajs/system-ui';
|
|
import { defineComponent } from 'vue';
|
|
import {
|
|
ChoiceItem,
|
|
ChoiceKey,
|
|
Choices,
|
|
ChoicesProps,
|
|
getConfirm,
|
|
waitbox
|
|
} from '../components';
|
|
import { mainUi } from '@motajs/legacy-ui';
|
|
import { gameKey } from '@motajs/system-action';
|
|
import { generateKeyboardEvent } from '@motajs/system-action';
|
|
import { getVitualKeyOnce } from '@motajs/legacy-ui';
|
|
import { getAllSavesData, getSaveData, syncFromServer } from '../utils';
|
|
import { getInput } from '../components';
|
|
import { openStatistics } from './statistics';
|
|
import { saveWithExist } from './save';
|
|
import { compressToBase64 } from 'lz-string';
|
|
|
|
export interface MainSettingsProps
|
|
extends Partial<ChoicesProps>,
|
|
UIComponentProps {
|
|
loc: ElementLocator;
|
|
}
|
|
|
|
const mainSettingsProps = {
|
|
props: ['loc', 'controller', 'instance']
|
|
} satisfies SetupComponentOptions<MainSettingsProps>;
|
|
|
|
const enum MainChoice {
|
|
SystemSetting,
|
|
VirtualKey,
|
|
ViewMap,
|
|
/** @see {@link ReplaySettings} */
|
|
Replay,
|
|
/** @see {@link SyncSave} */
|
|
SyncSave,
|
|
/** @see {@link GameInfo} */
|
|
GameInfo,
|
|
Restart,
|
|
Back
|
|
}
|
|
|
|
export const MainSettings = defineComponent<MainSettingsProps>(props => {
|
|
const choices: ChoiceItem[] = [
|
|
[MainChoice.SystemSetting, '系统设置'],
|
|
[MainChoice.VirtualKey, '虚拟键盘'],
|
|
[MainChoice.ViewMap, '浏览地图'],
|
|
[MainChoice.Replay, '录像回放'],
|
|
[MainChoice.SyncSave, '同步存档'],
|
|
[MainChoice.GameInfo, '游戏信息'],
|
|
[MainChoice.Restart, '返回标题'],
|
|
[MainChoice.Back, '返回游戏']
|
|
];
|
|
|
|
const choose = async (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: {
|
|
const confirm = await getConfirm(
|
|
props.controller,
|
|
'确认要返回标题吗?',
|
|
[420, 240, void 0, void 0, 0.5, 0.5],
|
|
240
|
|
);
|
|
if (confirm) {
|
|
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}
|
|
maxHeight={400}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
const enum ReplayChoice {
|
|
Start,
|
|
StartFromSave,
|
|
ResumeReplay,
|
|
ReplayRest,
|
|
ChooseReplay,
|
|
Download,
|
|
Back
|
|
}
|
|
|
|
export const ReplaySettings = defineComponent<MainSettingsProps>(props => {
|
|
const choice: ChoiceItem[] = [
|
|
[ReplayChoice.Start, '从头回放录像'],
|
|
[ReplayChoice.StartFromSave, '从存档开始回放'],
|
|
[ReplayChoice.ResumeReplay, '接续播放剩余录像'],
|
|
[ReplayChoice.ReplayRest, '播放存档剩余录像'],
|
|
[ReplayChoice.ChooseReplay, '选择录像文件'],
|
|
[ReplayChoice.Download, '下载当前录像'],
|
|
[ReplayChoice.Back, '返回游戏']
|
|
];
|
|
|
|
const choose = async (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: {
|
|
const index = await saveWithExist(
|
|
props.controller,
|
|
[0, 0, 840, 480]
|
|
);
|
|
if (index === -2) break;
|
|
if (index === -1) {
|
|
core.doSL('autoSave', 'replayLoad');
|
|
} else {
|
|
core.doSL(index + 1, 'replayLoad');
|
|
}
|
|
props.controller.closeAll();
|
|
break;
|
|
}
|
|
case ReplayChoice.ResumeReplay: {
|
|
const index = await saveWithExist(
|
|
props.controller,
|
|
[0, 0, 840, 480]
|
|
);
|
|
if (index === -2) break;
|
|
const name = index === -1 ? 'autoSave' : index + 1;
|
|
const success = core.doSL(name, 'replayRemain');
|
|
if (!success) {
|
|
props.controller.closeAll();
|
|
break;
|
|
}
|
|
await getConfirm(
|
|
props.controller,
|
|
'[步骤2]请选择第二个存档。\n\r[yellow]该存档必须是前一个存档的后续。\r\n将尝试播放到此存档。',
|
|
[420, 240, void 0, void 0, 0.5, 0.5],
|
|
240
|
|
);
|
|
const index2 = await saveWithExist(
|
|
props.controller,
|
|
[0, 0, 840, 480]
|
|
);
|
|
if (index2 === -2) break;
|
|
const name2 = index2 === -1 ? 'autoSave' : index2 + 1;
|
|
core.doSL(name2, 'replayRemain');
|
|
props.controller.closeAll();
|
|
break;
|
|
}
|
|
case ReplayChoice.ReplayRest: {
|
|
const index = await saveWithExist(
|
|
props.controller,
|
|
[0, 0, 840, 480]
|
|
);
|
|
if (index === -2) break;
|
|
if (index === -1) {
|
|
core.doSL('autoSave', 'replaySince');
|
|
} else {
|
|
core.doSL(index + 1, 'replaySince');
|
|
}
|
|
props.controller.closeAll();
|
|
break;
|
|
}
|
|
case ReplayChoice.ChooseReplay: {
|
|
props.controller.closeAll();
|
|
core.chooseReplayFile();
|
|
break;
|
|
}
|
|
case ReplayChoice.Download: {
|
|
core.download(
|
|
core.firstData.name + '_' + core.formatDate2() + '.h5route',
|
|
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}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
const enum GameInfoChoice {
|
|
Statistics,
|
|
Project,
|
|
Tower,
|
|
Help,
|
|
Download,
|
|
Back
|
|
}
|
|
|
|
export const GameInfo = defineComponent<MainSettingsProps>(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: {
|
|
openStatistics(props.controller);
|
|
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}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
const enum SyncSaveChoice {
|
|
// ----- 主菜单
|
|
ToServer,
|
|
FromServer,
|
|
ToLocal,
|
|
FromLocal,
|
|
ClearLocal,
|
|
Back,
|
|
// ----- 子菜单
|
|
AllSaves,
|
|
NowSave
|
|
}
|
|
|
|
export const SyncSave = defineComponent<MainSettingsProps>(props => {
|
|
const choices: ChoiceItem[] = [
|
|
[SyncSaveChoice.ToServer, '同步存档至服务器'],
|
|
[SyncSaveChoice.FromServer, '从服务器加载存档'],
|
|
[SyncSaveChoice.ToLocal, '存档至本地文件'],
|
|
[SyncSaveChoice.FromLocal, '存本地文件读档'],
|
|
[SyncSaveChoice.ClearLocal, '清空本地存档'],
|
|
[SyncSaveChoice.Back, '返回上一级']
|
|
];
|
|
|
|
const choose = async (key: ChoiceKey) => {
|
|
switch (key) {
|
|
case SyncSaveChoice.ToServer: {
|
|
props.controller.open(SyncSaveSelectUI, { loc: props.loc });
|
|
break;
|
|
}
|
|
case SyncSaveChoice.FromServer: {
|
|
const replay = await getInput(
|
|
props.controller,
|
|
'请输入存档编号+密码',
|
|
[240, 240, void 0, void 0, 0.5, 0.5],
|
|
240
|
|
);
|
|
await syncFromServer(props.controller, replay);
|
|
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}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
export const SyncSaveSelect = defineComponent<MainSettingsProps>(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}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
export const DownloadSaveSelect = defineComponent<MainSettingsProps>(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}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
export const ClearSaveSelect = defineComponent<MainSettingsProps>(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}
|
|
interval={8}
|
|
/>
|
|
);
|
|
}, mainSettingsProps);
|
|
|
|
/** @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
|
|
);
|
|
|
|
export function openSettings(
|
|
controller: IUIMountable,
|
|
loc: ElementLocator,
|
|
props?: MainSettingsProps
|
|
) {
|
|
controller.open(MainSettingsUI, {
|
|
...props,
|
|
loc
|
|
});
|
|
}
|