/** * 物品系统 - 使用/装备/品质计算 * Phase 6 核心系统实现 */ import { ITEM_CONFIG } from '@/config/items.js' import { GAME_CONSTANTS } from '@/config/constants.js' import { SKILL_CONFIG } from '@/config/skills.js' /** * 计算装备品质后的属性 * @param {String} itemId - 物品ID * @param {Number} quality - 品质值 (0-250) * @returns {Object|null} 计算后的属性 */ export function calculateItemStats(itemId, quality = 100) { const config = ITEM_CONFIG[itemId] if (!config) { return null } const qualityLevel = getQualityLevel(quality) const qualityMultiplier = qualityLevel.multiplier const stats = { id: itemId, name: config.name, type: config.type, subtype: config.subtype, description: config.description, icon: config.icon, quality: quality, qualityLevel: qualityLevel.level, qualityName: qualityLevel.name, qualityColor: qualityLevel.color } // 应用品质百分比到基础属性 const qualityRatio = quality / 100 if (config.baseDamage) { stats.baseDamage = config.baseDamage stats.finalDamage = Math.floor(config.baseDamage * qualityRatio * qualityMultiplier) } if (config.baseDefense) { stats.baseDefense = config.baseDefense stats.finalDefense = Math.floor(config.baseDefense * qualityRatio * qualityMultiplier) } if (config.baseShield) { stats.baseShield = config.baseShield stats.finalShield = Math.floor(config.baseShield * qualityRatio * qualityMultiplier) } if (config.attackSpeed) { stats.attackSpeed = config.attackSpeed } // 计算价值 stats.baseValue = config.baseValue || 0 stats.finalValue = Math.floor(config.baseValue * qualityRatio * qualityMultiplier) // 消耗品效果 if (config.effect) { stats.effect = { ...config.effect } } // 书籍信息 if (config.type === 'book') { stats.readingTime = config.readingTime stats.expReward = { ...config.expReward } stats.completionBonus = config.completionBonus } // 解锁技能 if (config.unlockSkill) { stats.unlockSkill = config.unlockSkill } // 堆叠信息 if (config.stackable !== undefined) { stats.stackable = config.stackable stats.maxStack = config.maxStack || 99 } return stats } /** * 获取品质等级 * @param {Number} quality - 品质值 * @returns {Object} 品质等级信息 */ export function getQualityLevel(quality) { // 确保品质在有效范围内 const clampedQuality = Math.max(0, Math.min(250, quality)) for (const [level, data] of Object.entries(GAME_CONSTANTS.QUALITY_LEVELS)) { if (clampedQuality >= data.range[0] && clampedQuality <= data.range[1]) { return { level: parseInt(level), ...data } } } // 默认返回垃圾品质 return GAME_CONSTANTS.QUALITY_LEVELS[1] } /** * 生成随机品质 * @param {Number} luck - 运气值 (0-100),影响高品质概率 * @returns {Number} 品质值 */ export function generateRandomQuality(luck = 0) { // 基础品质范围 50-150 const baseMin = 50 const baseMax = 150 // 运气加成:每1点运气增加0.5品质 const luckBonus = luck * 0.5 // 随机品质 let quality = Math.floor(Math.random() * (baseMax - baseMin + 1)) + baseMin + luckBonus // 小概率生成高品质(传说) if (Math.random() < 0.01 + luck / 10000) { quality = 180 + Math.floor(Math.random() * 70) // 180-250 } // 史诗品质 else if (Math.random() < 0.05 + luck / 2000) { quality = 160 + Math.floor(Math.random() * 40) // 160-199 } // 稀有品质 else if (Math.random() < 0.15 + luck / 500) { quality = 130 + Math.floor(Math.random() * 30) // 130-159 } // 优秀品质 else if (Math.random() < 0.3 + luck / 200) { quality = 100 + Math.floor(Math.random() * 30) // 100-129 } return Math.min(250, Math.max(0, Math.floor(quality))) } /** * 使用物品 * @param {Object} playerStore - 玩家Store * @param {Object} gameStore - 游戏Store * @param {String} itemId - 物品ID * @param {Number} count - 使用数量 * @returns {Object} { success: boolean, message: string, effects: Object } */ export function useItem(playerStore, gameStore, itemId, count = 1) { const config = ITEM_CONFIG[itemId] if (!config) { return { success: false, message: '物品不存在' } } // 查找背包中的物品 const itemIndex = playerStore.inventory.findIndex(i => i.id === itemId) if (itemIndex === -1) { return { success: false, message: '背包中没有该物品' } } const inventoryItem = playerStore.inventory[itemIndex] // 检查数量 if (inventoryItem.count < count) { return { success: false, message: '物品数量不足' } } // 根据物品类型处理 if (config.type === 'consumable') { return useConsumable(playerStore, gameStore, config, inventoryItem, count, itemIndex) } if (config.type === 'book') { return useBook(playerStore, gameStore, config, inventoryItem) } return { success: false, message: '该物品无法使用' } } /** * 使用消耗品 */ function useConsumable(playerStore, gameStore, config, inventoryItem, count, itemIndex) { const effects = {} const appliedEffects = [] // 应用效果 if (config.effect) { if (config.effect.health) { const oldHealth = playerStore.currentStats.health playerStore.currentStats.health = Math.min( playerStore.currentStats.maxHealth, playerStore.currentStats.health + config.effect.health * count ) effects.health = playerStore.currentStats.health - oldHealth if (effects.health > 0) appliedEffects.push(`恢复${effects.health}生命`) } if (config.effect.stamina) { const oldStamina = playerStore.currentStats.stamina playerStore.currentStats.stamina = Math.min( playerStore.currentStats.maxStamina, playerStore.currentStats.stamina + config.effect.stamina * count ) effects.stamina = playerStore.currentStats.stamina - oldStamina if (effects.stamina > 0) appliedEffects.push(`恢复${effects.stamina}耐力`) } if (config.effect.sanity) { const oldSanity = playerStore.currentStats.sanity playerStore.currentStats.sanity = Math.min( playerStore.currentStats.maxSanity, playerStore.currentStats.sanity + config.effect.sanity * count ) effects.sanity = playerStore.currentStats.sanity - oldSanity if (effects.sanity > 0) appliedEffects.push(`恢复${effects.sanity}精神`) } } // 消耗物品 inventoryItem.count -= count if (inventoryItem.count <= 0) { playerStore.inventory.splice(itemIndex, 1) } // 记录日志 const effectText = appliedEffects.length > 0 ? appliedEffects.join('、') : '使用' if (gameStore.addLog) { gameStore.addLog(`使用了 ${config.name} x${count},${effectText}`, 'info') } return { success: true, message: effectText, effects } } /** * 使用书籍(阅读) */ function useBook(playerStore, gameStore, config, inventoryItem) { // 书籍不消耗,返回阅读任务信息 return { success: true, message: '开始阅读', readingTask: { itemId: config.id, bookName: config.name, readingTime: config.readingTime, expReward: config.expReward, completionBonus: config.completionBonus } } } /** * 装备物品 * @param {Object} playerStore - 玩家Store * @param {Object} gameStore - 游戏Store * @param {String} uniqueId - 物品唯一ID(背包中的物品) * @returns {Object} { success: boolean, message: string } */ export function equipItem(playerStore, gameStore, uniqueId) { const inventoryItem = playerStore.inventory.find(i => i.uniqueId === uniqueId) if (!inventoryItem) { return { success: false, message: '物品不存在' } } const config = ITEM_CONFIG[inventoryItem.id] if (!config) { return { success: false, message: '物品配置不存在' } } // 确定装备槽位 let slot = config.type if (config.subtype === 'one_handed' || config.subtype === 'two_handed') { slot = 'weapon' } else if (config.type === 'armor') { slot = 'armor' } else if (config.type === 'shield') { slot = 'shield' } else if (config.type === 'accessory') { slot = 'accessory' } if (!slot || !playerStore.equipment.hasOwnProperty(slot)) { return { success: false, message: '无法装备该物品' } } // 如果已有装备,先卸下 const currentlyEquipped = playerStore.equipment[slot] if (currentlyEquipped) { unequipItemBySlot(playerStore, gameStore, slot) } // 装备新物品 playerStore.equipment[slot] = inventoryItem inventoryItem.equipped = true // 应用装备属性 applyEquipmentStats(playerStore, slot, inventoryItem) // 记录日志 if (gameStore.addLog) { gameStore.addLog(`装备了 ${inventoryItem.name}`, 'info') } return { success: true, message: '装备成功' } } /** * 卸下装备 * @param {Object} playerStore - 玩家Store * @param {Object} gameStore - 游戏Store * @param {String} slot - 装备槽位 * @returns {Object} { success: boolean, message: string } */ export function unequipItemBySlot(playerStore, gameStore, slot) { const equipped = playerStore.equipment[slot] if (!equipped) { return { success: false, message: '该槽位没有装备' } } // 移除装备属性 removeEquipmentStats(playerStore, slot) // 标记为未装备 equipped.equipped = false // 清空槽位 playerStore.equipment[slot] = null // 记录日志 if (gameStore.addLog) { gameStore.addLog(`卸下了 ${equipped.name}`, 'info') } return { success: true, message: '卸下成功' } } /** * 应用装备属性到玩家 */ function applyEquipmentStats(playerStore, slot, item) { if (!playerStore.equipmentStats) { playerStore.equipmentStats = { attack: 0, defense: 0, shield: 0, critRate: 0, attackSpeed: 1 } } if (item.finalDamage) { playerStore.equipmentStats.attack += item.finalDamage } if (item.finalDefense) { playerStore.equipmentStats.defense += item.finalDefense } if (item.finalShield) { playerStore.equipmentStats.shield += item.finalShield } if (item.attackSpeed) { playerStore.equipmentStats.attackSpeed *= item.attackSpeed } } /** * 移除装备属性 */ function removeEquipmentStats(playerStore, slot) { const equipped = playerStore.equipment[slot] if (!equipped || !playerStore.equipmentStats) { return } if (equipped.finalDamage) { playerStore.equipmentStats.attack -= equipped.finalDamage } if (equipped.finalDefense) { playerStore.equipmentStats.defense -= equipped.finalDefense } if (equipped.finalShield) { playerStore.equipmentStats.shield -= equipped.finalShield } } /** * 添加物品到背包 * @param {Object} playerStore - 玩家Store * @param {String} itemId - 物品ID * @param {Number} count - 数量 * @param {Number} quality - 品质(装备用) * @returns {Object} { success: boolean, item: Object } */ export function addItemToInventory(playerStore, itemId, count = 1, quality = null) { const config = ITEM_CONFIG[itemId] if (!config) { return { success: false, message: '物品不存在' } } // 素材和关键道具不需要品质,使用固定值 // 装备类物品才需要随机品质 const needsQuality = config.type === 'weapon' || config.type === 'armor' || config.type === 'shield' || config.type === 'accessory' const itemQuality = quality !== null ? quality : (needsQuality ? generateRandomQuality(playerStore.baseStats?.intuition || 0) : 100) // 计算物品属性 const itemStats = calculateItemStats(itemId, itemQuality) // 堆叠物品 - 只需要匹配ID,不需要匹配品质(素材类物品) if (config.stackable) { const existingItem = playerStore.inventory.find(i => i.id === itemId) if (existingItem) { existingItem.count += count return { success: true, item: existingItem } } } // 创建新物品 const newItem = { ...itemStats, uniqueId: `${itemId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, count, equipped: false, obtainedAt: Date.now() } playerStore.inventory.push(newItem) return { success: true, item: newItem } } /** * 从背包移除物品 * @param {Object} playerStore - 玩家Store * @param {String} uniqueId - 物品唯一ID * @param {Number} count - 移除数量 * @returns {Object} { success: boolean, message: string } */ export function removeItemFromInventory(playerStore, uniqueId, count = 1) { const itemIndex = playerStore.inventory.findIndex(i => i.uniqueId === uniqueId) if (itemIndex === -1) { return { success: false, message: '物品不存在' } } const item = playerStore.inventory[itemIndex] if (item.count < count) { return { success: false, message: '物品数量不足' } } item.count -= count if (item.count <= 0) { // 如果已装备,先卸下 if (item.equipped) { for (const [slot, equipped] of Object.entries(playerStore.equipment)) { if (equipped && equipped.uniqueId === uniqueId) { playerStore.equipment[slot] = null break } } } playerStore.inventory.splice(itemIndex, 1) } return { success: true, message: '移除成功' } } /** * 检查是否拥有物品 * @param {Object} playerStore - 玩家Store * @param {String} itemId - 物品ID * @returns {Boolean} */ export function hasItem(playerStore, itemId) { return playerStore.inventory.some(i => i.id === itemId && i.count > 0) } /** * 获取物品数量 * @param {Object} playerStore - 玩家Store * @param {String} itemId - 物品ID * @returns {Number} */ export function getItemCount(playerStore, itemId) { const item = playerStore.inventory.find(i => i.id === itemId) return item ? item.count : 0 } /** * 检查装备是否解锁技能 * @param {Object} playerStore - 玩家Store * @param {String} itemId - 物品ID * @returns {String|null} 解锁的技能ID */ export function checkSkillUnlock(playerStore, itemId) { const config = ITEM_CONFIG[itemId] if (!config || !config.unlockSkill) { return null } const skillId = config.unlockSkill // 检查技能是否已解锁 if (playerStore.skills[skillId] && playerStore.skills[skillId].unlocked) { return null } return skillId } /** * 计算物品出售价格 * @param {Object} item - 物品对象 * @param {Number} marketRate - 市场价格倍率 (0.1-2.0) * @returns {Number} 出售价格(铜币) */ export function calculateSellPrice(item, marketRate = 0.3) { const basePrice = item.finalValue || item.baseValue || 0 return Math.max(1, Math.floor(basePrice * marketRate)) } /** * 计算物品购买价格 * @param {Object} item - 物品对象 * @param {Number} marketRate - 市场价格倍率 (1.0-3.0) * @returns {Number} 购买价格(铜币) */ export function calculateBuyPrice(item, marketRate = 2.0) { const basePrice = item.finalValue || item.baseValue || 0 return Math.max(1, Math.floor(basePrice * marketRate)) } /** * 获取品质颜色 * @param {Number} quality - 品质值 * @returns {String} 颜色代码 */ export function getQualityColor(quality) { const level = getQualityLevel(quality) return level.color } /** * 获取品质名称 * @param {Number} quality - 品质值 * @returns {String} 品质名称 */ export function getQualityName(quality) { const level = getQualityLevel(quality) return level.name }