feat: 实现游戏核心系统和UI组件

核心系统:
- combatSystem: 战斗逻辑、伤害计算、战斗状态管理
- skillSystem: 技能系统、技能解锁、经验值、里程碑
- taskSystem: 任务系统、任务类型、任务执行和完成
- eventSystem: 事件系统、随机事件处理
- environmentSystem: 环境系统、时间流逝、区域效果
- levelingSystem: 升级系统、属性成长
- soundSystem: 音效系统

配置文件:
- enemies: 敌人配置、掉落表
- events: 事件配置、事件效果
- items: 物品配置、装备属性
- locations: 地点配置、探索事件
- skills: 技能配置、技能树

UI组件:
- CraftingDrawer: 制造界面
- InventoryDrawer: 背包界面
- 其他UI优化和动画

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-01-23 16:20:10 +08:00
parent 021f6a54f5
commit 16223c89a5
25 changed files with 2731 additions and 318 deletions

View File

@@ -21,8 +21,9 @@
>
<text class="inventory-item__icon">{{ item.icon || '📦' }}</text>
<view class="inventory-item__info">
<text class="inventory-item__name" :style="{ color: qualityColor(item.quality) }">
<text class="inventory-item__name" :style="{ color: item.qualityColor || '#fff' }">
{{ item.name }}
<text v-if="item.qualityName" class="quality-badge"> [{{ item.qualityName }}]</text>
</text>
<text v-if="item.count > 1" class="inventory-item__count">x{{ item.count }}</text>
</view>
@@ -73,6 +74,7 @@
import { ref, computed } from 'vue'
import { usePlayerStore } from '@/store/player'
import { useGameStore } from '@/store/game'
import { equipItem as equipItemUtil, unequipItemBySlot, useItem as useItemUtil } from '@/utils/itemSystem.js'
import FilterTabs from '@/components/common/FilterTabs.vue'
import TextButton from '@/components/common/TextButton.vue'
@@ -128,16 +130,6 @@ function isEquipped(item) {
return equipped && (equipped.uniqueId === item.uniqueId || equipped.id === item.id)
}
function qualityColor(quality) {
if (!quality) return '#ffffff'
if (quality >= 200) return '#f97316' // 传说
if (quality >= 160) return '#a855f7' // 史诗
if (quality >= 130) return '#60a5fa' // 稀有
if (quality >= 100) return '#4ade80' // 优秀
if (quality >= 50) return '#ffffff' // 普通
return '#808080' // 垃圾
}
function close() {
emit('close')
}
@@ -148,30 +140,10 @@ function selectItem(item) {
function equipItem() {
if (!selectedItem.value) return
const item = selectedItem.value
const slot = slotMap[item.type]
if (!slot) return
// 如果该槽位已有装备,先卸下旧装备放回背包
const oldEquip = player.equipment[slot]
if (oldEquip) {
player.inventory.push(oldEquip)
const result = equipItemUtil(player, game, selectedItem.value.uniqueId)
if (result.success) {
selectedItem.value = null
}
// 从背包中移除该物品
const index = player.inventory.findIndex(i =>
(i.uniqueId === item.uniqueId) || (i.id === item.id && i.uniqueId === undefined)
)
if (index > -1) {
player.inventory.splice(index, 1)
}
// 装备该物品
player.equipment[slot] = item
game.addLog(`装备了 ${item.name}`, 'info')
selectedItem.value = null
}
function unequipItem() {
@@ -181,57 +153,19 @@ function unequipItem() {
if (!slot) return
// 从装备槽移除
player.equipment[slot] = null
// 放回背包
player.inventory.push(item)
game.addLog(`卸下了 ${item.name}`, 'info')
selectedItem.value = null
const result = unequipItemBySlot(player, game, slot)
if (result.success) {
selectedItem.value = null
}
}
function useItem() {
if (!selectedItem.value) return
const item = selectedItem.value
// 检查是否是消耗品
if (item.type !== 'consumable') return
// 应用效果
if (item.effect) {
if (item.effect.health) {
player.currentStats.health = Math.min(
player.currentStats.maxHealth,
player.currentStats.health + item.effect.health
)
}
if (item.effect.stamina) {
player.currentStats.stamina = Math.min(
player.currentStats.maxStamina,
player.currentStats.stamina + item.effect.stamina
)
}
if (item.effect.sanity) {
player.currentStats.sanity = Math.min(
player.currentStats.maxSanity,
player.currentStats.sanity + item.effect.sanity
)
}
const result = useItemUtil(player, game, selectedItem.value.id, 1)
if (result.success) {
// 如果阅读任务,可以在这里处理
selectedItem.value = null
}
// 从背包中移除(如果是堆叠物品,减少数量)
if (item.count > 1) {
item.count--
} else {
const index = player.inventory.findIndex(i => i.id === item.id)
if (index > -1) {
player.inventory.splice(index, 1)
}
}
game.addLog(`使用了 ${item.name}`, 'info')
selectedItem.value = null
}
function sellItem() {
@@ -322,6 +256,13 @@ function sellItem() {
font-size: 22rpx;
margin-top: 4rpx;
}
}
.quality-badge {
font-size: 18rpx;
opacity: 0.8;
margin-left: 8rpx;
}
&__equipped-badge {
width: 32rpx;