diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8b3f890..ac55313 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -13,7 +13,10 @@ "Bash(git remote add:*)", "Bash(git add:*)", "Bash(git commit:*)", - "Bash(git push:*)" + "Bash(git push:*)", + "Read(//d/uniapp/app_test/wwa3/**)", + "Bash(npx eslint:*)", + "mcp__playwright__browser_close" ] } } diff --git a/components/common/MiniMap.vue b/components/common/MiniMap.vue index cf65fe5..c913723 100644 --- a/components/common/MiniMap.vue +++ b/components/common/MiniMap.vue @@ -2,40 +2,70 @@ - - - - - - - - - + + + + + - - + + + + - - {{ node.name }} + + - - 🔒 + + {{ node.name }} - - {{ getDistance(node) }} + + 🔒 + + + {{ getDistance(node) }} + + + + + + + + + + + + + + + + @@ -79,28 +109,45 @@ const player = usePlayerStore() const mapData = ref({ nodes: [], edges: [], width: 300, height: 400 }) const selectedPath = ref([]) +// 缩放和平移状态 +const zoom = ref(1) +const scrollLeft = ref(0) +const scrollTop = ref(0) +const currentLocationNode = ref(null) + +// 容器尺寸 +const containerWidth = 280 +const containerHeight = 300 + +// 触摸拖动状态 +const touchStartX = ref(0) +const touchStartY = ref(0) +const isDragging = ref(false) + const currentLocation = computed(() => player.currentLocation) +// 内容样式 +const contentStyle = computed(() => { + return { + width: (containerWidth * zoom.value) + 'px', + height: (containerHeight * zoom.value) + 'px', + transformOrigin: 'center center' + } +}) + // 初始化地图 function initMap() { const data = getMapData(player.currentLocation) - // 缩放坐标以适应容器 - const containerWidth = 280 - const containerHeight = 340 - const scaleX = containerWidth / data.width - const scaleY = containerHeight / data.height - const scale = Math.min(scaleX, scaleY, 1) - - // 居中偏移 - const offsetX = (containerWidth - data.width * scale) / 2 + 10 - const offsetY = (containerHeight - data.height * scale) / 2 + 10 + // 不再缩放以适应容器,使用原始坐标 + // 但确保地图足够大 + const padding = 40 mapData.value = { nodes: data.nodes.map(n => ({ ...n, - x: n.x * scale + offsetX, - y: n.y * scale + offsetY + x: n.x, + y: n.y })), edges: data.edges, width: data.width, @@ -108,21 +155,74 @@ function initMap() { reachable: data.reachable } - // 更新缩放比例供样式使用 - updateNodeStyles() + // 找到当前节点并居中 + centerOnLocation() selectedPath.value = [] } -// 更新节点样式(转换为rpx) -function updateNodeStyles() { - // 在实际渲染时使用百分比或rpx +// 居中到当前位置 +function centerOnLocation() { + const currentNode = mapData.value.nodes.find(n => n.id === currentLocation.value) + if (!currentNode) return + + currentLocationNode.value = currentNode + + // 计算需要滚动的位置以居中当前节点 + // 考虑缩放比例 + const targetLeft = currentNode.x * zoom.value - containerWidth / 2 + const targetTop = currentNode.y * zoom.value - containerHeight / 2 + + scrollLeft.value = Math.max(0, targetLeft) + scrollTop.value = Math.max(0, targetTop) +} + +// 缩放控制 +function zoomIn() { + zoom.value = Math.min(2, zoom.value + 0.2) + centerOnLocation() +} + +function zoomOut() { + zoom.value = Math.max(0.5, zoom.value - 0.2) + centerOnLocation() +} + +function resetZoom() { + zoom.value = 1 + centerOnLocation() +} + +// 触摸事件处理(用于拖动) +function onTouchStart(e) { + touchStartX.value = e.touches[0].clientX + touchStartY.value = e.touches[0].clientY + isDragging.value = false +} + +function onTouchMove(e) { + const deltaX = e.touches[0].clientX - touchStartX.value + const deltaY = e.touches[0].clientY - touchStartY.value + + if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { + isDragging.value = true + } +} + +function onTouchEnd(e) { + // 如果不是拖动,则视为点击 + // 这里的处理在 handleNodeClick 中完成 +} + +// 滚动事件 +function onScroll(e) { + // 可以在这里记录滚动位置 } // 获取节点样式 function getNodeStyle(node) { return { - left: node.x + 'px', - top: node.y + 'px' + left: (node.x * zoom.value) + 'px', + top: (node.y * zoom.value) + 'px' } } @@ -180,14 +280,14 @@ function getLineStyle(edge) { if (!fromNode || !toNode) return {} - const dx = toNode.x - fromNode.x - const dy = toNode.y - fromNode.y + const dx = (toNode.x - fromNode.x) * zoom.value + const dy = (toNode.y - fromNode.y) * zoom.value const length = Math.sqrt(dx * dx + dy * dy) const angle = Math.atan2(dy, dx) * 180 / Math.PI return { - left: fromNode.x + 'px', - top: fromNode.y + 'px', + left: (fromNode.x * zoom.value) + 'px', + top: (fromNode.y * zoom.value) + 'px', width: length + 'px', transform: `rotate(${angle}deg)` } @@ -252,6 +352,17 @@ onMounted(() => { border: 2rpx solid #4a5568; } + &__scroll { + width: 100%; + height: 300rpx; + } + + &__content { + position: relative; + min-width: 100%; + min-height: 100%; + } + &__connections { position: absolute; top: 0; @@ -276,6 +387,38 @@ onMounted(() => { border: 1rpx solid $accent; border-radius: 8rpx; } + + &__zoom-controls { + position: absolute; + bottom: 12rpx; + right: 12rpx; + display: flex; + flex-direction: column; + gap: 8rpx; + z-index: 20; + } +} + +.zoom-btn { + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba($bg-primary, 0.8); + border: 1rpx solid rgba($accent, 0.5); + border-radius: 8rpx; + backdrop-filter: blur(4rpx); + + &:active { + background-color: rgba($accent, 0.3); + } +} + +.zoom-icon { + font-size: 32rpx; + color: $accent; + font-weight: bold; } .connection-line { diff --git a/config/enemies.js b/config/enemies.js index 5944c95..e7a990a 100644 --- a/config/enemies.js +++ b/config/enemies.js @@ -14,11 +14,16 @@ export const ENEMY_CONFIG = { health: 35, attack: 8, defense: 2, + strength: 8, // 力量,影响攻击 + agility: 6, // 敏捷,影响闪避(降低EP,提高玩家命中率) + dexterity: 7, // 灵巧,影响命中 + intuition: 3, // 智力,影响AP/EP + vitality: 8, // 体质 speed: 1.2 }, derivedStats: { ap: 8, - ep: 10 + ep: 6 // 降低EP值,使玩家更容易命中 }, expReward: 20, skillExpReward: 12, @@ -36,11 +41,16 @@ export const ENEMY_CONFIG = { health: 20, attack: 5, defense: 1, + strength: 5, + agility: 10, // 高敏捷,难命中 + dexterity: 6, + intuition: 2, + vitality: 5, speed: 1.5 }, derivedStats: { ap: 6, - ep: 12 + ep: 11 // 高闪避,符合老鼠特性 }, expReward: 10, skillExpReward: 6, @@ -58,11 +68,16 @@ export const ENEMY_CONFIG = { health: 50, attack: 15, defense: 5, + strength: 14, + agility: 10, + dexterity: 12, + intuition: 6, + vitality: 12, speed: 1.3 }, derivedStats: { ap: 14, - ep: 13 + ep: 11 }, expReward: 40, skillExpReward: 20, @@ -80,6 +95,11 @@ export const ENEMY_CONFIG = { health: 60, attack: 18, defense: 8, + strength: 16, + agility: 9, + dexterity: 14, + intuition: 8, + vitality: 15, speed: 1.0 }, derivedStats: { @@ -103,17 +123,23 @@ export const ENEMY_CONFIG = { health: 250, attack: 30, defense: 15, + strength: 25, + agility: 14, + dexterity: 20, + intuition: 12, + vitality: 25, speed: 1.1 }, derivedStats: { ap: 30, - ep: 18 + ep: 16 }, expReward: 200, skillExpReward: 80, drops: [ { itemId: 'basement_key', chance: 1.0, count: { min: 1, max: 1 } }, - { itemId: 'dog_skin', chance: 0.5, count: { min: 2, max: 4 } } + { itemId: 'dog_skin', chance: 0.5, count: { min: 2, max: 4 } }, + { itemId: 'steel_arm_prosthetic', chance: 0.3, count: { min: 1, max: 1 }, fixedQuality: true } // 义体掉落 ], isBoss: true }, @@ -127,11 +153,16 @@ export const ENEMY_CONFIG = { health: 40, attack: 25, defense: 3, + strength: 20, + agility: 16, // 高闪避 + dexterity: 18, + intuition: 6, + vitality: 8, speed: 1.8 }, derivedStats: { ap: 22, - ep: 24 + ep: 18 // 高闪避,但不是不可能命中 }, expReward: 70, skillExpReward: 35, @@ -149,17 +180,24 @@ export const ENEMY_CONFIG = { health: 500, attack: 50, defense: 25, + strength: 45, + agility: 18, + dexterity: 35, + intuition: 20, + vitality: 50, speed: 0.9 }, derivedStats: { ap: 50, - ep: 25 + ep: 22 }, expReward: 500, skillExpReward: 200, drops: [ { itemId: 'rare_gem', chance: 0.8, count: { min: 1, max: 1 } }, - { itemId: 'iron_sword', chance: 0.3, count: { min: 1, max: 1 } } + { itemId: 'iron_sword', chance: 0.3, count: { min: 1, max: 1 } }, + { itemId: 'optical_eye_prosthetic', chance: 0.4, count: { min: 1, max: 1 }, fixedQuality: true }, + { itemId: 'dermal_armor_prosthetic', chance: 0.3, count: { min: 1, max: 1 }, fixedQuality: true } ], isBoss: true } diff --git a/config/items.js b/config/items.js index 1c02938..7b27e3d 100644 --- a/config/items.js +++ b/config/items.js @@ -494,6 +494,71 @@ export const ITEM_CONFIG = { description: '一本神圣的书籍,可以用来祈祷。', stackable: false, effect: { sanity: 10 } + }, + + // ===== 义体(仅Boss掉落,无品质) ===== + steel_arm_prosthetic: { + id: 'steel_arm_prosthetic', + name: '合金臂义体', + type: 'prosthetic', + subtype: 'arm', + icon: '🦾', + baseValue: 2000, + quality: 150, // 固定品质 + fixedQuality: true, // 标记为固定品质 + stats: { strength: 8, attack: 10 }, + skill: 'steel_arm_slash', // 提供的技能 + skillUnlocked: true, + description: '用合金制成的义体手臂,大幅增强力量。可使用【合金斩击】技能。', + bossOnly: true // 仅Boss掉落 + }, + + optical_eye_prosthetic: { + id: 'optical_eye_prosthetic', + name: '光学义眼', + type: 'prosthetic', + subtype: 'head', + icon: '👁️', + baseValue: 1800, + quality: 140, + fixedQuality: true, + stats: { dexterity: 8, intuition: 5 }, + skill: 'target_lock', // 提供的技能 + skillUnlocked: true, + description: '植入式义眼,提高命中和暴击。可使用【目标锁定】技能。', + bossOnly: true + }, + + spinal_boost_prosthetic: { + id: 'spinal_boost_prosthetic', + name: '脊柱加速器', + type: 'prosthetic', + subtype: 'spine', + icon: '🦴', + baseValue: 2500, + quality: 160, + fixedQuality: true, + stats: { agility: 10, speed: 15 }, + skill: 'overdrive', // 提供的技能 + skillUnlocked: true, + description: '植入脊柱的加速装置,极大提升速度。可使用【过载】技能。', + bossOnly: true + }, + + dermal_armor_prosthetic: { + id: 'dermal_armor_prosthetic', + name: '真皮装甲', + type: 'prosthetic', + subtype: 'body', + icon: '🦾', + baseValue: 2200, + quality: 155, + fixedQuality: true, + stats: { defense: 15, vitality: 8 }, + skill: 'iron_skin', // 提供的技能 + skillUnlocked: true, + description: '植入皮下的装甲层,提供强大防御。可使用【钢铁皮肤】技能。', + bossOnly: true } } @@ -506,6 +571,7 @@ export const ITEM_CATEGORIES = { armor: { id: 'armor', name: '防具', icon: '🛡️' }, shield: { id: 'shield', name: '盾牌', icon: '🛡️' }, accessory: { id: 'accessory', name: '饰品', icon: '💍' }, + prosthetic: { id: 'prosthetic', name: '义体', icon: '🦾' }, consumable: { id: 'consumable', name: '消耗品', icon: '🧪' }, book: { id: 'book', name: '书籍', icon: '📖' }, material: { id: 'material', name: '素材', icon: '📦' }, diff --git a/config/skills.js b/config/skills.js index 5165f40..bd05e5e 100644 --- a/config/skills.js +++ b/config/skills.js @@ -111,5 +111,111 @@ export const SKILL_CONFIG = { 15: { desc: '所有制药成功率+20%', effect: { herbingSuccessRate: 20 } } }, unlockCondition: null + }, + + // ===== 义体相关技能 ===== + prosthetic_adaptation: { + id: 'prosthetic_adaptation', + name: '义体适应性', + type: 'passive', + category: 'prosthetic', + icon: '🦾', + maxLevel: 20, + expPerLevel: (level) => level * 50, + parentSkill: null, + milestones: { + 1: { desc: '解锁义体装备功能', effect: {} }, + 3: { desc: '义体技能伤害+10%', effect: { prostheticSkillDamage: 10 } }, + 5: { desc: '义体耐力消耗-20%', effect: { prostheticStaminaCost: -0.2 } }, + 10: { desc: '义体技能伤害+25%', effect: { prostheticSkillDamage: 25 } }, + 15: { desc: '义体冷却时间-30%', effect: { prostheticCooldown: -0.3 } }, + 20: { desc: '义体所有效果+50%', effect: { prostheticAllEffect: 50 } } + }, + unlockCondition: { + type: 'item', + item: 'any_prosthetic' + }, + desc: '使用义体时提升,增强义体效果' + }, + + // ===== 义体攻击技能 ===== + steel_arm_slash: { + id: 'steel_arm_slash', + name: '合金斩击', + type: 'combat', + category: 'prosthetic_skill', + icon: '⚔️', + maxLevel: 1, // 义体技能不可升级 + expPerLevel: () => 0, + parentSkill: null, + milestones: {}, + unlockCondition: { + type: 'equipment', + item: 'steel_arm_prosthetic' + }, + desc: '合金臂义体专属技能。造成200%攻击力的伤害。', + staminaCost: 15, + cooldown: 0, + damageMultiplier: 2.0 + }, + + target_lock: { + id: 'target_lock', + name: '目标锁定', + type: 'combat', + category: 'prosthetic_skill', + icon: '🎯', + maxLevel: 1, + expPerLevel: () => 0, + parentSkill: null, + milestones: {}, + unlockCondition: { + type: 'equipment', + item: 'optical_eye_prosthetic' + }, + desc: '光学义眼专属技能。下一次攻击必定暴击。', + staminaCost: 8, + cooldown: 3, + buff: 'guaranteedCrit' + }, + + overdrive: { + id: 'overdrive', + name: '过载', + type: 'combat', + category: 'prosthetic_skill', + icon: '⚡', + maxLevel: 1, + expPerLevel: () => 0, + parentSkill: null, + milestones: {}, + unlockCondition: { + type: 'equipment', + item: 'spinal_boost_prosthetic' + }, + desc: '脊柱加速器专属技能。3秒内攻击速度+100%。', + staminaCost: 20, + cooldown: 5, + buff: 'attackSpeedBoost' + }, + + iron_skin: { + id: 'iron_skin', + name: '钢铁皮肤', + type: 'combat', + category: 'prosthetic_skill', + icon: '🛡️', + maxLevel: 1, + expPerLevel: () => 0, + parentSkill: null, + milestones: {}, + unlockCondition: { + type: 'equipment', + item: 'dermal_armor_prosthetic' + }, + desc: '真皮装甲专属技能。3秒内防御力+50%。', + staminaCost: 15, + cooldown: 4, + buff: 'defenseBoost' } } diff --git a/store/game.js b/store/game.js index c5409e3..5f77c90 100644 --- a/store/game.js +++ b/store/game.js @@ -29,6 +29,7 @@ export const useGameStore = defineStore('game', () => { const combatState = ref(null) const autoCombat = ref(false) // 自动战斗模式 const isSearching = ref(false) // 正在寻找敌人中(自动战斗间隔期间) + const preferredStance = ref('balance') // 记住玩家偏好的战斗姿态 // 活动任务 const activeTasks = ref([]) @@ -45,6 +46,16 @@ export const useGameStore = defineStore('game', () => { // 当前事件 const currentEvent = ref(null) + // 成就系统 + const achievements = ref({}) + const achievementProgress = ref({ + totalKills: 0, + bossKills: 0, + prostheticEquipped: 0, + visitedLocations: new Set(), + totalEarned: 0 + }) + // 添加日志 function addLog(message, type = 'info') { const time = `${String(gameTime.value.hour).padStart(2, '0')}:${String(gameTime.value.minute).padStart(2, '0')}` @@ -60,6 +71,54 @@ export const useGameStore = defineStore('game', () => { } } + // 检查成就 + function checkAchievements(triggerType, data = {}) { + const { ACHIEVEMENT_CONFIG } = require('@/config/achievements.js') + let newAchievements = [] + + for (const [id, achievement] of Object.entries(ACHIEVEMENT_CONFIG)) { + // 已完成的跳过 + if (achievements.value[id]) continue + + let unlocked = false + + switch (achievement.condition.type) { + case 'kill': + unlocked = achievementProgress.value.totalKills >= achievement.condition.count + break + case 'boss_kill': + unlocked = achievementProgress.value.bossKills >= achievement.condition.count + break + case 'equip_prosthetic': + unlocked = data.prostheticCount >= achievement.condition.count + break + case 'skill_level': + unlocked = data.maxSkillLevel >= achievement.condition.level + break + case 'visit_locations': + unlocked = achievementProgress.value.visitedLocations.size >= achievement.condition.count + break + case 'total_earned': + unlocked = achievementProgress.value.totalEarned >= achievement.condition.amount + break + case 'survive_days': + unlocked = gameTime.value.day >= achievement.condition.days + break + } + + if (unlocked) { + achievements.value[id] = { + unlocked: true, + timestamp: Date.now() + } + newAchievements.push(achievement) + addLog(`🏆 解锁成就: ${achievement.icon} ${achievement.name}`, 'reward') + } + } + + return newAchievements + } + // 重置游戏状态 function resetGame() { currentTab.value = 'status' @@ -79,6 +138,7 @@ export const useGameStore = defineStore('game', () => { combatState.value = null autoCombat.value = false isSearching.value = false + preferredStance.value = 'balance' activeTasks.value = [] negativeStatus.value = [] marketPrices.value = { @@ -86,6 +146,14 @@ export const useGameStore = defineStore('game', () => { prices: {} } currentEvent.value = null + achievements.value = {} + achievementProgress.value = { + totalKills: 0, + bossKills: 0, + prostheticEquipped: 0, + visitedLocations: new Set(), + totalEarned: 0 + } } return { @@ -97,11 +165,15 @@ export const useGameStore = defineStore('game', () => { combatState, autoCombat, isSearching, + preferredStance, activeTasks, negativeStatus, marketPrices, currentEvent, + achievements, + achievementProgress, addLog, - resetGame + resetGame, + checkAchievements } }) diff --git a/store/player.js b/store/player.js index 263fc0a..0504a43 100644 --- a/store/player.js +++ b/store/player.js @@ -5,7 +5,7 @@ export const usePlayerStore = defineStore('player', () => { // 基础属性 const baseStats = ref({ strength: 10, // 力量 - agility: 8, // 敏捷 + agility: 6, // 敏捷(降低以提高被命中率) dexterity: 8, // 灵巧 intuition: 10, // 智力 vitality: 10 // 体质(影响HP) @@ -36,9 +36,13 @@ export const usePlayerStore = defineStore('player', () => { weapon: null, armor: null, shield: null, - accessory: null + accessory: null, + prosthetic: null // 义体装备位 }) + // 义体适应性(使用义体时增加) + const prostheticAdaptation = ref(0) + // 背包 const inventory = ref([]) @@ -53,6 +57,12 @@ export const usePlayerStore = defineStore('player', () => { // 标记 const flags = ref({}) + // 击杀统计(用于成就和解锁条件) + const killCount = ref({}) + + // 访问过的位置 + const visitedLocations = ref(new Set()) + // 计算属性:总货币 const totalCurrency = computed(() => { return { @@ -66,7 +76,7 @@ export const usePlayerStore = defineStore('player', () => { function resetPlayer() { baseStats.value = { strength: 10, - agility: 8, + agility: 6, // 降低以提高被命中率 dexterity: 8, intuition: 10, vitality: 10 @@ -89,12 +99,16 @@ export const usePlayerStore = defineStore('player', () => { weapon: null, armor: null, shield: null, - accessory: null + accessory: null, + prosthetic: null } + prostheticAdaptation.value = 0 inventory.value = [] currency.value = { copper: 0 } currentLocation.value = 'camp' flags.value = {} + killCount.value = {} + visitedLocations.value = new Set() } return { @@ -103,11 +117,14 @@ export const usePlayerStore = defineStore('player', () => { level, skills, equipment, + prostheticAdaptation, inventory, currency, totalCurrency, currentLocation, flags, + killCount, + visitedLocations, resetPlayer } }) diff --git a/计划第二步.md b/计划第二步.md deleted file mode 100644 index 2094d38..0000000 --- a/计划第二步.md +++ /dev/null @@ -1,45 +0,0 @@ -接下来开发计划 -阶段1:修复代码问题(优先) -# 任务 文件 -1 添加gameStore中shop抽屉状态 store/game.js -2 修复eventSystem中SKILL_CONFIG导入 utils/eventSystem.js -3 修复environmentSystem中里程碑奖励调用 utils/environmentSystem.js -4 统一击杀数存储方式 utils/eventSystem.js, components/panels/MapPanel.vue -5 InventoryDrawer使用itemSystem函数 components/drawers/InventoryDrawer.vue -6 任务完成使用addSkillExp utils/taskSystem.js -阶段2:补充未实现功能 -# 功能 描述 -1 阅读功能 将书籍任务与UI关联,显示阅读进度 -2 训练功能 添加训练任务UI,技能训练 -3 任务UI面板 显示活动中的挂机任务进度 -4 装备属性生效 确保装备属性正确应用到战斗计算 -5 探索功能 添加探索事件触发 -阶段3:UI美化 -# 内容 说明 -1 状态面板优化 添加属性详情展开/折叠 -2 战斗视觉反馈 伤害数字跳动、暴击动画 -3 品质特效 不同品质物品的光效/边框 -4 日志颜色优化 更精细的日志类型着色 -5 技能面板 里程碑奖励可视化显示 -6 过渡动画 页面切换、抽屉弹出动画 -阶段4:数值调整 -# 内容 当前 建议 -1 初始属性 全部10 按角色差异化 -2 敌人伤害 野狗攻击8 对新手可能偏高 -3 经验曲线 线性增长 考虑指数衰减 -4 耐力消耗 战斗2/秒 平衡性测试 -5 物品价格 基础价格10-50 经济平衡 -6 品质概率 随机50-150 高品质概率过低 -阶段5:内容扩展(剧情之前先讨论) -# 内容 说明 -1 更多武器类型 剑、斧、法杖等 -2 更多消耗品 药水、食物种类 -3 更多敌人 不同区域的怪物 -4 更多被动技能 适应不同环境 -5 成就系统 记录玩家成就 -🎯 建议优先处理顺序 -立即修复:代码逻辑问题(阶段1) -核心功能补全:阅读、训练、任务UI(阶段2) -数值平衡测试:实际游玩体验后调整(阶段4) -UI美化:在功能稳定后进行(阶段3) -剧情讨论:最后我们一起讨论完善(阶段5) \ No newline at end of file