Browse Source

加强存档校验,系统设置新增游戏加速

2.0
许孟阳 3 weeks ago
parent
commit
1c1f3df89b
  1. 31
      src/config/i18n/zh/index.ts
  2. 1
      src/config/i18n/zh/setting.ts
  3. 6
      src/store/action.ts
  4. 3
      src/store/mutation.ts
  5. 7
      src/store/state.ts
  6. 56
      src/tool/archive.ts
  7. 55
      src/views/archive.vue
  8. 1
      src/views/backpack/equip-menu/fusion/equip.vue
  9. 2
      src/views/backpack/equip-menu/fusion/fusion.vue
  10. 3
      src/views/backpack/equip-menu/reforge.vue
  11. 3
      src/views/backpack/equip-menu/strengthen.vue
  12. 7
      src/views/save-game.vue
  13. 13
      src/views/setting.vue
  14. 7
      src/views/version/update-log.vue

31
src/config/i18n/zh/index.ts

@ -92,8 +92,9 @@ export default class Zh {
desc: [ desc: [
'融合说明', '融合说明',
'消耗同名太古装备可进行装备融合', '消耗同名太古装备可进行装备融合',
'未融合的太古作为材料可以添加或重置一星融合属性', '一星太古可消耗任意材料重置融合属性类型',
'一星或二星太古做材料可以给同星且融合属性类型相同的太古升星', '一星以上太古可消耗同星切融合属性类型相同的材料升星,最多升为三星',
'融合属性品质为一星:神话,二星:远古,三星:太古',
], ],
confirm: ['融合后作为材料的装备将会消失,确认要融合吗?', '确定', '算了'], confirm: ['融合后作为材料的装备将会消失,确认要融合吗?', '确定', '算了'],
empty: '无融合属性', empty: '无融合属性',
@ -174,17 +175,21 @@ export default class Zh {
addPoints = '左键加+1,右键+10'; addPoints = '左键加+1,右键+10';
subtractPoints = '左键-1,右键-10'; subtractPoints = '左键-1,右键-10';
saveGame = ['保存游戏(Ctrl+S)', '每5分钟会自动保存游戏一次', '游戏进度已经保存了。']; archive = {
archive = ['存档管理(A)', '导入存档前请将存档内容粘贴到输入框内']; empty: '未读取到存档!',
copyArchive = ['复制存档', '已经复制存档了,建议保存到备忘录', '复制存档失败', '复制旧存档']; error: '存档坏了!',
pasteArchive = ['粘贴存档', '粘贴存档内容到输入框成功', '粘贴失败']; load: '读取存档成功',
cleanArchive = ['删除存档']; menu: ['存档管理(A)', '导入存档前请将存档内容粘贴到输入框内'],
uploadArchive = '上传存档'; copy: ['复制存档', '已经复制存档了,建议保存到备忘录', '复制存档失败', '复制旧存档'],
downArchive = '下载存档'; paste: ['粘贴存档', '粘贴存档内容到输入框成功', '粘贴失败'],
importArchive = ['导入', '导入存档成功,继续游戏吧!', '导入存档失败', '存档版本已过期,无法导入!']; clean: ['删除存档'],
experiential = ['导入体验存档成功,体验时间${0}分钟。', '体验存档无法保存!', '体验时间已过,自动读取本地存档。']; upload: '上传存档',
down: '下载存档',
music = ['播放或禁音背景音乐(M)']; import: ['导入', '导入存档成功,继续游戏吧!', '导入存档失败', '存档版本已过期,无法导入!'],
experiential: ['导入体验存档成功,体验时间${0}分钟。', '体验存档无法保存!', '体验时间已过,自动读取本地存档。'],
save: ['保存游戏(Ctrl+S)', '每5分钟会自动保存游戏一次', '游戏进度已经保存了。', '保存游戏失败!'],
check: ['存档已更新,保存游戏10分钟内无法回档!'],
};
illustrated = ['装备图鉴(I)']; illustrated = ['装备图鉴(I)'];

1
src/config/i18n/zh/setting.ts

@ -2,3 +2,4 @@ export const title = '系统设置(ESC)';
export const action = ['保存设置', '取消']; export const action = ['保存设置', '取消'];
export const music = ['背景音乐', '完全关闭', '最小音量', '正常音量']; export const music = ['背景音乐', '完全关闭', '最小音量', '正常音量'];
export const battle = ['战斗日志', '显示', '隐藏']; export const battle = ['战斗日志', '显示', '隐藏'];
export const speed = ['游戏加速', '正常', '倍'];

6
src/store/action.ts

@ -70,8 +70,9 @@ export const saveGame = ({ state, commit }) => {
if (state.experiential) { if (state.experiential) {
commit('set_sys_info', { msg: t('experiential.1'), type: 'warning' }); commit('set_sys_info', { msg: t('experiential.1'), type: 'warning' });
} else { } else {
saveArchive(state); saveArchive(state).then((msg) => {
commit('set_sys_info', { msg: t('saveGame.2'), type: 'win' }); commit('set_sys_info', msg);
});
} }
}; };
@ -90,6 +91,7 @@ const loadArchive = (commit, data) => {
} else if (data == 'undfind') { } else if (data == 'undfind') {
commit('set_sys_info', { msg: t('loadError'), type: 'warning' }); commit('set_sys_info', { msg: t('loadError'), type: 'warning' });
} else { } else {
commit('set_archive_flag', data.flag);
commit('set_experiential', data.experiential); commit('set_experiential', data.experiential);
commit('set_player_equips', data.equips); commit('set_player_equips', data.equips);
commit('set_player_layer', data.layer || 1); commit('set_player_layer', data.layer || 1);

3
src/store/mutation.ts

@ -127,3 +127,6 @@ export const add_player_curhp = (state, hp) => {
export const set_experiential = (state, data) => { export const set_experiential = (state, data) => {
state.experiential = data || false; state.experiential = data || false;
}; };
export const set_archive_flag = (state, data) => {
data && (state.archiveFlag = data);
};

7
src/store/state.ts

@ -1,4 +1,5 @@
import { i18n, Player, Points, BaseAttribute } from '@/config'; import { i18n, Player, Points, BaseAttribute } from '@/config';
import { uuid } from '@/tool';
const { t } = i18n; const { t } = i18n;
@ -7,7 +8,13 @@ export default {
{ type: 'win', msg: t('welcome.0') }, { type: 'win', msg: t('welcome.0') },
{ type: 'win', msg: t('welcome.1') }, { type: 'win', msg: t('welcome.1') },
], ],
archiveFlag: {
id: uuid(),
seq: 0,
time: 0,
},
battleLog: 1, //是否显示战斗日志 battleLog: 1, //是否显示战斗日志
speed: 1, //游戏加速
mobile: window.innerWidth < 768, mobile: window.innerWidth < 768,
curMenu: null, curMenu: null,
equipTip: { equipTip: {

56
src/tool/archive.ts

@ -1,8 +1,10 @@
import { Equip, Player, Points } from '@/config'; import { Equip, Player, Points } from '@/config';
import { getFromStore, insertToStore, store_name_archive } from './IndexedDB'; import { getFromStore, insertToStore, store_name_archive } from './IndexedDB';
import { uuid } from './random'; import { uuid } from './random';
import { createt } from '@/config/i18n';
export const archive_version = '1.0'; export const archive_version = '1.0';
const archive_version_strengthen = '1.0_flag'; const archive_version_strengthen = '1.0_flag';
const at = createt('archive.');
export class GameArchive { export class GameArchive {
version: String; version: String;
@ -14,41 +16,51 @@ export class GameArchive {
autoSell: string[]; autoSell: string[];
shop: any[]; shop: any[];
points: Points; points: Points;
flag;
constructor(player: Player, grid: any[], autoSell: any[], shop: any[], points: Points) { constructor(state) {
this.version = archive_version; this.version = archive_version;
const player = state.playerAttribute;
this.equips = [player.weapon, player.armor, player.ring, player.neck, player.jewelry, player.pants, player.shoes, player.bracers]; this.equips = [player.weapon, player.armor, player.ring, player.neck, player.jewelry, player.pants, player.shoes, player.bracers];
this.lv = player.lv; this.lv = player.lv;
this.layer = player.layer; this.layer = player.layer;
this.coins = player.coins; this.coins = player.coins;
this.grid = grid; this.grid = state.grid;
this.autoSell = autoSell; this.autoSell = state.autoSell;
this.shop = shop; this.shop = state.shop;
this.points = points; this.points = state.points;
this.flag = state.archiveFlag;
} }
} }
export const saveArchive = (state) => { export const checkArchive = (flag) => {
const archive = new GameArchive(state.playerAttribute, state.grid, state.autoSell, state.shop, state.points); return new Promise((resolve, reject) => {
getFromStore(store_name_archive, archive_version_strengthen).then((flag: any) => { getArchive().then((rsp: any) => {
const flago = rsp && rsp.flag;
const time = new Date().getTime(); const time = new Date().getTime();
if (!flag || !flag.time || flag.time + 10 * 60 * 1000 < time) { if (flago && flago.id == flag.id && flag.seq < flago.seq && flago.time + 10 * 60 * 1000 > time) {
flag = { version: archive_version_strengthen, time: time }; resolve(false);
} } else {
const equips = new Array(); resolve(true);
Array.prototype.push.apply(equips, archive.equips);
Array.prototype.push.apply(equips, archive.grid);
equips.forEach((equip) => {
if (!equip) return;
if (equip.strengthenLv > 0 && !equip.id) {
equip.id = uuid();
} }
if (equip.id) { });
flag[equip.id] = equip.strengthenLv; });
};
export const saveArchive = (state) => {
return new Promise((resolve, reject) => {
checkArchive(state.archiveFlag).then((rsp) => {
if (rsp) {
state.archiveFlag.time = new Date().getTime();
state.archiveFlag.seq++;
const archive = new GameArchive(state);
insertToStore(store_name_archive, archive).then((rsp) => {
resolve({ msg: at(rsp ? 'save.2' : 'save.3'), type: 'win' });
});
} else {
resolve({ msg: at('check.0'), type: 'warning' });
} }
}); });
insertToStore(store_name_archive, archive);
insertToStore(store_name_archive, flag);
}); });
}; };

55
src/views/archive.vue

@ -1,21 +1,21 @@
<template> <template>
<Tooltip :infos="[t('archive.0')]" width="12rem"> <Tooltip :infos="[at('menu.0')]" width="12rem">
<img class="menu-img" :src="menu_icons.exportGame" @click="showMenu"> <img class="menu-img" :src="menu_icons.exportGame" @click="showMenu">
</Tooltip> </Tooltip>
<Dialog :title="t('archive.0')" v-model="showArchive" top="4rem" left='8%' @close="state.curMenu = null"> <Dialog :title="at('menu.0')" v-model="showArchive" top="4rem" left='8%' @close="state.curMenu = null">
<div class="archive"> <div class="archive">
<span class="tip">* {{ t('archive.1') }}</span> <span class="tip">* {{ at('menu.1') }}</span>
<textarea class="textarea" v-model="archive"></textarea> <textarea class="textarea" v-model="archive"></textarea>
</div> </div>
<div class="footer"> <div class="footer">
<button class="button" @click="copyArchive(archive)">{{ t('copyArchive.0') }}</button> <button class="button" @click="copyArchive(archive)">{{ at('copy.0') }}</button>
<button class="button" @click="pasteArchive">{{ t('pasteArchive.0') }}</button> <button class="button" @click="pasteArchive">{{ at('paste.0') }}</button>
<button class="button" @click="importArchive">{{ t('importArchive.0') }}</button> <button class="button" @click="importArchive">{{ at('import.0') }}</button>
</div> </div>
<div class="footer"> <div class="footer">
<button class="button" @click="uploadArchive">{{ t('uploadArchive') }}</button> <button class="button" @click="uploadArchive">{{ at('upload') }}</button>
<button class="button" @click="downArchive">{{ t('downArchive') }}</button> <button class="button" @click="downArchive">{{ at('down') }}</button>
</div> </div>
</Dialog> </Dialog>
@ -27,10 +27,12 @@ import { computed, onMounted, ref, onBeforeUnmount } from "vue";
import { useI18n } from "vue3-i18n"; import { useI18n } from "vue3-i18n";
import { Tooltip, Dialog } from "@/components" import { Tooltip, Dialog } from "@/components"
import { menu_icons } from "@/config"; import { menu_icons } from "@/config";
import { getArchive, AES_CBC_ENCRYPT, AES_CBC_DECRYPT, checkImportArchive, saveArchive, replace, archive_version, MD5 } from "@/tool"; import { getArchive, AES_CBC_ENCRYPT, AES_CBC_DECRYPT, checkImportArchive, saveArchive, replace, archive_version, MD5, checkArchive } from "@/tool";
import * as API from "@/api"; import * as API from "@/api";
import { createt } from "@/config/i18n";
const { t } = useI18n(); // const { t } = useI18n();
const at = createt('archive.');
const { state, commit, dispatch } = useStore(); const { state, commit, dispatch } = useStore();
const showArchive = computed(() => { const showArchive = computed(() => {
return state.curMenu == 'archive'; return state.curMenu == 'archive';
@ -62,10 +64,10 @@ const showMenu = () => {
const copyArchive = (archive) => { const copyArchive = (archive) => {
if (navigator.clipboard && window.isSecureContext) { if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(archive).then(() => { navigator.clipboard.writeText(archive).then(() => {
commit('set_sys_info', { msg: t('copyArchive.1'), type: 'win' }) commit('set_sys_info', { msg: at('copy.1'), type: 'win' })
showMenu(); showMenu();
}).catch(() => { }).catch(() => {
commit('set_sys_info', { msg: t('copyArchive.2'), type: 'waring' }) commit('set_sys_info', { msg: at('copy.2'), type: 'waring' })
}); });
} else { } else {
const textField = document.createElement("textarea"); const textField = document.createElement("textarea");
@ -74,7 +76,7 @@ const copyArchive = (archive) => {
textField.select(); textField.select();
document.execCommand("copy"); document.execCommand("copy");
textField.remove(); textField.remove();
commit('set_sys_info', { msg: t('copyArchive.1'), type: 'win' }) commit('set_sys_info', { msg: at('copy.1'), type: 'win' })
showMenu(); showMenu();
} }
} }
@ -112,10 +114,10 @@ const pasteArchive = () => {
if (result.state === 'granted') { if (result.state === 'granted') {
navigator.clipboard.readText().then(text => { navigator.clipboard.readText().then(text => {
archive.value = text; archive.value = text;
commit('set_sys_info', { msg: t('pasteArchive.1'), type: 'win' }) commit('set_sys_info', { msg: at('paste.1'), type: 'win' })
}); });
} else { } else {
commit('set_sys_info', { msg: t('pasteArchive.2'), type: 'waring' }) commit('set_sys_info', { msg: at('paste.2'), type: 'waring' })
} }
}); });
} }
@ -124,29 +126,40 @@ const importArchive = async () => {
try { try {
const data = JSON.parse(AES_CBC_DECRYPT(archive.value, key)); const data = JSON.parse(AES_CBC_DECRYPT(archive.value, key));
if (!data || data == 'undfind') { if (!data || data == 'undfind') {
commit('set_sys_info', { msg: t('loadEmpty'), type: 'warning' }); commit('set_sys_info', { msg: at('empty'), type: 'warning' });
} }
if (data.version != archive_version) { if (data.version != archive_version) {
commit('set_sys_info', { msg: t('importArchive.3'), type: 'warning' }); commit('set_sys_info', { msg: at('import.3'), type: 'warning' });
return; return;
} }
await checkImportArchive(data); const flag = data.flag || state.archiveFlag;
console.log(flag);
checkArchive(flag).then(async rsp => {
if (!rsp) {
commit('set_sys_info', { msg: at('check.0'), type: 'warning' });
return;
} else {
await dispatch('loadGame', data); await dispatch('loadGame', data);
if (state.experiential) { if (state.experiential) {
commit('set_sys_info', { msg: replace(t('experiential.0'), [data.experientialTime]), type: 'win' }); commit('set_sys_info', { msg: replace(at('experiential.0'), [data.experientialTime]), type: 'win' });
const time = data.experientialTime || 10; const time = data.experientialTime || 10;
clearTimeout(timeOut); clearTimeout(timeOut);
timeOut = setTimeout(() => { timeOut = setTimeout(() => {
commit('set_sys_info', { msg: replace(t('experiential.2'), [data.experientialTime]), type: 'warning' }); commit('set_sys_info', { msg: replace(at('experiential.2'), [data.experientialTime]), type: 'warning' });
dispatch('loadGame'); dispatch('loadGame');
}, time * 60 * 100); }, time * 60 * 100);
} else { } else {
saveArchive(state); saveArchive(state);
} }
showMenu(); showMenu();
}
})
// await checkImportArchive(data);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
commit('set_sys_info', { msg: t('loadError'), type: 'warning' }); commit('set_sys_info', { msg: at('error'), type: 'warning' });
} }
} }

1
src/views/backpack/equip-menu/fusion/equip.vue

@ -40,6 +40,7 @@ onMounted(() => { });
<style lang="scss" scoped> <style lang="scss" scoped>
.fusion-equip { .fusion-equip {
display: flex; display: flex;
margin-bottom: 1rem;
} }
.entry { .entry {

2
src/views/backpack/equip-menu/fusion/fusion.vue

@ -3,7 +3,7 @@
<div class="coins"> <div class="coins">
{{ t('coins.0') }}:{{ userCoins }} {{ t('coins.0') }}:{{ userCoins }}
</div> </div>
<Tooltip :infos="[ft('desc.1'), ft('desc.2'), ft('desc.3')]" width="8rem"> <Tooltip :infos="[ft('desc.1'), ft('desc.2'), ft('desc.3'), ft('desc.4')]" width="8rem">
<div class="descript">- {{ ft('desc.0') }} -</div> <div class="descript">- {{ ft('desc.0') }} -</div>
</Tooltip> </Tooltip>
<Equip :equip="equip" /> <Equip :equip="equip" />

3
src/views/backpack/equip-menu/reforge.vue

@ -130,7 +130,7 @@ const reforge = () => {
tempEntry.value = extraEntry; tempEntry.value = extraEntry;
reforgeing.value = true; reforgeing.value = true;
check(); check();
auto.value && setTimeout(reforge, 200); auto.value && setTimeout(reforge, 200 / state.speed);
} }
const saveOld = () => { const saveOld = () => {
@ -213,5 +213,4 @@ onMounted(() => { });
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
</style> </style>

3
src/views/backpack/equip-menu/strengthen.vue

@ -110,7 +110,7 @@ const strengthen = (test?) => {
} }
commit("add_player_coins", -1 * needCoins.value); commit("add_player_coins", -1 * needCoins.value);
auto.value && setTimeout(strengthen, 200); auto.value && setTimeout(strengthen, 200 / state.speed);
}; };
const startAuto = () => { const startAuto = () => {
auto.value = true; auto.value = true;
@ -158,5 +158,4 @@ onMounted(() => { });
color: red; color: red;
} }
} }
</style> </style>

7
src/views/save-game.vue

@ -1,5 +1,5 @@
<template> <template>
<Tooltip :infos="[t('saveGame.0'), t('saveGame.1')]" width="12rem"> <Tooltip :infos="[at('save.0'), at('save.1')]" width="12rem">
<img class="menu-img" :src="menu_icons.saveGame" @click="saveGame"> <img class="menu-img" :src="menu_icons.saveGame" @click="saveGame">
</Tooltip> </Tooltip>
</template> </template>
@ -7,12 +7,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useStore } from "vuex"; import { useStore } from "vuex";
import { onBeforeUnmount, onMounted, ref } from "vue"; import { onBeforeUnmount, onMounted, ref } from "vue";
import { useI18n } from "vue3-i18n";
import { Tooltip } from "@/components" import { Tooltip } from "@/components"
import { menu_icons } from "@/config"; import { menu_icons } from "@/config";
import { Base64 } from "js-base64"; import { createt } from "@/config/i18n";
const { t } = useI18n(); const at = createt('archive.');
const { state, commit, dispatch } = useStore(); const { state, commit, dispatch } = useStore();
let interval = 0; let interval = 0;

13
src/views/setting.vue

@ -21,6 +21,15 @@
<input type="radio" :value="0" v-model="settings.battle"> {{ t('battle.2') }} <input type="radio" :value="0" v-model="settings.battle"> {{ t('battle.2') }}
</div> </div>
</div> </div>
<div class="item">
<div class="label">{{ t('speed.0') }}</div>
<div class="options">
<input type="radio" :value="1" v-model="settings.speed"> {{ t('speed.1') }}
<input type="radio" :value="10" v-model="settings.speed"> 10{{ t('speed.2') }}
<input type="radio" :value="50" v-model="settings.speed"> 50{{ t('speed.2') }}
<input type="radio" :value="100" v-model="settings.speed"> 100{{ t('speed.2') }}
</div>
</div>
</div> </div>
<div class="footer"> <div class="footer">
<button class="button" @click="saveSettings">{{ t('action.0') }}</button> <button class="button" @click="saveSettings">{{ t('action.0') }}</button>
@ -46,7 +55,8 @@ const show = computed(() => {
const settings = ref({ const settings = ref({
type: 'setting', type: 'setting',
music: 0.01, music: 0.01,
battle: 1 battle: 1,
speed: 1,
}) })
const saveSettings = () => { const saveSettings = () => {
@ -58,6 +68,7 @@ const saveSettings = () => {
const set = () => { const set = () => {
dispatch('set_music_volume', settings.value.music); dispatch('set_music_volume', settings.value.music);
state.battleLog = settings.value.battle; state.battleLog = settings.value.battle;
state.speed = settings.value.speed || 1;
} }
const readSetting = () => { const readSetting = () => {

7
src/views/version/update-log.vue

@ -38,6 +38,13 @@ const hisVersions = [
] ]
const updateLogs: any = [{ const updateLogs: any = [{
date: '2025-05-27', version: '1.0',
adjust: [
'新增太古装备融合功能',
'完善存档校验',
'系统设置新增游戏加速',
],
}, {
date: '2025-05-26', version: '1.0', date: '2025-05-26', version: '1.0',
adjust: [ adjust: [
'新增神话武器:银河裂', '新增神话武器:银河裂',

Loading…
Cancel
Save