feat: 完善数学模型和品质系统
- 修复品质等级范围重叠问题(优秀/稀有无重叠) - 统一 formulas.js 与 constants.js 的品质判定 - 修复经验公式与游戏实际逻辑不一致 - 调整品质生成概率: 传说0.1%, 史诗1%, 稀有4%, 优秀10% - 添加数学模型文档和README - 添加数值验证脚本 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,30 +73,45 @@
|
||||
<view class="equipment-slots">
|
||||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.weapon }">
|
||||
<text class="equipment-slot__label">武器</text>
|
||||
<text v-if="player.equipment.weapon" class="equipment-slot__item">
|
||||
{{ player.equipment.weapon.icon }} {{ player.equipment.weapon.name }}
|
||||
</text>
|
||||
<view v-if="player.equipment.weapon" class="equipment-slot__item">
|
||||
<text class="equipment-slot__name" :style="{ color: player.equipment.weapon.qualityColor }">
|
||||
{{ player.equipment.weapon.icon }} {{ player.equipment.weapon.name }}
|
||||
</text>
|
||||
<text class="equipment-slot__stats">攻{{ player.equipment.weapon.finalDamage }}</text>
|
||||
<text class="equipment-slot__quality">[{{ player.equipment.weapon.qualityName }}]</text>
|
||||
</view>
|
||||
<text v-else class="equipment-slot__empty">空</text>
|
||||
</view>
|
||||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.armor }">
|
||||
<text class="equipment-slot__label">防具</text>
|
||||
<text v-if="player.equipment.armor" class="equipment-slot__item">
|
||||
{{ player.equipment.armor.icon }} {{ player.equipment.armor.name }}
|
||||
</text>
|
||||
<view v-if="player.equipment.armor" class="equipment-slot__item">
|
||||
<text class="equipment-slot__name" :style="{ color: player.equipment.armor.qualityColor }">
|
||||
{{ player.equipment.armor.icon }} {{ player.equipment.armor.name }}
|
||||
</text>
|
||||
<text class="equipment-slot__stats">防{{ player.equipment.armor.finalDefense }}</text>
|
||||
<text class="equipment-slot__quality">[{{ player.equipment.armor.qualityName }}]</text>
|
||||
</view>
|
||||
<text v-else class="equipment-slot__empty">空</text>
|
||||
</view>
|
||||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.shield }">
|
||||
<text class="equipment-slot__label">盾牌</text>
|
||||
<text v-if="player.equipment.shield" class="equipment-slot__item">
|
||||
{{ player.equipment.shield.icon }} {{ player.equipment.shield.name }}
|
||||
</text>
|
||||
<view v-if="player.equipment.shield" class="equipment-slot__item">
|
||||
<text class="equipment-slot__name" :style="{ color: player.equipment.shield.qualityColor }">
|
||||
{{ player.equipment.shield.icon }} {{ player.equipment.shield.name }}
|
||||
</text>
|
||||
<text class="equipment-slot__stats">格挡{{ player.equipment.shield.finalShield }}</text>
|
||||
<text class="equipment-slot__quality">[{{ player.equipment.shield.qualityName }}]</text>
|
||||
</view>
|
||||
<text v-else class="equipment-slot__empty">空</text>
|
||||
</view>
|
||||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.accessory }">
|
||||
<text class="equipment-slot__label">饰品</text>
|
||||
<text v-if="player.equipment.accessory" class="equipment-slot__item">
|
||||
{{ player.equipment.accessory.icon }} {{ player.equipment.accessory.name }}
|
||||
</text>
|
||||
<view v-if="player.equipment.accessory" class="equipment-slot__item">
|
||||
<text class="equipment-slot__name" :style="{ color: player.equipment.accessory.qualityColor }">
|
||||
{{ player.equipment.accessory.icon }} {{ player.equipment.accessory.name }}
|
||||
</text>
|
||||
<text class="equipment-slot__quality">[{{ player.equipment.accessory.qualityName }}]</text>
|
||||
</view>
|
||||
<text v-else class="equipment-slot__empty">空</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -127,19 +142,47 @@
|
||||
<!-- 技能列表 -->
|
||||
<scroll-view class="status-panel__skills" scroll-y>
|
||||
<view v-for="skill in filteredSkills" :key="skill.id" class="skill-item">
|
||||
<text class="skill-item__icon">{{ skill.icon || '⚡' }}</text>
|
||||
<view class="skill-item__info">
|
||||
<text class="skill-item__name">{{ skill.name }}</text>
|
||||
<text class="skill-item__level">Lv.{{ skill.level }}</text>
|
||||
<view class="skill-item__main" @click="toggleSkillDetail(skill.id)">
|
||||
<text class="skill-item__icon">{{ skill.icon || '⚡' }}</text>
|
||||
<view class="skill-item__info">
|
||||
<text class="skill-item__name">{{ skill.name }}</text>
|
||||
<text class="skill-item__level">Lv.{{ skill.level }}/{{ skill.maxLevel }}</text>
|
||||
</view>
|
||||
<view class="skill-item__bar">
|
||||
<ProgressBar
|
||||
:value="skill.exp"
|
||||
:max="skill.maxExp"
|
||||
height="8rpx"
|
||||
:showText="false"
|
||||
/>
|
||||
<text class="skill-item__progress">{{ Math.floor(skill.exp) }}/{{ Math.floor(skill.maxExp) }}</text>
|
||||
</view>
|
||||
<text class="skill-item__expand">{{ expandedSkills.includes(skill.id) ? '▼' : '▶' }}</text>
|
||||
</view>
|
||||
<view class="skill-item__bar">
|
||||
<ProgressBar
|
||||
:value="skill.exp"
|
||||
:max="skill.maxExp"
|
||||
height="8rpx"
|
||||
:showText="false"
|
||||
/>
|
||||
<text class="skill-item__progress">{{ skill.exp }}/{{ skill.maxExp }}</text>
|
||||
|
||||
<!-- 技能详情(展开后显示) -->
|
||||
<view v-if="expandedSkills.includes(skill.id)" class="skill-item__detail">
|
||||
<!-- 已获得的增幅 -->
|
||||
<view v-if="getActiveMilestones(skill).length > 0" class="skill-detail__section">
|
||||
<text class="skill-detail__title">✓ 已获得增幅</text>
|
||||
<view v-for="m in getActiveMilestones(skill)" :key="m.level" class="skill-detail__bonus">
|
||||
<text class="skill-detail__bonus-level">Lv.{{ m.level }}</text>
|
||||
<text class="skill-detail__bonus-desc">{{ m.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 即将获得的增幅 -->
|
||||
<view v-if="getUpcomingMilestones(skill).length > 0" class="skill-detail__section">
|
||||
<text class="skill-detail__title">即将解锁</text>
|
||||
<view v-for="m in getUpcomingMilestones(skill)" :key="m.level" class="skill-detail__bonus upcoming">
|
||||
<text class="skill-detail__bonus-level">Lv.{{ m.level }}</text>
|
||||
<text class="skill-detail__bonus-desc">{{ m.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="getActiveMilestones(skill).length === 0 && getUpcomingMilestones(skill).length === 0" class="skill-detail__empty">
|
||||
<text class="skill-detail__empty-text">暂无增幅效果</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="filteredSkills.length === 0" class="skill-empty">
|
||||
@@ -277,6 +320,7 @@ const currentSkillFilter = ref('all')
|
||||
const showBooks = ref(false)
|
||||
const showTraining = ref(false)
|
||||
const showTasks = ref(true)
|
||||
const expandedSkills = ref([]) // 展开的技能ID列表
|
||||
|
||||
const skillFilterTabs = [
|
||||
{ id: 'all', label: '全部' },
|
||||
@@ -382,6 +426,42 @@ const readingTimeText = computed(() => {
|
||||
return `剩余 ${minutes}:${String(seconds).padStart(2, '0')}`
|
||||
})
|
||||
|
||||
// 技能详情相关函数
|
||||
function toggleSkillDetail(skillId) {
|
||||
const index = expandedSkills.value.indexOf(skillId)
|
||||
if (index > -1) {
|
||||
expandedSkills.value.splice(index, 1)
|
||||
} else {
|
||||
expandedSkills.value.push(skillId)
|
||||
}
|
||||
}
|
||||
|
||||
function getActiveMilestones(skill) {
|
||||
const config = SKILL_CONFIG[skill.id]
|
||||
if (!config || !config.milestones) return []
|
||||
|
||||
return Object.entries(config.milestones)
|
||||
.filter(([level]) => parseInt(level) <= skill.level)
|
||||
.map(([level, milestone]) => ({
|
||||
level: parseInt(level),
|
||||
desc: milestone.desc
|
||||
}))
|
||||
.sort((a, b) => a.level - b.level)
|
||||
}
|
||||
|
||||
function getUpcomingMilestones(skill) {
|
||||
const config = SKILL_CONFIG[skill.id]
|
||||
if (!config || !config.milestones) return []
|
||||
|
||||
return Object.entries(config.milestones)
|
||||
.filter(([level]) => parseInt(level) > skill.level)
|
||||
.map(([level, milestone]) => ({
|
||||
level: parseInt(level),
|
||||
desc: milestone.desc
|
||||
}))
|
||||
.sort((a, b) => a.level - b.level)
|
||||
}
|
||||
|
||||
function startReading(book) {
|
||||
const result = startTask(game, player, 'reading', {
|
||||
itemId: book.id,
|
||||
@@ -598,12 +678,19 @@ function cancelActiveTask(task) {
|
||||
|
||||
.skill-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
padding: 12rpx;
|
||||
flex-direction: column;
|
||||
background-color: $bg-secondary;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&__main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
padding: 12rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 32rpx;
|
||||
@@ -641,6 +728,72 @@ function cancelActiveTask(task) {
|
||||
font-size: 20rpx;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__expand {
|
||||
color: $text-muted;
|
||||
font-size: 20rpx;
|
||||
padding-left: 8rpx;
|
||||
}
|
||||
|
||||
&__detail {
|
||||
padding: 12rpx;
|
||||
padding-top: 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.skill-detail {
|
||||
&__section {
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
color: $text-primary;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__bonus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
padding: 6rpx 12rpx;
|
||||
background-color: rgba(78, 205, 196, 0.1);
|
||||
border-radius: 4rpx;
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
&.upcoming {
|
||||
background-color: rgba(255, 107, 107, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&__bonus-level {
|
||||
color: $accent;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
min-width: 60rpx;
|
||||
}
|
||||
|
||||
&__bonus-desc {
|
||||
color: $text-secondary;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
&__empty {
|
||||
padding: 16rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__empty-text {
|
||||
color: $text-muted;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skill-empty {
|
||||
@@ -687,8 +840,26 @@ function cancelActiveTask(task) {
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rpx;
|
||||
}
|
||||
|
||||
&__name {
|
||||
color: $text-primary;
|
||||
font-size: 22rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__stats {
|
||||
color: $accent;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
&__quality {
|
||||
color: $text-muted;
|
||||
font-size: 18rpx;
|
||||
}
|
||||
|
||||
&__empty {
|
||||
|
||||
Reference in New Issue
Block a user