/** * 环境系统 - 环境惩罚计算、被动技能获取 * Phase 6 核心系统实现 */ import { LOCATION_CONFIG } from '@/config/locations.js' import { SKILL_CONFIG } from '@/config/skills.js' /** * 环境类型枚举 */ export const ENVIRONMENT_TYPES = { NORMAL: 'normal', // 普通环境 DARK: 'dark', // 黑暗环境 NARROW: 'narrow', // 狭窄空间 HARSH: 'harsh' // 恶劣环境 } /** * 环境惩罚配置 */ const ENVIRONMENT_PENALTIES = { [ENVIRONMENT_TYPES.DARK]: { apPenalty: 0.2, // AP -20% epPenalty: 0.2, // EP -20% readingPenalty: 0.5, // 阅读效率 -50% adaptSkill: 'night_vision', // 适应技能 description: '黑暗' }, [ENVIRONMENT_TYPES.NARROW]: { apPenalty: 0, // AP 无惩罚 epPenalty: 0.3, // EP -30% readingPenalty: 0, // 阅读无惩罚 adaptSkill: 'narrow_fighting', // 适应技能 description: '狭窄' }, [ENVIRONMENT_TYPES.HARSH]: { apPenalty: 0.1, // AP -10% epPenalty: 0.1, // EP -10% readingPenalty: 0.2, // 阅读效率 -20% adaptSkill: 'survivalist', // 适应技能 description: '恶劣' }, [ENVIRONMENT_TYPES.NORMAL]: { apPenalty: 0, epPenalty: 0, readingPenalty: 0, adaptSkill: null, description: '普通' } } /** * 获取当前位置的环境惩罚 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {Object} 惩罚信息 */ export function getEnvironmentPenalty(locationId, playerStore) { const location = LOCATION_CONFIG[locationId] if (!location) { return getDefaultPenalty() } const environmentType = location.environment || ENVIRONMENT_TYPES.NORMAL const basePenalty = ENVIRONMENT_PENALTIES[environmentType] || ENVIRONMENT_PENALTIES[ENVIRONMENT_TYPES.NORMAL] // 应用技能减少惩罚 const penalty = { ...basePenalty } // 夜视技能减少黑暗惩罚 if (environmentType === ENVIRONMENT_TYPES.DARK) { const darkPenaltyReduce = playerStore.globalBonus?.darkPenaltyReduce || 0 penalty.apPenalty = Math.max(0, penalty.apPenalty * (1 - darkPenaltyReduce / 100)) penalty.epPenalty = Math.max(0, penalty.epPenalty * (1 - darkPenaltyReduce / 100)) penalty.readingPenalty = Math.max(0, penalty.readingPenalty * (1 - darkPenaltyReduce / 100)) } return penalty } /** * 获取默认惩罚(普通环境) */ function getDefaultPenalty() { return { apPenalty: 0, epPenalty: 0, readingPenalty: 0, adaptSkill: null, description: '普通' } } /** * 计算AP环境修正 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {Number} 修正倍率 (0-1) */ export function getAPModifier(locationId, playerStore) { const penalty = getEnvironmentPenalty(locationId, playerStore) return 1 - penalty.apPenalty } /** * 计算EP环境修正 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {Number} 修正倍率 (0-1) */ export function getEPModifier(locationId, playerStore) { const penalty = getEnvironmentPenalty(locationId, playerStore) return 1 - penalty.epPenalty } /** * 计算阅读效率修正 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {Number} 修正倍率 (0-1) */ export function getReadingModifier(locationId, playerStore) { const penalty = getEnvironmentPenalty(locationId, playerStore) return 1 - penalty.readingPenalty } /** * 处理环境被动经验 * 在特定环境中被动获得技能经验 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {String} locationId - 位置ID * @param {Number} deltaTime - 时间增量(秒) * @returns {Object} 获得的经验 { skillId: exp } */ export function processEnvironmentExp(gameStore, playerStore, locationId, deltaTime = 1) { const location = LOCATION_CONFIG[locationId] if (!location) return {} const environmentType = location.environment || ENVIRONMENT_TYPES.NORMAL const penalty = ENVIRONMENT_PENALTIES[environmentType] if (!penalty.adaptSkill) { return {} } const adaptSkillId = penalty.adaptSkill const skillConfig = SKILL_CONFIG[adaptSkillId] if (!skillConfig) { return {} } // 检查技能是否已解锁 if (!playerStore.skills[adaptSkillId] || !playerStore.skills[adaptSkillId].unlocked) { // 检查解锁条件 if (skillConfig.unlockCondition) { const condition = skillConfig.unlockCondition if (condition.location === locationId) { // 首次进入该环境,解锁技能 if (!playerStore.skills[adaptSkillId]) { playerStore.skills[adaptSkillId] = { level: 0, exp: 0, unlocked: true } if (gameStore.addLog) { gameStore.addLog(`解锁了被动技能: ${skillConfig.name}`, 'system') } } } } return {} } // 计算被动经验(每秒获得基础经验) const baseExpRate = 0.5 // 每秒0.5经验 const expGain = baseExpRate * deltaTime // 应用阅读速度加成 const readingSpeedBonus = playerStore.globalBonus?.readingSpeed || 1 const finalExp = expGain * readingSpeedBonus // 添加经验 playerStore.skills[adaptSkillId].exp += finalExp // 检查升级 const exp = playerStore.skills[adaptSkillId].exp const maxExp = skillConfig.expPerLevel(playerStore.skills[adaptSkillId].level + 1) if (exp >= maxExp && playerStore.skills[adaptSkillId].level < skillConfig.maxLevel) { playerStore.skills[adaptSkillId].level++ playerStore.skills[adaptSkillId].exp -= maxExp if (gameStore.addLog) { gameStore.addLog(`${skillConfig.name} 提升到了 Lv.${playerStore.skills[adaptSkillId].level}!`, 'reward') } // 检查里程碑奖励(需要调用技能系统) // TODO: 调用 applyMilestoneBonus } return { [adaptSkillId]: finalExp } } /** * 检查位置解锁条件 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {Object} { unlocked: boolean, reason: string } */ export function checkLocationUnlock(locationId, playerStore) { const location = LOCATION_CONFIG[locationId] if (!location) { return { unlocked: false, reason: '位置不存在' } } if (!location.unlockCondition) { return { unlocked: true, reason: '' } } const condition = location.unlockCondition switch (condition.type) { case 'kill': // 击杀条件 const killCount = (playerStore.killCount || {})[condition.target] || 0 if (killCount < condition.count) { return { unlocked: false, reason: `需要击杀 ${condition.count} 只${getEnemyName(condition.target)} (当前: ${killCount})` } } break case 'item': // 物品条件 const hasItem = playerStore.inventory?.some(i => i.id === condition.item) if (!hasItem) { const itemName = getItemName(condition.item) return { unlocked: false, reason: `需要 ${itemName}` } } break case 'level': // 等级条件 const playerLevel = playerStore.level?.current || 1 if (playerLevel < condition.level) { return { unlocked: false, reason: `需要等级 ${condition.level}` } } break case 'skill': // 技能条件 const skill = playerStore.skills[condition.skillId] if (!skill || skill.level < condition.level) { return { unlocked: false, reason: `需要技能达到指定等级` } } break case 'flag': // 标志条件 if (!playerStore.flags?.[condition.flag]) { return { unlocked: false, reason: '需要完成特定事件' } } break } return { unlocked: true, reason: '' } } /** * 获取敌人名称(辅助函数) */ function getEnemyName(enemyId) { // 简化实现,实际应该从 ENEMY_CONFIG 获取 const names = { wild_dog: '野狗', test_boss: '测试Boss' } return names[enemyId] || enemyId } /** * 获取物品名称(辅助函数) */ function getItemName(itemId) { // 简化实现,实际应该从 ITEM_CONFIG 获取 const names = { basement_key: '地下室钥匙', old_book: '破旧书籍' } return names[itemId] || itemId } /** * 检查位置是否安全 * @param {String} locationId - 位置ID * @returns {Boolean} */ export function isSafeLocation(locationId) { const location = LOCATION_CONFIG[locationId] return location?.type === 'safe' } /** * 检查位置是否为战斗区域 * @param {String} locationId - 位置ID * @returns {Boolean} */ export function isCombatLocation(locationId) { const location = LOCATION_CONFIG[locationId] return location?.type === 'danger' || location?.type === 'dungeon' } /** * 获取位置可用的活动 * @param {String} locationId - 位置ID * @returns {Array} 活动列表 */ export function getLocationActivities(locationId) { const location = LOCATION_CONFIG[locationId] return location?.activities || [] } /** * 获取位置连接 * @param {String} locationId - 位置ID * @returns {Array} 连接的位置ID列表 */ export function getLocationConnections(locationId) { const location = LOCATION_CONFIG[locationId] return location?.connections || [] } /** * 检查位置是否可到达 * @param {String} fromId - 起始位置ID * @param {String} toId - 目标位置ID * @param {Object} playerStore - 玩家Store * @returns {Object} { reachable: boolean, reason: string } */ export function canTravelTo(fromId, toId, playerStore) { const fromLocation = LOCATION_CONFIG[fromId] if (!fromLocation) { return { reachable: false, reason: '当前位置不存在' } } // 检查是否直接连接 if (!fromLocation.connections?.includes(toId)) { return { reachable: false, reason: '无法直接到达' } } // 检查目标位置解锁条件 return checkLocationUnlock(toId, playerStore) } /** * 计算位置转移时间 * @param {String} fromId - 起始位置ID * @param {String} toId - 目标位置ID * @returns {Number} 转移时间(秒) */ export function getTravelTime(fromId, toId) { // 简化实现:所有位置间转移时间为5秒 return 5 } /** * 获取环境描述 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {String} 环境描述 */ export function getEnvironmentDescription(locationId, playerStore) { const location = LOCATION_CONFIG[locationId] if (!location) return '' const penalty = getEnvironmentPenalty(locationId, playerStore) let description = location.description || '' // 添加环境效果描述 if (location.environment === ENVIRONMENT_TYPES.DARK) { const hasNightVision = playerStore.skills.night_vision?.level > 0 if (!hasNightVision) { description += ' [黑暗: AP/EP-20% 阅读效率-50%]' } else { const reduce = playerStore.globalBonus?.darkPenaltyReduce || 0 description += ` [黑暗: 惩罚减少${reduce}%]` } } return description } /** * 获取位置的可敌人列表 * @param {String} locationId - 位置ID * @returns {Array} 敌人ID列表 */ export function getLocationEnemies(locationId) { const location = LOCATION_CONFIG[locationId] return location?.enemies || [] } /** * 获取位置的NPC列表 * @param {String} locationId - 位置ID * @returns {Array} NPC ID列表 */ export function getLocationNPCs(locationId) { const location = LOCATION_CONFIG[locationId] return location?.npcs || [] } /** * 检查是否有负面状态来自环境 * @param {String} locationId - 位置ID * @param {Object} playerStore - 玩家Store * @returns {Array} 负面状态列表 */ export function getEnvironmentNegativeStatus(locationId, playerStore) { const location = LOCATION_CONFIG[locationId] if (!location) return [] const status = [] // 黑暗环境可能造成精神压力 if (location.environment === ENVIRONMENT_TYPES.DARK) { const hasNightVision = playerStore.skills.night_vision?.level || 0 if (hasNightVision < 5) { status.push({ type: 'darkness', name: '黑暗恐惧', effect: '精神逐渐下降', severity: hasNightVision > 0 ? 'mild' : 'moderate' }) } } return status } /** * 处理环境负面状态效果 * @param {Object} playerStore - 玩家Store * @param {String} locationId - 位置ID * @param {Number} deltaTime - 时间增量(秒) */ export function processEnvironmentEffects(playerStore, locationId, deltaTime = 1) { const location = LOCATION_CONFIG[locationId] if (!location) return // 黑暗环境的精神压力 if (location.environment === ENVIRONMENT_TYPES.DARK) { const nightVisionLevel = playerStore.skills.night_vision?.level || 0 // 夜视5级以下会缓慢消耗精神 if (nightVisionLevel < 5) { const sanityDrain = 0.1 * (1 - nightVisionLevel * 0.1) * deltaTime playerStore.currentStats.sanity = Math.max( 0, playerStore.currentStats.sanity - sanityDrain ) } } // 恶劣环境的持续影响 if (location.environment === ENVIRONMENT_TYPES.HARSH) { const survivalistLevel = playerStore.skills.survivalist?.level || 0 // 生存技能可以减少恶劣环境影响 if (survivalistLevel < 5) { const staminaDrain = 0.2 * (1 - survivalistLevel * 0.1) * deltaTime playerStore.currentStats.stamina = Math.max( 0, playerStore.currentStats.stamina - staminaDrain ) } } }