465 lines
11 KiB
JavaScript
465 lines
11 KiB
JavaScript
|
|
/**
|
|||
|
|
* 存档系统 - 保存、加载、重置游戏数据
|
|||
|
|
* Phase 7 核心实现
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { resetSkillSystem } from './skillSystem.js'
|
|||
|
|
|
|||
|
|
// 存档版本(用于版本控制和迁移)
|
|||
|
|
const SAVE_VERSION = 1
|
|||
|
|
const SAVE_KEY = 'wwa3_save_data'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 保存游戏数据
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @returns {boolean} 是否保存成功
|
|||
|
|
*/
|
|||
|
|
export function saveGame(gameStore, playerStore) {
|
|||
|
|
try {
|
|||
|
|
// 构建存档数据
|
|||
|
|
const saveData = {
|
|||
|
|
version: SAVE_VERSION,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
player: serializePlayerData(playerStore),
|
|||
|
|
game: serializeGameData(gameStore)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用uni.setStorageSync同步保存
|
|||
|
|
uni.setStorageSync(SAVE_KEY, JSON.stringify(saveData))
|
|||
|
|
|
|||
|
|
console.log('Game saved successfully')
|
|||
|
|
return true
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to save game:', error)
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 加载游戏数据
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @returns {Object} { success: boolean, isNewGame: boolean, version: number }
|
|||
|
|
*/
|
|||
|
|
export function loadGame(gameStore, playerStore) {
|
|||
|
|
try {
|
|||
|
|
// 读取存档
|
|||
|
|
const saveDataRaw = uni.getStorageSync(SAVE_KEY)
|
|||
|
|
|
|||
|
|
if (!saveDataRaw) {
|
|||
|
|
console.log('No save data found, starting new game')
|
|||
|
|
return { success: false, isNewGame: true, version: 0 }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const saveData = JSON.parse(saveDataRaw)
|
|||
|
|
|
|||
|
|
// 验证存档版本
|
|||
|
|
if (saveData.version > SAVE_VERSION) {
|
|||
|
|
console.warn('Save version is newer than game version')
|
|||
|
|
return { success: false, isNewGame: false, error: 'version_mismatch' }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 应用存档数据
|
|||
|
|
applySaveData(gameStore, playerStore, saveData)
|
|||
|
|
|
|||
|
|
console.log(`Game loaded successfully (version ${saveData.version})`)
|
|||
|
|
return { success: true, isNewGame: false, version: saveData.version }
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to load game:', error)
|
|||
|
|
return { success: false, isNewGame: true, error: error.message }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重置游戏数据
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @returns {boolean} 是否重置成功
|
|||
|
|
*/
|
|||
|
|
export function resetGame(gameStore, playerStore) {
|
|||
|
|
try {
|
|||
|
|
// 重置玩家数据
|
|||
|
|
playerStore.$reset?.() || resetPlayerData(playerStore)
|
|||
|
|
|
|||
|
|
// 重置游戏数据
|
|||
|
|
gameStore.$reset?.() || resetGameData(gameStore)
|
|||
|
|
|
|||
|
|
// 删除存档
|
|||
|
|
uni.removeStorageSync(SAVE_KEY)
|
|||
|
|
|
|||
|
|
console.log('Game reset successfully')
|
|||
|
|
return true
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to reset game:', error)
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用存档数据到Store
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @param {Object} saveData - 存档数据
|
|||
|
|
*/
|
|||
|
|
export function applySaveData(gameStore, playerStore, saveData) {
|
|||
|
|
// 应用玩家数据
|
|||
|
|
if (saveData.player) {
|
|||
|
|
applyPlayerData(playerStore, saveData.player)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 应用游戏数据
|
|||
|
|
if (saveData.game) {
|
|||
|
|
applyGameData(gameStore, saveData.game)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 版本迁移(如果需要)
|
|||
|
|
if (saveData.version < SAVE_VERSION) {
|
|||
|
|
migrateSaveData(saveData.version, SAVE_VERSION, gameStore, playerStore)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 序列化玩家数据
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @returns {Object} 序列化的数据
|
|||
|
|
*/
|
|||
|
|
function serializePlayerData(playerStore) {
|
|||
|
|
return {
|
|||
|
|
baseStats: { ...playerStore.baseStats },
|
|||
|
|
currentStats: { ...playerStore.currentStats },
|
|||
|
|
level: { ...playerStore.level },
|
|||
|
|
skills: JSON.parse(JSON.stringify(playerStore.skills || {})),
|
|||
|
|
equipment: JSON.parse(JSON.stringify(playerStore.equipment || {})),
|
|||
|
|
inventory: JSON.parse(JSON.stringify(playerStore.inventory || [])),
|
|||
|
|
currency: { ...playerStore.currency },
|
|||
|
|
currentLocation: playerStore.currentLocation,
|
|||
|
|
flags: { ...playerStore.flags },
|
|||
|
|
// 额外数据
|
|||
|
|
milestoneBonuses: playerStore.milestoneBonuses ? JSON.parse(JSON.stringify(playerStore.milestoneBonuses)) : {},
|
|||
|
|
globalBonus: playerStore.globalBonus ? { ...playerStore.globalBonus } : null,
|
|||
|
|
equipmentStats: playerStore.equipmentStats ? { ...playerStore.equipmentStats } : null,
|
|||
|
|
killCount: playerStore.killCount ? { ...playerStore.killCount } : {}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 序列化游戏数据
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @returns {Object} 序列化的数据
|
|||
|
|
*/
|
|||
|
|
function serializeGameData(gameStore) {
|
|||
|
|
return {
|
|||
|
|
currentTab: gameStore.currentTab,
|
|||
|
|
gameTime: { ...gameStore.gameTime },
|
|||
|
|
activeTasks: JSON.parse(JSON.stringify(gameStore.activeTasks || [])),
|
|||
|
|
negativeStatus: JSON.parse(JSON.stringify(gameStore.negativeStatus || [])),
|
|||
|
|
marketPrices: JSON.parse(JSON.stringify(gameStore.marketPrices || {})),
|
|||
|
|
// 不保存日志(logs不持久化)
|
|||
|
|
// 不保存战斗状态(重新登录时退出战斗)
|
|||
|
|
// 不保存当前事件(重新登录时清除)
|
|||
|
|
scheduledEvents: JSON.parse(JSON.stringify(gameStore.scheduledEvents || []))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用玩家数据
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @param {Object} data - 序列化的玩家数据
|
|||
|
|
*/
|
|||
|
|
function applyPlayerData(playerStore, data) {
|
|||
|
|
// 基础属性
|
|||
|
|
if (data.baseStats) {
|
|||
|
|
Object.assign(playerStore.baseStats, data.baseStats)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 当前资源
|
|||
|
|
if (data.currentStats) {
|
|||
|
|
Object.assign(playerStore.currentStats, data.currentStats)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 等级
|
|||
|
|
if (data.level) {
|
|||
|
|
Object.assign(playerStore.level, data.level)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 技能
|
|||
|
|
if (data.skills) {
|
|||
|
|
playerStore.skills = JSON.parse(JSON.stringify(data.skills))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 装备
|
|||
|
|
if (data.equipment) {
|
|||
|
|
playerStore.equipment = JSON.parse(JSON.stringify(data.equipment))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 背包
|
|||
|
|
if (data.inventory) {
|
|||
|
|
playerStore.inventory = JSON.parse(JSON.stringify(data.inventory))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 货币
|
|||
|
|
if (data.currency) {
|
|||
|
|
Object.assign(playerStore.currency, data.currency)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 位置
|
|||
|
|
if (data.currentLocation !== undefined) {
|
|||
|
|
playerStore.currentLocation = data.currentLocation
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 标志
|
|||
|
|
if (data.flags) {
|
|||
|
|
Object.assign(playerStore.flags, data.flags)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 额外数据
|
|||
|
|
if (data.milestoneBonuses) {
|
|||
|
|
playerStore.milestoneBonuses = JSON.parse(JSON.stringify(data.milestoneBonuses))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data.globalBonus) {
|
|||
|
|
playerStore.globalBonus = { ...data.globalBonus }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data.equipmentStats) {
|
|||
|
|
playerStore.equipmentStats = { ...data.equipmentStats }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data.killCount) {
|
|||
|
|
playerStore.killCount = { ...data.killCount }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用游戏数据
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} data - 序列化的游戏数据
|
|||
|
|
*/
|
|||
|
|
function applyGameData(gameStore, data) {
|
|||
|
|
// 当前标签
|
|||
|
|
if (data.currentTab !== undefined) {
|
|||
|
|
gameStore.currentTab = data.currentTab
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 游戏时间
|
|||
|
|
if (data.gameTime) {
|
|||
|
|
Object.assign(gameStore.gameTime, data.gameTime)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 活动任务
|
|||
|
|
if (data.activeTasks) {
|
|||
|
|
gameStore.activeTasks = JSON.parse(JSON.stringify(data.activeTasks))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 负面状态
|
|||
|
|
if (data.negativeStatus) {
|
|||
|
|
gameStore.negativeStatus = JSON.parse(JSON.stringify(data.negativeStatus))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 市场价格
|
|||
|
|
if (data.marketPrices) {
|
|||
|
|
gameStore.marketPrices = JSON.parse(JSON.stringify(data.marketPrices))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 定时事件
|
|||
|
|
if (data.scheduledEvents) {
|
|||
|
|
gameStore.scheduledEvents = JSON.parse(JSON.stringify(data.scheduledEvents))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除不持久化的状态
|
|||
|
|
gameStore.logs = []
|
|||
|
|
gameStore.inCombat = false
|
|||
|
|
gameStore.combatState = null
|
|||
|
|
gameStore.currentEvent = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重置玩家数据
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
*/
|
|||
|
|
function resetPlayerData(playerStore) {
|
|||
|
|
// 基础属性
|
|||
|
|
playerStore.baseStats = {
|
|||
|
|
strength: 10,
|
|||
|
|
agility: 8,
|
|||
|
|
dexterity: 8,
|
|||
|
|
intuition: 10,
|
|||
|
|
vitality: 10
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 当前资源
|
|||
|
|
playerStore.currentStats = {
|
|||
|
|
health: 100,
|
|||
|
|
maxHealth: 100,
|
|||
|
|
stamina: 100,
|
|||
|
|
maxStamina: 100,
|
|||
|
|
sanity: 100,
|
|||
|
|
maxSanity: 100
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 等级
|
|||
|
|
playerStore.level = {
|
|||
|
|
current: 1,
|
|||
|
|
exp: 0,
|
|||
|
|
maxExp: 100
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 技能
|
|||
|
|
playerStore.skills = {}
|
|||
|
|
|
|||
|
|
// 装备
|
|||
|
|
playerStore.equipment = {
|
|||
|
|
weapon: null,
|
|||
|
|
armor: null,
|
|||
|
|
shield: null,
|
|||
|
|
accessory: null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 背包
|
|||
|
|
playerStore.inventory = []
|
|||
|
|
|
|||
|
|
// 货币
|
|||
|
|
playerStore.currency = { copper: 0 }
|
|||
|
|
|
|||
|
|
// 位置
|
|||
|
|
playerStore.currentLocation = 'camp'
|
|||
|
|
|
|||
|
|
// 标志
|
|||
|
|
playerStore.flags = {}
|
|||
|
|
|
|||
|
|
// 清空额外数据
|
|||
|
|
resetSkillSystem(playerStore)
|
|||
|
|
delete playerStore.equipmentStats
|
|||
|
|
delete playerStore.killCount
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重置游戏数据
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
*/
|
|||
|
|
function resetGameData(gameStore) {
|
|||
|
|
gameStore.currentTab = 'status'
|
|||
|
|
gameStore.drawerState = {
|
|||
|
|
inventory: false,
|
|||
|
|
event: false
|
|||
|
|
}
|
|||
|
|
gameStore.logs = []
|
|||
|
|
gameStore.gameTime = {
|
|||
|
|
day: 1,
|
|||
|
|
hour: 8,
|
|||
|
|
minute: 0,
|
|||
|
|
totalMinutes: 480
|
|||
|
|
}
|
|||
|
|
gameStore.inCombat = false
|
|||
|
|
gameStore.combatState = null
|
|||
|
|
gameStore.activeTasks = []
|
|||
|
|
gameStore.negativeStatus = []
|
|||
|
|
gameStore.marketPrices = {
|
|||
|
|
lastRefreshDay: 1,
|
|||
|
|
prices: {}
|
|||
|
|
}
|
|||
|
|
gameStore.currentEvent = null
|
|||
|
|
gameStore.scheduledEvents = []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 版本迁移
|
|||
|
|
* @param {Number} fromVersion - 源版本
|
|||
|
|
* @param {Number} toVersion - 目标版本
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
*/
|
|||
|
|
function migrateSaveData(fromVersion, toVersion, gameStore, playerStore) {
|
|||
|
|
console.log(`Migrating save from v${fromVersion} to v${toVersion}`)
|
|||
|
|
|
|||
|
|
// 版本1暂无迁移需求
|
|||
|
|
// 未来版本可以在这里添加迁移逻辑
|
|||
|
|
|
|||
|
|
console.log('Save migration completed')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查是否存在存档
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
export function hasSaveData() {
|
|||
|
|
try {
|
|||
|
|
const saveData = uni.getStorageSync(SAVE_KEY)
|
|||
|
|
return !!saveData
|
|||
|
|
} catch (error) {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取存档信息
|
|||
|
|
* @returns {Object|null} 存档信息
|
|||
|
|
*/
|
|||
|
|
export function getSaveInfo() {
|
|||
|
|
try {
|
|||
|
|
const saveDataRaw = uni.getStorageSync(SAVE_KEY)
|
|||
|
|
if (!saveDataRaw) return null
|
|||
|
|
|
|||
|
|
const saveData = JSON.parse(saveDataRaw)
|
|||
|
|
return {
|
|||
|
|
version: saveData.version,
|
|||
|
|
timestamp: saveData.timestamp,
|
|||
|
|
playTime: saveData.timestamp,
|
|||
|
|
gameTime: saveData.game?.gameTime
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 导出存档(字符串)
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @returns {string|null} 存档字符串
|
|||
|
|
*/
|
|||
|
|
export function exportSave(gameStore, playerStore) {
|
|||
|
|
try {
|
|||
|
|
const saveData = {
|
|||
|
|
version: SAVE_VERSION,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
player: serializePlayerData(playerStore),
|
|||
|
|
game: serializeGameData(gameStore)
|
|||
|
|
}
|
|||
|
|
return JSON.stringify(saveData)
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to export save:', error)
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 导入存档(从字符串)
|
|||
|
|
* @param {Object} gameStore - 游戏Store
|
|||
|
|
* @param {Object} playerStore - 玩家Store
|
|||
|
|
* @param {string} saveString - 存档字符串
|
|||
|
|
* @returns {boolean} 是否成功
|
|||
|
|
*/
|
|||
|
|
export function importSave(gameStore, playerStore, saveString) {
|
|||
|
|
try {
|
|||
|
|
const saveData = JSON.parse(saveString)
|
|||
|
|
|
|||
|
|
// 验证格式
|
|||
|
|
if (!saveData.version || !saveData.player || !saveData.game) {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 应用存档
|
|||
|
|
applySaveData(gameStore, playerStore, saveData)
|
|||
|
|
|
|||
|
|
// 保存到本地
|
|||
|
|
uni.setStorageSync(SAVE_KEY, saveString)
|
|||
|
|
|
|||
|
|
return true
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to import save:', error)
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|