- 修复商店物品名称显示问题,添加堆叠物品出售数量选择 - 自动战斗状态持久化,战斗结束显示"寻找中"状态 - 战斗日志显示经验获取详情(战斗经验、武器经验) - 技能进度条显示当前/最大经验值 - 阅读自动解锁技能并持续获得阅读经验,背包可直接阅读 - 优化训练平衡:时长60秒,经验5点/秒,耐力消耗降低 - 实现自然回复系统:基于体质回复HP/耐力,休息提供3倍加成 - 战斗和训练时不进行自然回复 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
471 lines
11 KiB
JavaScript
471 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 || {})),
|
||
autoCombat: gameStore.autoCombat || false, // 保存自动战斗状态
|
||
// 不保存日志(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.autoCombat !== undefined) {
|
||
gameStore.autoCombat = data.autoCombat
|
||
}
|
||
|
||
// 定时事件
|
||
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
|
||
}
|
||
}
|