fix: 修复武器经验获取并完善义体系统

- 修复战斗胜利后未获得武器技能经验的问题 (initCombat未传递skillExpReward)
- 每级武器技能提供5%武器伤害加成(已实现,无需修改)
- 实现义体安装/卸载功能,支持NPC对话交互
- StatusPanel添加义体装备槽显示
- MapPanel修复NPC对话import问题
- 新增成就系统框架
- 添加项目文档CLAUDE.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-02-02 15:52:32 +08:00
parent 27e1c8d440
commit ccfd6a5e75
8 changed files with 672 additions and 35 deletions

View File

@@ -124,7 +124,7 @@ import { NPC_CONFIG } from '@/config/npcs.js'
import { ENEMY_CONFIG, getRandomEnemyForLocation } from '@/config/enemies.js'
import { getShopConfig } from '@/config/shop.js'
import { initCombat, getEnvironmentType } from '@/utils/combatSystem.js'
import { triggerExploreEvent, tryFlee } from '@/utils/eventSystem.js'
import { triggerExploreEvent, tryFlee, startNPCDialogue } from '@/utils/eventSystem.js'
import TextButton from '@/components/common/TextButton.vue'
import ProgressBar from '@/components/common/ProgressBar.vue'
import FilterTabs from '@/components/common/FilterTabs.vue'
@@ -252,16 +252,8 @@ const availableNPCs = computed(() => {
function talkToNPC(npc) {
game.addLog(`${npc.name} 开始对话...`, 'info')
// 打开 NPC 对话
if (npc.dialogue && npc.dialogue.first) {
game.drawerState.event = true
game.currentEvent = {
type: 'npc_dialogue',
npcId: npc.id,
dialogue: npc.dialogue.first,
choices: npc.dialogue.first.choices || []
}
}
// 使用 eventSystem 的 startNPCDialogue 来处理对话
startNPCDialogue(game, npc.id, 'first', player)
}
function openInventory() {
@@ -360,14 +352,15 @@ function startCombat() {
// 获取环境类型
const environment = getEnvironmentType(player.currentLocation)
// 使用 initCombat 初始化战斗
game.combatState = initCombat(enemyConfig.id, enemyConfig, environment)
// 使用 initCombat 初始化战斗,传入偏好的战斗姿态
game.combatState = initCombat(enemyConfig.id, enemyConfig, environment, game.preferredStance)
game.inCombat = true
}
function changeStance(stance) {
if (game.combatState) {
game.combatState.stance = stance
game.preferredStance = stance // 记住玩家选择的姿态
game.addLog(`切换到${stanceTabs.find(t => t.id === stance)?.label}姿态`, 'combat')
}
}

View File

@@ -114,6 +114,19 @@
</view>
<text v-else class="equipment-slot__empty"></text>
</view>
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.prosthetic }">
<text class="equipment-slot__label">义体</text>
<view v-if="player.equipment.prosthetic" class="equipment-slot__item">
<text class="equipment-slot__name" style="color: #ff6b9d;">
{{ player.equipment.prosthetic.icon }} {{ player.equipment.prosthetic.name }}
</text>
<text v-if="player.equipment.prosthetic.stats" class="equipment-slot__stats">
{{ formatProstheticStats(player.equipment.prosthetic.stats) }}
</text>
<text v-if="player.equipment.prosthetic.skill" class="equipment-slot__quality">[{{ getProstheticSkillName(player.equipment.prosthetic.skill) }}]</text>
</view>
<text v-else class="equipment-slot__empty"></text>
</view>
</view>
</view>
@@ -575,6 +588,26 @@ function cancelActiveTask(task) {
endTask(game, player, task.id, false)
game.addLog(`取消了 ${task.name}`, 'info')
}
// 义体相关辅助函数
function formatProstheticStats(stats) {
if (!stats) return ''
const parts = []
if (stats.strength) parts.push(`力+${stats.strength}`)
if (stats.agility) parts.push(`敏+${stats.agility}`)
if (stats.dexterity) parts.push(`灵+${stats.dexterity}`)
if (stats.intuition) parts.push(`智+${stats.intuition}`)
if (stats.vitality) parts.push(`体+${stats.vitality}`)
if (stats.attack) parts.push(`攻+${stats.attack}`)
if (stats.defense) parts.push(`防+${stats.defense}`)
return parts.join(' ')
}
function getProstheticSkillName(skillId) {
const { SKILL_CONFIG } = require('@/config/skills')
const skill = SKILL_CONFIG[skillId]
return skill ? skill.name : skillId
}
</script>
<style lang="scss" scoped>