Files
text-adventure-game/utils/craftingSystem.js
Claude 021f6a54f5 fix: 修复制造系统耦合问题并优化物品堆叠逻辑
- 修复 craftingSystem.js 引用不存在的 inventorySystem.js
- 移除 recipes.js 中重复的 getItemCount 函数
- 装备类物品改为按品质等级(1-6)分组堆叠,而非精确品质值
- 统一品质计算,使用 GAME_CONSTANTS.QUALITY_LEVELS 范围
- 移除未使用的 createCraftedItem 函数

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

358 lines
11 KiB
JavaScript
Raw 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 6 核心系统实现
*/
import { ITEM_CONFIG } from '@/config/items.js'
import { RECIPE_CONFIG, getAvailableRecipes, checkMaterials } from '@/config/recipes.js'
import { SKILL_CONFIG } from '@/config/skills.js'
import { startTask } from './taskSystem.js'
import { TASK_TYPES } from './taskSystem.js'
import { addItemToInventory } from './itemSystem.js'
import { addSkillExp, unlockSkill } from './skillSystem.js'
/**
* 获取玩家可制作的配方
* @param {Object} playerStore - 玩家Store
* @returns {Array} 可制作的配方列表(含材料检查)
*/
export function getCraftableRecipes(playerStore) {
const availableRecipes = getAvailableRecipes(playerStore)
return availableRecipes.map(recipe => {
const materialCheck = checkMaterials(playerStore, recipe)
return {
...recipe,
canCraftNow: materialCheck.canCraft,
missingMaterials: materialCheck.missingMaterials
}
})
}
/**
* 按分类获取可制作的配方
* @param {Object} playerStore - 玩家Store
* @param {String} category - 分类 'weapon' | 'armor' | 'shield' | 'consumable' | 'special'
* @returns {Array} 配方列表
*/
export function getRecipesByCategory(playerStore, category) {
const recipes = getCraftableRecipes(playerStore)
return recipes.filter(recipe => recipe.type === category)
}
/**
* 计算制造时间
* @param {Object} playerStore - 玩家Store
* @param {Object} recipe - 配方对象
* @returns {Number} 制造时间(秒)
*/
export function calculateCraftingTime(playerStore, recipe) {
let baseTime = recipe.baseTime
// 技能加成
const skill = playerStore.skills[recipe.requiredSkill]
if (skill && skill.level > 0) {
// 检查制造技能的时间加成里程碑
let speedBonus = 0
if (playerStore.skills.crafting?.unlocked) {
const craftingSkill = playerStore.skills.crafting
// 检查里程碑
const milestones = SKILL_CONFIG.crafting?.milestones || {}
for (const [level, milestone] of Object.entries(milestones)) {
if (craftingSkill.level >= parseInt(level) && milestone.effect?.craftingSpeed) {
speedBonus += milestone.effect.craftingSpeed
}
}
}
// 技能等级直接加成每级5%
speedBonus += Math.min(skill.level, 20) * 0.05
baseTime = baseTime * (1 - speedBonus)
}
return Math.max(5, Math.floor(baseTime)) // 最少5秒
}
/**
* 计算制造成功率
* @param {Object} playerStore - 玩家Store
* @param {Object} recipe - 配方对象
* @returns {Number} 成功率 (0-100)
*/
export function calculateSuccessRate(playerStore, recipe) {
let successRate = recipe.baseSuccessRate * 100
// 技能等级加成(每级+2%
const skill = playerStore.skills[recipe.requiredSkill]
if (skill && skill.level > 0) {
successRate += Math.min(skill.level, 20) * 2
}
// 运气加成
const luck = playerStore.baseStats?.luck || 10
successRate += luck * 0.1
// 检查制造技能的成功率加成里程碑
if (playerStore.skills.crafting?.unlocked) {
const craftingSkill = playerStore.skills.crafting
const milestones = SKILL_CONFIG.crafting?.milestones || {}
for (const [level, milestone] of Object.entries(milestones)) {
if (craftingSkill.level >= parseInt(level) && milestone.effect?.craftingSuccessRate) {
successRate += milestone.effect.craftingSuccessRate
}
}
}
return Math.min(98, Math.max(5, Math.floor(successRate)))
}
/**
* 计算物品品质
* @param {Object} playerStore - 玩家Store
* @param {Object} recipe - 配方对象
* @returns {Number} 品质值 (0-250)
*/
export function calculateQuality(playerStore, recipe) {
// 基础品质(普通范围 50-99
let qualityValue = 50 + Math.floor(Math.random() * 50) // 默认普通
// 随机因素 (0-100)
const randomRoll = Math.random() * 100
// 技能加成(每级增加高品质概率)
const skill = playerStore.skills[recipe.requiredSkill]
let skillBonus = 0
if (skill) {
skillBonus = skill.level * 2
}
// 运气加成
const luck = playerStore.baseStats?.luck || 10
const luckBonus = luck * 0.5
// 制造技能品质加成
let craftingQualityBonus = 0
if (playerStore.skills.crafting?.unlocked) {
const craftingSkill = playerStore.skills.crafting
const milestones = SKILL_CONFIG.crafting?.milestones || {}
for (const [level, milestone] of Object.entries(milestones)) {
if (craftingSkill.level >= parseInt(level) && milestone.effect?.craftingQuality) {
craftingQualityBonus += milestone.effect.craftingQuality
}
}
}
const totalBonus = skillBonus + luckBonus + craftingQualityBonus
const finalRoll = randomRoll + totalBonus
// 品质判定 - 与全局品质等级保持一致
if (finalRoll >= 95) qualityValue = 200 + Math.floor(Math.random() * 50) // 传说 [200, 250]
else if (finalRoll >= 85) qualityValue = 160 + Math.floor(Math.random() * 39) // 史诗 [160, 199]
else if (finalRoll >= 70) qualityValue = 130 + Math.floor(Math.random() * 29) // 稀有 [130, 159]
else if (finalRoll >= 50) qualityValue = 100 + Math.floor(Math.random() * 29) // 优秀 [100, 129]
else if (finalRoll >= 20) qualityValue = 50 + Math.floor(Math.random() * 49) // 普通 [50, 99]
else qualityValue = Math.floor(Math.random() * 50) // 垃圾 [0, 49]
// 特殊道具(如剧情道具)固定为传说品质
if (recipe.type === 'special' && recipe.keyItem) {
qualityValue = 225 // 传说中值
}
return Math.min(250, Math.max(0, qualityValue))
}
/**
* 消耗材料(通过 itemSystem 接口)
* @param {Object} playerStore - 玩家Store
* @param {Object} recipe - 配方对象
* @returns {Boolean} 是否成功消耗
*/
export function consumeMaterials(playerStore, recipe) {
// 先检查材料是否足够
const check = checkMaterials(playerStore, recipe)
if (!check.canCraft) {
return false
}
// 消耗材料 - 使用 itemSystem 接口
for (const material of recipe.materials) {
// 查找背包中该物品的实例
const itemIndex = playerStore.inventory.findIndex(i => i.id === material.itemId)
if (itemIndex !== -1) {
const item = playerStore.inventory[itemIndex]
const countToRemove = Math.min(item.count, material.count)
item.count -= countToRemove
if (item.count <= 0) {
playerStore.inventory.splice(itemIndex, 1)
}
}
}
return true
}
/**
* 开始制造
* @param {Object} gameStore - 游戏Store
* @param {Object} playerStore - 玩家Store
* @param {String} recipeId - 配方ID
* @returns {Object} { success: boolean, message: string, taskId: string|null }
*/
export function startCrafting(gameStore, playerStore, recipeId) {
const recipe = RECIPE_CONFIG[recipeId]
if (!recipe) {
return { success: false, message: '配方不存在' }
}
// 检查材料
const materialCheck = checkMaterials(playerStore, recipe)
if (!materialCheck.canCraft) {
const missingList = materialCheck.missingMaterials.map(m => `${m.itemName} x${m.need - m.have}`).join(', ')
return { success: false, message: `材料不足:${missingList}` }
}
// 检查技能
if (recipe.requiredSkill) {
const skill = playerStore.skills[recipe.requiredSkill]
if (!skill || !skill.unlocked) {
return { success: false, message: '需要解锁对应技能' }
}
if (skill.level < recipe.requiredSkillLevel) {
return { success: false, message: `技能等级不足(需要${recipe.requiredSkillLevel}级)` }
}
}
// 计算制造时间
const craftingTime = calculateCraftingTime(playerStore, recipe)
// 消耗材料(在开始制造时消耗)
if (!consumeMaterials(playerStore, recipe)) {
return { success: false, message: '材料消耗失败' }
}
// 开始制造任务
const result = startTask(gameStore, playerStore, TASK_TYPES.CRAFTING, {
recipeId,
duration: craftingTime
})
if (result.success) {
// 预先计算品质(制造开始时确定)
const task = gameStore.activeTasks.find(t => t.id === result.taskId)
if (task) {
task.preCalculatedQuality = calculateQuality(playerStore, recipe)
}
const itemConfig = ITEM_CONFIG[recipe.resultItem]
return {
success: true,
message: `开始制造:${itemConfig?.name || recipe.resultItem}`,
taskId: result.taskId,
estimatedTime: craftingTime,
successRate: calculateSuccessRate(playerStore, recipe)
}
}
return result
}
/**
* 完成制造(任务完成回调)
* @param {Object} _gameStore - 游戏Store未使用保留以兼容接口
* @param {Object} playerStore - 玩家Store
* @param {Object} task - 制造任务
* @param {Object} result - 任务结果
* @returns {Object} { success: boolean, item: Object|null }
*/
export function completeCrafting(_gameStore, playerStore, task, result) {
const recipeId = task.data.recipeId
const recipe = RECIPE_CONFIG[recipeId]
if (!recipe) {
return { success: false, message: '配方不存在' }
}
// 如果制造失败
if (!result.success) {
// 失败可能部分返还材料(基于成功率)
const successRate = calculateSuccessRate(playerStore, recipe)
if (successRate > 70) {
// 高成功率:返还部分材料
// 材料已在开始时消耗,这里可以考虑返还逻辑
return {
success: false,
message: '制造失败,但部分材料已保留',
failed: true
}
}
return {
success: false,
message: '制造失败,材料已消耗',
failed: true
}
}
// 制造成功,计算品质(已经是品质值 0-250
const qualityValue = task.preCalculatedQuality || calculateQuality(playerStore, recipe)
// 添加到背包 - 使用 itemSystem 接口
const addResult = addItemToInventory(playerStore, recipe.resultItem, recipe.resultCount, qualityValue)
if (!addResult.success) {
return { success: false, message: '添加物品失败' }
}
// 获取物品显示信息
const item = addResult.item
// 给予制造技能经验
if (result.rewards && result.rewards[recipe.requiredSkill]) {
const skillId = recipe.requiredSkill
const exp = result.rewards[skillId]
if (!playerStore.skills[skillId]) {
unlockSkill(playerStore, skillId)
}
addSkillExp(playerStore, skillId, exp)
}
return {
success: true,
item,
message: `制造成功:${item.qualityName} ${item.name}`,
addResult
}
}
/**
* 获取分类显示信息
* @param {String} type - 配方类型
* @returns {Object} 分类信息
*/
export function getCategoryInfo(type) {
const categories = {
weapon: { id: 'weapon', name: '武器', icon: '⚔️' },
armor: { id: 'armor', name: '防具', icon: '🛡️' },
shield: { id: 'shield', name: '盾牌', icon: '🛡️' },
consumable: { id: 'consumable', name: '消耗品', icon: '🧪' },
special: { id: 'special', name: '特殊', icon: '✨' }
}
return categories[type] || { id: type, name: type, icon: '📦' }
}
/**
* 获取所有分类
* @returns {Array} 分类列表
*/
export function getAllCategories() {
return [
{ id: 'weapon', name: '武器', icon: '⚔️' },
{ id: 'armor', name: '防具', icon: '🛡️' },
{ id: 'shield', name: '盾牌', icon: '🛡️' },
{ id: 'consumable', name: '消耗品', icon: '🧪' },
{ id: 'special', name: '特殊', icon: '✨' }
]
}