Files
text-adventure-game/utils/storage.js
Claude cef974d94f feat: 优化游戏体验和系统平衡性
- 修复商店物品名称显示问题,添加堆叠物品出售数量选择
- 自动战斗状态持久化,战斗结束显示"寻找中"状态
- 战斗日志显示经验获取详情(战斗经验、武器经验)
- 技能进度条显示当前/最大经验值
- 阅读自动解锁技能并持续获得阅读经验,背包可直接阅读
- 优化训练平衡:时长60秒,经验5点/秒,耐力消耗降低
- 实现自然回复系统:基于体质回复HP/耐力,休息提供3倍加成
- 战斗和训练时不进行自然回复

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 19:40:55 +08:00

471 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 存档系统 - 保存、加载、重置游戏数据
* 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
}
}